ViewModelCache

Source
import { ViewModelCache } from "@prestojs/viewmodel";

Cache for ViewModel instances based on the specified field names set.

The key to the cache is the primary key for the record and the field names set on it. For example if you have a record that accepts id, name and email you could have a record cached for id, for name, for email or any combination of the 3 fields. This is to handle the common case of fetching partial data from a backend.

The cache implementation will update any cache entries that are a subset of a new cache entry. eg. Caching a record with all the possible fields set would result in all the existing partial field cache entries being updated to match the data on the full record for the fields it care about.

Usage:

// Assume User is a ViewModel already defined
// Add a record
User.cache.add(new User({ id: 1, name: 'John' }));
// Retrieve a record
const record = User.cache.get(1, ['id', 'name']);
// To update a record just add it again
User.cache.add(new User({ id: 1, name: 'Johnny' }));
// Cache is per unique set of fields but a superset will update a subset
User.cache.add(new User({ id: 1, name: 'Johnny Smith', email: 'johnny@test.com' }));
User.cache.get(1, ['id', 'name']);
// { id: 1, name: 'Johnny Smith' }
User.cache.get(1, ['id', 'name', 'email'])
// { id: 1, name: 'Johnny Smith', email: 'johnny@test.com' }
// Delete a specific cache for a subset of fields
User.cache.delete(1, ['id', 'name']);
User.cache.get(1, ['id', 'name']);
// null
User.cache.get(1, ['id', 'name', 'email'])
// { id: 1, name: 'Johnny Smith', email: 'johnny@test.com' }
// Or all fields
User.cache.delete(1);
User.cache.get(1, ['id', 'name', 'email'])
// null
// You can add multiple values at a time
User.cache.addList([johnny, sam]);
// You can listen to changes
User.cache.addListener(2, ['id', 'name'], (previous, next) => console.log(previous, 'change to', next));
User.cache.add(new User({ id: 2, name: 'Bob' }));
// null changed to User({ id: 2, name: 'Bob' })
User.cache.add(new User({ id: 2, name: 'Bobby' }));
// User({ id: 2, name: 'Bob' }) changed to User({ id: 2, name: 'Bobby' })
User.cache.delete(2)
// User({ id: 2, name: 'Bobby' }) changed to null
// You can listen to multiple changes. If you use this and addList then you only get one
// call for each change that occurs within addList
User.cache.addListenerList(
// Ids to listen for changes to
[3, 4],
// Only get updates for cached records with these field names
['id', 'name'],
(previous, next) => console.log(previous, 'change to', next)
);
User.cache.addList([new User({ id: 3, name: 'Jay' }), new User({ id: 4, name: 'Bee' })]);
// [null, null] changed to [new User({ id: 3, name: 'Jay' }), new User({ id: 4, name: 'Bee' })]
User.cache.addList([new User({ id: 3, name: 'Jayz' }), new User({ id: 4, name: 'Beeb' })]);
// [new User({ id: 3, name: 'Jay' }), new User({ id: 4, name: 'Bee' })] changed to [new User({ id: 3, name: 'Jayz' }), new User({ id: 4, name: 'Beeb' })]
User.cache.delete(3)
// [new User({ id: 3, name: 'Jayz' }), new User({ id: 4, name: 'Beeb' })] changed to [null, new User({ id: 4, name: 'Beeb' })]

If a model has a RelatedViewModelField the data for a related field can be retrieved using array notation:

// Fetch the 'name' field and the related 'group' record and its 'label' field
['name', ['group', 'label']]

To fetch all fields from a relation you can just specify its name:

['name', 'group']
// This will be expanded to include all non-relation fields on the related ViewModel
['name', ['group', 'label'], ['group', 'ownerId']]

NOTE: Using the shorthand for a relation won't include any nested relation

Accessing deeply related records is supported:

['name', ['group', 'owner', 'name']]

You can combine the shorthand with array notation to get all fields and the specified deep relations:

