Entity Class

Entities in Nymph work pretty much like regular objects, and this is due to the Entity class. The Entity class constructor returns a proxy to allow accessing data on it just like a regular object while keeping track of changes and only unserializing data from the DB when it's first accessed. Instance methods and properties begin with "$" to differentiate them from data.

In the client, assigning a property internally marks that property as "dirty", and calling $patch will push just the changed properties up to the server, whereas $save would push all properties.

Entities can be organized using tags. To add, remove, and check tags, the methods $addTag, $removeTag, and $hasTag are used, respectively. Each takes any number of tags as arguments.

const entity = await MyEntityClass.factory();

entity.$addTag('foo', 'bar');
entity.$hasTag('foo'); // True

entity.$removeTag('foo', 'bar');
entity.$hasTag('foo'); // False

In Node.js, entities that have been saved inside another entity's property are loaded as "sleeping references". This means their data is not actually pulled from the database. It will "wake up" when you first try to access something other than its GUID.

In the client, there are still sleeping references, but they can't be loaded synchronously. Instead, they are loaded with the $ready method, or by using the $readyAll method on the entity that contains them.

To clear the cache of referenced entities, so that the next time one is accessed it will be pulled from the database, use the $clearCache method in Node.js and the $refresh method in the client.

// Create some entities.
let entity = await MyEntityClass.factory();
entity.foo = await MyEntityClass.factory();
entity.foo.bar = 'Old value.';
await entity.foo.$save();
await entity.$save();

// Reset the entity to a copy just pulled from the DB.
entity = await nymph.getEntity(
  { class: MyEntityClass },
  { type: '&', guid: entity.guid }
);

// Get a copy of the referenced entity.
let instOfFoo = await nymph.getEntity(
  { class: MyEntityClass },
  { type: '&', guid: entity.foo.guid }
);
// And change a value on it.
instOfFoo.bar = 'New value.';
await instOfFoo.$save();

console.log(entity.foo.bar); // Outputs 'Old value.'
// If on Node.js
entity.$clearCache();
// Else if on the client
await entity.$refresh();
await entity.foo.$ready();
// End if
console.log(entity.foo.bar); // Outputs 'New value.'

Much like clearing the entity cache, you may need to refresh the entity's own data in Node.js. Use the $refresh method, just like in the client, for this.

// Create an entity.
const entity = await MyEntityClass.factory();
entity.foo = 'Old value.';
await entity.$save();

// Get a copy of the entity.
const instOfEnt = await nymph.getEntity(
  { class: MyEntityClass },
  { type: '&', guid: entity.guid }
);
// And change a value on it.
instOfEnt.foo = 'New value.';
await instOfEnt.$save();

console.log(entity.foo); // Outputs 'Old value.'
await entity.$refresh();
console.log(entity.foo); // Outputs 'New value.'

To save an entity, use the $save method. Likewise, to delete the entity, use the $delete method. You can also call the saveEntity, deleteEntity, and deleteEntityByID methods of Nymph. The Entity class uses these methods.

const entity = await MyEntityClass.factory();

// Save the entity.
await entity.$save();
// or
await nymph.saveEntity(entity);
// or
await nymph.saveEntities([entity]);

// (Client only.) Save only the data that has changed.
await entity.$patch();
// or
await nymph.patchEntity(entity);
// or
await nymph.patchEntities([entity]);

// Delete the entity.
await entity.$delete();
// or
await nymph.deleteEntity(entity);
// or
await nymph.deleteEntities([entity]);

Entities can't be checked using the == operator. Instead, you can use the following entity methods.

  • $is - Perform a less strict comparison of two entities (basically a GUID check). To return true, the entities must meet the following criteria.
    • They must be entities.
    • They must have equal GUIDs, or both can have no GUID.
    • If they have no GUIDs, their data must be equal.
  • $equals - Perform a more strict comparison of two entities (basically a GUID + class + data check). To return true, the entities must meet the following criteria.
    • They must be entities.
    • They must have equal GUIDs, or both can have no GUID.
    • They must be instances of the same class.
    • Their data must be equal.
  • $inArray - Check whether the entity is in an array. Takes two arguments, the array and a boolean strict. If strict is false, the function uses $is to compare, and if it's true, the function uses $equals.
  • $arraySearch - Search an array for the entity and return the corresponding key. Takes two arguments, the array and a boolean strict. If strict is false, the function uses $is to compare, and if it's true, the function uses $equals. This method may return 0, which evaluates to false, so you should use $inArray if you are only checking whether the entity is in the array.
// Assuming the entity with GUID 'a4c1591d6ea91c8450d2d360' exists.
let entity = await MyEntityClass.factory('a4c1591d6ea91c8450d2d360');
let entity2 = await MyEntityClass.factory('a4c1591d6ea91c8450d2d360');

entity2.someProp = 'some new value';
entity.$is(entity2); // True
entity.$equals(entity2); // False

const arr = [null, null, entity];
entity.$arraySearch(arr); // 2
entity.$inArray(arr); // True
entity.$arraySearch(arr, true); // -1
entity.$inArray(arr, true); // false

Client side Nymph entities can use the $serverCall method to call methods on a server side instance of the entity. $serverCall expects three parameters.

  • method - the name of the method to call on the server side object.
  • params - an array of the parameters to pass to the method.
  • stateless - if set to true, the method won't update the entity with the returned server side representation.

Normally, when you use this method, just before the promise is fulfilled, the entity's data will be replaced with that of the entity on the server side after the method was run. This will cause any awoken entities in the data of your entity to be replaced with sleeping entities, so you will have to run $readyAll again. If you know that the server side method will not change any of the data on the entity, you can set stateless to true.

You can also call static methods on the server with serverCallStatic.

In order to be called from the client side, the method must be listed in the $clientEnabledMethods or clientEnabledStaticMethods property in the Node.js class. This guards against a user submitting tailored requests to perform potentially dangerous tasks on the server side. If the method is not listed, the request will fail with a 403 Forbidden status.

// You can use $serverCall and serverCallStatic directly.
try {
  const success: boolean = await todo.$serverCall('$archive', []);
  if (!success) {
    alert("Couldn't archive " + todo.name);
  }
} catch (e) {
  alert('Error: ' + e.textStatus + "
Couldn't archive " + todo.name);
}

// Or you can define methods like this in your client class.
class Todo extends Entity<TodoData> {
  // ...

  static async archiveAllDone(onlyOlderThanDay: boolean): Promise<boolean> {
    return await this.serverCallStatic('archiveAllDone', [onlyOlderThanDay]);
  }

  async $archive(): Promise<boolean> {
    return await this.$serverCall('archive', []);
  }
}

// You'd use them like this.
const success = await Todo.archiveAllDone(true);

// And
const todo = await Todo.factory();
const success = await todo.$archive();