# Instance Methods
All instance methods start with the character $
to prevent them from colliding with the database column names.
# $query()
const queryBuilder = person.$query(transactionOrKnex);
Copied!
Creates a query builder for this model instance.
All queries built using the returned builder only affect this instance.
# Arguments
Argument | Type | Description |
---|---|---|
transactionOrKnex | object | Optional transaction or knex instance for the query. This can be used to specify a transaction or even a different database for a query. Falsy values are ignored. |
# Return value
Type | Description |
---|---|
QueryBuilder | query builder |
# Examples
Re-fetch an item from the database:
// If you need to refresh the same instance you can do this: const reFetchedPerson = await person.$query(); // Note that `person` did not get modified by the fetch. person.$set(reFetchedPerson);
Copied!
Insert a new item to database:
const jennifer = await Person.fromJson({ firstName: "Jennifer" }) .$query() .insert(); console.log(jennifer.id);
Copied!
Patch an item:
await person.$query().patch({ lastName: "Cooper" }); console.log("person updated");
Copied!
Delete an item.
await person.$query().delete(); console.log("person deleted");
Copied!
# $relatedQuery()
const builder = person.$relatedQuery(relationName, transactionOrKnex);
Copied!
Use this to build a query that only affects the items related to an instance through a relation. By default, any fetched or inserted items are also stored to the owner model’s property named after the relation. See relatedFindQueryMutates or relatedInsertQueryMutates to change this behaviour.
See the examples below and here.
TIP
This methods is just a shortcut for this call to the static relatedQuery method:
const builder = Person.relatedQuery(relationName, transactionOrKnex).for( person );
Copied!
# Arguments
Argument | Type | Description |
---|---|---|
relationName | string | The name of the relation to query. |
transactionOrKnex | object | Optional transaction or knex instance for the query. This can be used to specify a transaction or even a different database for a query. Falsy values are ignored. |
# Return value
Type | Description |
---|---|
QueryBuilder | A query builder |
# Examples
Fetch all items related to an item through a relation. The fetched items are also stored to the owner item's property named after the relation (by default):
const pets = await jennifer.$relatedQuery("pets"); console.log("jennifer has", pets.length, "pets"); console.log(jennifer.pets === pets); // --> true
Copied!
The related query is just like any other query. All knex methods are available:
const dogsAndCats = await jennifer .$relatedQuery("pets") .select("animals.*", "persons.name as ownerName") .where("species", "=", "dog") .orWhere("breed", "=", "cat") .innerJoin("persons", "persons.id", "animals.ownerId") .orderBy("animals.name"); // All the dogs and cats have the owner's name "Jennifer" // joined as the `ownerName` property. console.log(dogsAndCats);
Copied!
This inserts a new item to the database and binds it to the owner item as defined by the relation (by default):
const waldo = await jennifer .$relatedQuery("pets") .insert({ species: "dog", name: "Fluffy" }); console.log(waldo.id);
Copied!
To attach an existing item to a relation the relate
method can be used. In this example the dog fluffy
already exists in the database but it isn't related to jennifer
through the pets
relation. We can make the connection like this:
await jennifer.$relatedQuery("pets").relate(fluffy.id); console.log("fluffy is now related to jennifer through pets relation");
Copied!
The connection can be removed using the unrelate
method. Again, this doesn't delete the related model. Only the connection is removed. For example in the case of ManyToMany relation the join table entries are deleted.
await jennifer.$relatedQuery("pets").unrelate().where("id", fluffy.id); console.log("jennifer no longer has fluffy as a pet");
Copied!
Related items can be deleted using the delete method. Note that in the case of ManyToManyRelation the join table entries are not deleted. You should use ON DELETE CASCADE
in your database migrations to make the database properly delete the join table rows when either end of the relation is deleted. Naturally the delete query can be chained with any query building methods.
await jennifer.$relatedQuery("pets").delete().where("species", "cat"); console.log("jennifer no longer has any cats");
Copied!
update
and patch
can be used to update related models. Only difference between the mentioned methods is that update
validates the input objects using the related model class's full schema and patch
ignores the required
property of the schema. Use update
when you want to update all properties of a model and patch
when only a subset should be updated.
const updatedFluffy = await jennifer .$relatedQuery("pets") .update({ species: "dog", name: "Fluffy the great", vaccinated: false }) .where("id", fluffy.id); console.log("fluffy's new name is", updatedFluffy.name); // This query will be rejected assuming that `name` or `species` // is a required property for an Animal. await jennifer .$relatedQuery("pets") .update({ vaccinated: true }) .where("species", "dog"); // This query will succeed. await jennifer .$relatedQuery("pets") .patch({ vaccinated: true }) .where("species", "dog"); console.log("jennifer just got all her dogs vaccinated");
Copied!
# $beforeInsert()
class Person extends Model { async $beforeInsert(queryContext) { await super.$beforeInsert(queryContext); await this.doPossiblyAsyncStuff(); } }
Copied!
Called before a model is inserted into the database.
You can return a promise from this function if you need to do asynchronous stuff. You can also throw an exception to abort the insert and reject the query. This can be useful if you need to do insert specific validation.
If you start a query from this hook, make sure you specify queryContext.transaction
as it's connection to make sure the query takes part in the same transaction as the parent query. See the example below.
# Arguments
Argument | Type | Description |
---|---|---|
queryContext | Object | The context object of the insert query. See context. |
# Return value
Type | Description |
---|---|
Promise (opens new window) void | Promise or void depending whether your hook is async or not. |
# Examples
The current query's transaction/knex instance can always be accessed through queryContext.transaction
.
class Person extends Model { async $beforeInsert(queryContext) { await super.$beforeInsert(queryContext); // This can always be done even if there is no running // transaction. In that case `queryContext.transaction` // returns the normal knex instance. This makes sure that // the query is not executed outside the original query's // transaction. await SomeModel.query(queryContext.transaction).insert(whatever); } }
Copied!
# $afterInsert()
class Person extends Model { async $afterInsert(queryContext) { await super.$afterInsert(queryContext); await this.doPossiblyAsyncStuff(); } }
Copied!
Called after a model has been inserted into the database.
You can return a promise from this function if you need to do asynchronous stuff.
# Arguments
Argument | Type | Description |
---|---|---|
queryContext | Object | The context object of the insert query. See context. |
# Return value
Type | Description |
---|---|
Promise (opens new window) void | Promise or void depending whether your hook is async or not. |
# Examples
The current query's transaction/knex instance can always be accessed through queryContext.transaction
.
class Person extends Model { async $afterInsert(queryContext) { await super.$afterInsert(queryContext); // This can always be done even if there is no running transaction. In that // case `queryContext.transaction` returns the normal knex instance. This // makes sure that the query is not executed outside the original query's // transaction. await SomeModel.query(queryContext.transaction).insert(whatever); } }
Copied!
# $beforeUpdate()
class Person extends Model { async $beforeUpdate(opt, queryContext) { await super.$beforeUpdate(opt, queryContext); await this.doPossiblyAsyncStuff(); } }
Copied!
Called before a model instance is updated.
You can return a promise from this function if you need to do asynchronous stuff. You can also throw an exception to abort the update and reject the query. This can be useful if you need to do update specific validation.
This method is also called before a model is patched. Therefore all the model's properties may not exist. You can check if the update operation is a patch by checking the opt.patch
boolean.
Inside the hook, this
contains the values to be updated. If (and only if) the query is started for an existing model instance using $query, opt.old
object contains the old values. The old values are never fetched from the database implicitly. For non-instance queries the opt.old
object is undefined
. See the examples.
# Arguments
Argument | Type | Description |
---|---|---|
opt | ModelOptions | Update options. |
queryContext | Object | The context object of the update query. See context. |
# Return value
Type | Description |
---|---|
Promise (opens new window) void | Promise or void depending whether your hook is async or not. |
# Examples
The current query's transaction/knex instance can always be accessed through queryContext.transaction
.
class Person extends Model { async $beforeUpdate(opt, queryContext) { await super.$beforeUpdate(opt, queryContext); // This can always be done even if there is no running transaction. // In that case `queryContext.transaction` returns the normal knex // instance. This makes sure that the query is not executed outside // the original query's transaction. await SomeModel.query(queryContext.transaction).insert(whatever); } }
Copied!
Note that the opt.old
object is only populated for instance queries started with $query
:
somePerson.$query().update(newValues);
Copied!
For the following query opt.old
is undefined
because there is no old object in the JavaScript side. ExpressWeb JS doesn't fetch the old values even if they existed in the database
for performance and simplicity reasons.
Person.query().update(newValues).where("foo", "bar");
Copied!
# $afterUpdate()
class Person extends Model { async $afterUpdate(opt, queryContext) { await super.$afterUpdate(opt, queryContext); await this.doPossiblyAsyncStuff(); } }
Copied!
Called after a model instance is updated.
You can return a promise from this function if you need to do asynchronous stuff.
This method is also called after a model is patched. Therefore all the model's properties may not exist. You can check if the update operation is a patch by checking the opt.patch
boolean.
Inside the hook, this
contains the values to be updated. If (and only if) the query is started for an existing model instance using $query, opt.old
object contains the old values. The old values are never fetched from the database implicitly. For non-instance queries the opt.old
object is undefined
. See the examples.
# Arguments
Argument | Type | Description |
---|---|---|
opt | ModelOptions | Update options. |
queryContext | Object | The context object of the update query. See context. |
# Return value
Type | Description |
---|---|
Promise (opens new window) void | Promise or void depending whether your hook is async or not. |
# Examples
The current query's transaction/knex instance can always be accessed through queryContext.transaction
.
class Person extends Model { async $afterUpdate(opt, queryContext) { await super.$afterUpdate(opt, queryContext); // This can always be done even if there is no running transaction. // In that case `queryContext.transaction` returns the normal knex // instance. This makes sure that the query is not executed // outside the original query's transaction. await SomeModel.query(queryContext.transaction).insert(whatever); } }
Copied!
Note that the opt.old
object is only populated for instance queries started with $query
:
somePerson.$query().update(newValues);
Copied!
For the following query opt.old
is undefined
because there is no old object in the JavaScript side. ExpressWeb JS doesn't fetch the old values even if they existed in the database for performance and simplicity reasons.
Person.query().update(newValues).where("foo", "bar");
Copied!
# $beforeDelete()
class Person extends Model { async $beforeDelete(queryContext) { await super.$beforeDelete(queryContext); await doPossiblyAsyncStuff(); } }
Copied!
Called before a model is deleted.
You can return a promise from this function if you need to do asynchronous stuff.
WARNING
This method is only called for instance deletes started with $query() method. All hooks are instance methods. For deletes there is no instance for which to call the hook, except when $query() is used. ExpressWeb JS doesn't fetch the item just to call the hook for it to ensure predictable performance and prevent a whole class of concurrency bugs.
# Arguments
Argument | Type | Description |
---|---|---|
queryContext | Object | The context object of the update query. See context. |
# Return value
Type | Description |
---|---|
Promise (opens new window) void | Promise or void depending whether your hook is async or not. |
# Examples
The current query's transaction/knex instance can always be accessed through queryContext.transaction
.
class Person extends Model { async $beforeDelete(queryContext) { await super.$beforeDelete(queryContext); // This can always be done even if there is no running transaction. // In that case `queryContext.transaction` returns the normal knex // instance. This makes sure that the query is not executed outside // the original query's transaction. await SomeModel.query(queryContext.transaction).insert(whatever); } }
Copied!
# $afterDelete()
class Person extends Model { async $afterDelete(queryContext) { await super.$afterDelete(queryContext); await this.doPossiblyAsyncStuff(); } }
Copied!
Called after a model is deleted.
You can return a promise from this function if you need to do asynchronous stuff.
WARNING
This method is only called for instance deletes started with $query() method. All hooks are instance methods. For deletes there is no instance for which to call the hook, except when $query() is used. ExpressWeb JS doesn't fetch the item just to call the hook for it to ensure predictable performance and prevent a whole class of concurrency bugs.
# Arguments
Argument | Type | Description |
---|---|---|
queryContext | Object | The context object of the update query. See context. |
# Return value
Type | Description |
---|---|
Promise (opens new window) void | Promise or void depending whether your hook is async or not. |
# Examples
The current query's transaction/knex instance can always be accessed through queryContext.transaction
.
class Person extends Model { async $afterDelete(queryContext) { await super.$afterDelete(queryContext); // This can always be done even if there is no running transaction. In that // case `queryContext.transaction` returns the normal knex instance. This // makes sure that the query is not executed outside the original query's // transaction. await SomeModel.query(queryContext.transaction).insert(whatever); } }
Copied!
# $afterGet()
WARNING
Deprecated! Will be removed in version 3.0. Use $afterFind instead.
# $afterFind()
class Person extends Model { $afterFind(queryContext) { return doPossiblyAsyncStuff(); } }
Copied!
Called after a model is fetched.
This method is not called for insert, update or delete operations.
You can return a promise from this function if you need to do asynchronous stuff.
# Arguments
Argument | Type | Description |
---|---|---|
queryContext | Object | The context object of the update query. See context. |
# Return value
Type | Description |
---|---|
Promise (opens new window) void | Promise or void depending whether your hook is async or not. |
# Examples
The current query's transaction/knex instance can always be accessed through queryContext.transaction
.
class Person extends Model { $afterFind(queryContext) { // This can always be done even if there is no running transaction. // In that case `queryContext.transaction` returns the normal knex // instance. This makes sure that the query is not executed outside // the original query's transaction. return SomeModel.query(queryContext.transaction).insert(whatever); } }
Copied!
# $clone()
const clone = modelInstance.$clone(options);
Copied!
Returns a (deep) copy of a model instance.
If the item to be cloned has instances of Model as properties (or arrays of them) they are cloned using their $clone()
method. A shallow copy without relations can be created by passing the shallow: true
option.
# Arguments
Argument | Type | Description |
---|---|---|
opt | CloneOptions | Optional options |
# Return value
Type | Description |
---|---|
Model | Deep clone of this |
# Examples
const shallowClone = modelInstance.$clone({ shallow: true });
Copied!
# toJSON()
const jsonObj = modelInstance.toJSON(opt);
Copied!
Exports this model as a JSON object.
See this section for more information.
# Arguments
Argument | Type | Description |
---|---|---|
opt | ToJsonOptions | Optional options |
# Return value
Type | Description |
---|---|
Object | Model as a JSON object. |
# Examples
const shallowObj = modelInstance.toJSON({ shallow: true, virtuals: false });
Copied!
const onlySomeVirtuals = modelInstance.toJSON({ virtuals: ["fullName"] });
Copied!
# $toJson()
Alias for toJSON
# $toDatabaseJson()
const row = modelInstance.$toDatabaseJson();
Copied!
Exports this model as a database JSON object.
This method is called internally to convert a model into a database row.
See this section for more information.
# Return value
Type | Description |
---|---|
Object | Database row. |
# $parseDatabaseJson()
class Person extends Model { $parseDatabaseJson(json) { // Remember to call the super class's implementation. json = super.$parseDatabaseJson(json); // Do your conversion here. return json; } }
Copied!
This is called when a Model instance is created from a database JSON object. This method converts the JSON object from the database format to the internal format.
You can override this method to carry out whatever conversions you want for the data when it's fetched from the database, before it's converted into a model instance. See this section for more information.
There are a couple of requirements for the implementation:
This function must be pure. It shouldn't have any side effects because it is called from "unexpected" places (for example to determine if your model somehow transforms column names between db and code).
This function must be able to handle any subset of the model's properties coming in.You cannot assume that some column is present in the
json
object as it depends on the select statement. There can also be additional columns because of joins, aliases etc. This method must also be prepared for null values in any property of thejson
object.
# Arguments
Argument | Type | Description |
---|---|---|
json | Object | The JSON POJO in database format |
# Return value
Type | Description |
---|---|
Object | The JSON POJO in internal format |
# $formatDatabaseJson()
class Person extends Model { $formatDatabaseJson(json) { // Remember to call the super class's implementation. json = super.$formatDatabaseJson(json); // Do your conversion here. return json; } }
Copied!
This is called when a Model is converted to database format.
You can override this method to carry out whatever conversions you want for the data when it's being sent to the database driver. See this section for more information.
There are a couple of requirements for the implementation:
This function must be pure. It shouldn't have any side effects because it is called from "unexpected" places (for example to determine if your model somehow transforms column names between db and code).
This function must be able to handle any subset of the model's properties coming in. You cannot assume that some property is present in the
json
object. There can also be additional properties. This method must also be prepared for null values in any property of thejson
object.
# Arguments
Argument | Type | Description |
---|---|---|
json | Object | The JSON POJO in internal format |
# Return value
Type | Description |
---|---|
Object | The JSON POJO in database format |
# $parseJson()
class Person extends Model { $parseJson(json, opt) { // Remember to call the super class's implementation. json = super.$parseJson(json, opt); // Do your conversion here. return json; } }
Copied!
This is called when a Model is created from a JSON object. Converts the JSON object from the external format to the internal format.
You can override this method to carry out whatever conversions you want for the data when a model instance is being created from external data. See this section for more information.
There are a couple of requirements for the implementation:
This function must be pure. It shouldn't have any side effects because it is called from "unexpected" places (for example to determine if your model somehow transforms column names between db and code).
This function must be able to handle any subset of the model's properties coming in. You cannot assume that some property is present in the
json
object. There can also be additional properties. This method must also be prepared for null values in any property of thejson
object.
# Arguments
Argument | Type | Description |
---|---|---|
json | Object | The JSON POJO in external format |
opt | ModelOptions | Optional options |
# Return value
Type | Description |
---|---|
Object | The JSON POJO in internal format |
# $formatJson()
class Person extends Model { $formatJson(json) { // Remember to call the super class's implementation. json = super.$formatJson(json); // Do your conversion here. return json; } }
Copied!
This is called when a Model is converted to JSON. Converts the JSON object from the internal format to the external format.
You can override this method to carry out whatever conversions you want for the data when a model instance is being converted into external representation. See this section for more information.
There are a couple of requirements for the implementation:
This function must be pure. It shouldn't have any side effects because it is called from "unexpected" places (for example to determine if your model somehow transforms column names between db and code).
This function must be able to handle any subset of the model's properties coming in. You cannot assume that some column is present in the
json
object as it depends on the select statement. There can also be additional columns because of joins, aliases etc. This method must also be prepared for null values in any property of thejson
object.
# Arguments
Argument | Type | Description |
---|---|---|
json | Object | The JSON POJO in internal format |
# Return value
Type | Description |
---|---|
Object | The JSON POJO in external format |
# $setJson()
modelInstance.$setJson(json, opt);
Copied!
Sets the values from a JSON object.
Validates the JSON before setting values.
# Arguments
Argument | Type | Description |
---|---|---|
json | Object | The JSON POJO to set |
opt | ModelOptions | Optional options |
# Return value
Type | Description |
---|---|
Model | this for chaining |
# $setDatabaseJson()
modelInstance.$setDatabaseJson(json);
Copied!
Sets the values from a JSON object in database format.
# Arguments
Argument | Type | Description |
---|---|---|
json | Object | The JSON POJO in database format |
# Return value
Type | Description |
---|---|
Model | this for chaining |
# $set()
modelInstance.$set(json);
Copied!
Sets the values from another model instance or object.
Unlike $setJson, this doesn't call any $parseJson hooks or validate the input. This simply sets each value in the object to this object.
# Arguments
Argument | Type | Description |
---|---|---|
obj | Object | The values to set |
# Return value
Type | Description |
---|---|
Model | this for chaining |
# $setRelated()
modelInstance.$setRelated(relation, relatedModels);
Copied!
Sets related models to a corresponding property in the object.
# Arguments
Argument | Type | Description |
---|---|---|
relation | string|Relation | Relation name or a relation instance to set. |
relatedModels | Model|Model[] | Models to set. |
# Return value
Type | Description |
---|---|
Model | this for chaining |
# Examples
person.$setRelated("parent", parent); console.log(person.parent);
Copied!
person.$setRelated("children", children); console.log(person.children[0]);
Copied!
# $appendRelated()
modelInstance.$appendRelated(relation, relatedModels);
Copied!
Appends related models to a corresponding property in the object.
# Arguments
Argument | Type | Description |
---|---|---|
relation | string|Relation | Relation name or a relation instance to append to. |
relatedModels | Model|Model[] | Models to append. |
# Return value
Type | Description |
---|---|
Model | this for chaining |
# Examples
person.$appendRelated("parent", parent); console.log(person.parent);
Copied!
person.$appendRelated("children", child1); person.$appendRelated("children", child2); child1 = person.children[person.children.length - 1]; child2 = person.children[person.children.length - 2];
Copied!
# $loadRelated()
WARNING
Deprecated! Will be removed in version 3.0. Use $fetchGraph instead.
# $fetchGraph()
const builder = person.$fetchGraph(expression, options);
Copied!
Shortcut for Person.fetchGraph(person, options)
# $traverse()
person.$traverse(filterConstructor, callback);
Copied!
Shortcut for Model.traverse(filterConstructor, this, callback).
# $traverseAsync()
person.$traverseAsync(filterConstructor, callback);
Copied!
Shortcut for Model.traverseAsync(filterConstructor, this, callback).
# $knex()
const knex = person.$knex();
Copied!
Shortcut for Person.knex().
# $transaction()
const knex = person.$transaction();
Copied!
Shortcut for Person.knex().
# $id()
console.log(model.$id()); // -> 100 // Sets the id. model.$id(100);
Copied!
Returns or sets the identifier of a model instance.
The identifier property does not have to be accessed or set using this method.
If the identifier property is known it can be accessed or set just like any other property. You don't need to use this method to set the identifier. This method is mainly helpful when building plugins and other tools on top of ExpressWeb JS.
# Examples
Composite key
console.log(model.$id()); // -> [100, 20, 30] // Sets the id. model.$id([100, 20, 30]);
Copied!
# $beforeValidate()
class Person extends Model { $beforeValidate(jsonSchema, json, opt) { return jsonSchema; } }
Copied!
This is called before validation.
You can add any additional validation to this method. If validation fails, simply throw an exception and the query will be rejected. If you modify the jsonSchema
argument and return it, that one will be used to validate the model.
opt.old
object contains the old values while json
contains the new values if validation is being done for an existing object.
# Arguments
Argument | Type | Description |
---|---|---|
jsonSchema | Object | A deep clone of this class's jsonSchema |
json | Object | The JSON object to be validated |
opt | ModelOptions | Optional options |
# Return value
Type | Description |
---|---|
Object | The modified jsonSchema or the input jsonSchema. |
# $afterValidate()
class Person extends Model { $afterValidate(json, opt) {} }
Copied!
This is called after successful validation.
You can do further validation here and throw an error if something goes wrong.
opt.old
object contains the old values while json
contains the new values if validation is being done for an existing object.
# Arguments
Argument | Type | Description |
---|---|---|
json | Object | The JSON object to be validated |
opt | ModelOptions | Optional options |
# $validate()
modelInstance.$validate();
Copied!
Validates the model instance.
Calls $beforeValidate and $afterValidate methods. This method is called automatically from fromJson and $setJson methods. This method can also be called explicitly when needed.
# Throws
Type | Description |
---|---|
ValidationError | If validation fails. |
# $omit()
modelInstance.$omit(keys);
Copied!
Omits a set of properties.
# Arguments
Argument | Type | Description |
---|---|---|
keys | string string[] Object<string, boolean> | keys to omit |
# Return value
Type | Description |
---|---|
Model | this for chaining |
# Examples
Omits a set of properties.
const json = person .fromJson({ firstName: "Jennifer", lastName: "Lawrence", age: 24 }) .$omit("lastName") .toJSON(); console.log(_.has(json, "lastName")); // --> false
Copied!
const json = person .fromJson({ firstName: "Jennifer", lastName: "Lawrence", age: 24 }) .$omit(["lastName"]) .toJSON(); console.log(_.has(json, "lastName")); // --> false
Copied!
const json = person .fromJson({ firstName: "Jennifer", lastName: "Lawrence", age: 24 }) .$omit({ lastName: true }) .toJSON(); console.log(_.has(json, "lastName")); // --> false
Copied!
# $pick()
modelInstance.$pick(keys);
Copied!
Picks a set of properties.
# Arguments
Argument | Type | Description |
---|---|---|
keys | string string[] Object<string, boolean> | keys to pick |
# Return value
Type | Description |
---|---|
Model | this for chaining |
# Examples
const json = person .fromJson({ firstName: "Jennifer", lastName: "Lawrence", age: 24 }) .$pick("firstName", "age") .toJSON(); console.log(_.has(json, "lastName")); // --> false
Copied!
const json = person .fromJson({ firstName: "Jennifer", lastName: "Lawrence", age: 24 }) .$pick(["firstName", "age"]) .toJSON(); console.log(_.has(json, "lastName")); // --> false
Copied!
const json = person .fromJson({ firstName: "Jennifer", lastName: "Lawrence", age: 24 }) .$pick({ firstName: true, age: true }) .toJSON(); console.log(_.has(json, "lastName")); // --> false
Copied!
← Static Methods Types →