['name', 'group', ['group', 'owner', 'name']]
// Equivalent to:
['name', ['group', 'label'], ['group', 'owner', 'name']]

NOTE: When accessing a relation its sourceFieldName is always included regardless of whether you explicitly request it:

User.cache.get(1, ['name', 'group']);
// {
// id: 1,
// name: 'Bob',
// groupId: 1,
// group: {
// id: 1,
// label: 'Staff',
// }
// }

API

new ViewModelCache(viewModel)

Source
ParameterTypeDescription
*viewModelViewModel Class

The ViewModel this class is for

Methods

Add a record or records to the cache. Records are cached based on the fields that are set on them (record._assignedFields).

If record A has a superset of fields of record B then when A is cached it will update the cache for record B. The reverse isn't true.

ParameterTypeDescription
*recordOrDataT

The record instance to cache. If a plain object is passed then an instance of the view model will be created and returned. An array is also supported in which case each entry in the array will be converted to the view model if required and returned.

T
ParameterTypeDescription
*recordOrDataT[]
T[]
ParameterTypeDescription
*recordOrDataFieldDataMappingRaw
PartialViewModel
KeyTypeDescription
__instanceFieldMappingType__type
_assignedFieldsstring[]

List of field names with data available on this instance.

_dataObject

The assigned data for this record. You usually don't need to access this directly; values for a field can be retrieved from the record directly using the field name

_fObject

Access record bound fields. A record bound field is a normal field with it's value property set to the corresponding value for that field on the record. This is useful as a shortcut to get both the field and it's value.

class User extends viewModelFactory({ username: CharField() }) {}
const admin = new User({ id: 1, username: 'admin' });
// true
admin._f.username instanceof CharField();
admin._f.username.value === 'admin'
_keyPrimaryKey

Returns the primary key value(s) for this instance. This is to conform to the Identifiable interface.

_modelViewModel Class

Get the actual ViewModel class for this instance

clone
isEqual
toJS
ParameterTypeDescription
*recordOrDataFieldDataMappingRaw[]
PartialViewModel[]

Add a list of records. Use this in place of manually calling add() on each record individually so that listeners only get notified once of the change to the list rather than for each record in the list.

ParameterTypeDescription
*recordsT[]
T[]
ParameterTypeDescription
*recordsFieldDataMappingRaw[]
PartialViewModel[]

Add a listener to any changes at all. The detail of the changes are not available.

ParameterTypeDescription
*listener
Function

Function to that is called when any change occurs. The function is called with no parameters.

Function

A function that removes the listener

Add a listener for any changes, additions or deletions for the record(s) identified by pkOrPks for the field names fieldNames.

ParameterTypeDescription
*pkOrPksPrimaryKey

Primary key or array of multiple primary keys that identifies the record(s) to listen to changes/additions/deletions to

*fieldNamesFieldNames[]

Field names to listen to changes/additions/deletions to. See Field notation for supported format.

*listener
Function

Function to call with any changes

batchboolean

Whether or not to batch this call with other calls (defaults to true). You shouldn't need to change the default.

Function

A function that removes the listener

ParameterTypeDescription
*pkOrPksPrimaryKey[]
*fieldNamesFieldNames[]
*listener
Function
batchboolean
Function
ParameterTypeDescription
*pkOrPksPrimaryKey
*fieldNamesFieldPath[]
*listener
Function
batchboolean
Function
ParameterTypeDescription
*pkOrPksPrimaryKey[]
*fieldNamesFieldPath[]
*listener
Function
batchboolean
Function
ParameterTypeDescription
*pksPrimaryKey[]
*fieldNamesFieldNames[]
*listener
Function
Function
ParameterTypeDescription
*pksPrimaryKey[]
*fieldNamesFieldPath[]
*listener
Function
Function

Batch changes made within provided function. This guarantees that any changes made will result in a single call for each relevant listener.

User.cache.addListener(listenerAll);
User.cache.addListener(1, ['id', 'name'], listener);
User.cache.addListenerList([1, 2], ['id', 'name'], listenerList);
User.cache.batch(() => {
// This value won't appear in changes at all as it's replaced 2 lines down
User.cache.add({ id: 1, name: 'Bob', groupId: 1 });
User.cache.add({ id: 2, name: 'Sam', groupId: null });
User.cache.add({ id: 1, name: 'Bobby', groupId: 1 });
});
// All listeners called once
ParameterTypeDescription
*run
Function
T

Delete a record from the cache, optionally only for the specified fieldNames

If fieldNames is omitted then the cache for the record is cleared in it's entirety.

ParameterTypeDescription
*pkPrimaryKey

The primary key of the record to delete

fieldNames|FieldPath[]

Optionally only delete the entry with the specified field names. If this is not set then all data for the record is removed. See Field notation for supported format.

boolean

true if anything was removed, false otherwise

Get the currently cached version of the specified version

ParameterTypeDescription
*recordT

a current instance of a ViewModel to get the latest cached version of

One of the following:

T

OR

null

The cached record or null if none found

Get a record with the specified fieldNames set from the cache

ParameterTypeDescription
*pkPrimaryKey

the primary key of the record to get

*fieldNames|"*"

the field names to use to look up the cache entry. Use '*' to indicate all fields. See Field notation for supported format.

One of the following:

PartialViewModel
KeyTypeDescription
__instanceFieldMappingType__type
_assignedFieldsstring[]

List of field names with data available on this instance.

_dataObject

The assigned data for this record. You usually don't need to access this directly; values for a field can be retrieved from the record directly using the field name

_fObject

Access record bound fields. A record bound field is a normal field with it's value property set to the corresponding value for that field on the record. This is useful as a shortcut to get both the field and it's value.

class User extends viewModelFactory({ username: CharField() }) {}
const admin = new User({ id: 1, username: 'admin' });
// true
admin._f.username instanceof CharField();
admin._f.username.value === 'admin'
_keyPrimaryKey

Returns the primary key value(s) for this instance. This is to conform to the Identifiable interface.

_modelViewModel Class

Get the actual ViewModel class for this instance

clone
isEqual
toJS

OR

null

The cached record or null if none found

ParameterTypeDescription
*pkPrimaryKey
*fieldNamesFieldPath[]

One of the following:

InstanceType

OR

null

Get all records in the cache for the specified field names. This acts like getList but returns all records not just records with specified primary keys.

This function guarantees to return the same array (ie. passes strict equality check) if the underlying records have not changed.

ParameterTypeDescription
*fieldNames|"*"

List of field names to return records for. See Field notation for supported format.

PartialViewModel[]
ParameterTypeDescription
*fieldNamesFieldPath[]
InstanceType[]

Get list of cached records from an existing list of records

ParameterTypeDescription
*recordsT[]

List of existing ViewModel records to get latest cache version for

*removeNullstrue

whether to remove entries that have no record in the cache. Defaults to true.

T[]

an array of the cached records. Any records not found will be in the array as a null value if removeNulls is false otherwise they will be removed.

ParameterTypeDescription
*recordsT[]
removeNullsfalse

One of the following:

null[]

OR

T[]

Get a list of records with the specified fieldNames set from the cache

Any record that is not found will end up in the array as a null value. If this isn't desired you must filter them manually.

ParameterTypeDescription
*pksPrimaryKey[]

An array of primary keys

*fieldNames|"*"

the field names to use to look up the cached entries. See Field notation for supported format.

removeNullsboolean

whether to remove entries that have no record in the cache. Defaults to true.

One of the following:

null[]

OR

[]

an array of the cached records. Any records not found will be in the array as a null value if removeNulls is false otherwise they will be removed.

ParameterTypeDescription
*pksPrimaryKey[]
*fieldNames
null[]

Static Properties