Class: SC.Store
Extends
SC.Object.
The Store is where you can find all of your dataHashes
. Stores can be
chained for editing purposes and committed back one chain level at a time
all the way back to a persistent data source.
Every application you create should generally have its own store objects. Once you create the store, you will rarely need to work with the store directly except to retrieve records and collections.
Internally, the store will keep track of changes to your json data hashes and manage syncing those changes with your data source. A data source may be a server, local storage, or any other persistent code.
Defined in: store.js
- Since:
- SproutCore 1.0
Field Summary
- CHAIN_CONFLICT_ERROR
- changelog
- childRecords
- commitRecordsAutomatically
- dataSource
- EDITABLE
- editables
- INHERITED
- isNested
- LOCKED
- name
- NESTED_STORE_RETRIEVE_DIRTY_ERROR
- NESTED_STORE_UNSUPPORTED_ERROR
- nestedStores
- NO_PARENT_STORE_ERROR
- parentRecords
- queryErrors
- recordErrors
- Fields borrowed from SC.Object:
- concatenatedProperties, isDestroyed, isObject, nextProperty, object, property, target, toInvalidate
- Fields borrowed from SC.Observable:
- isObservable
Instance Methods
- cancelRecord(recordTypes, ids, storeKeys)
- cancelRecords(recordTypes, ids, storeKeys)
- cascade(dataSource)
- chain(attrs, newStoreClass)
- chainAutonomousStore(attrs, newStoreClass)
- commitRecord(recordType, id, storeKey, params, callback)
- commitRecords(recordTypes, ids, storeKeys, params, callback)
- createRecord(recordType, dataHash, id)
- createRecords(recordTypes, dataHashes, ids)
- dataHashDidChange(storeKeys, rev, statusOnly, key)
- dataSourceDidCancel(storeKey)
- dataSourceDidCancelQuery(query)
- dataSourceDidComplete(storeKey, dataHash, newId)
- dataSourceDidDestroy(storeKey)
- dataSourceDidError(storeKey, error)
- dataSourceDidErrorQuery(query, error)
- dataSourceDidFetchQuery(query, storeKeys)
- destroyRecord(recordType, id, storeKey)
- destroyRecords(recordTypes, ids, storeKeys)
- find(recordType, id)
- flush()
- from(dataSource)
- generateStoreKey()
- hasNestedStore(store)
- idFor(storeKey)
- loadQueryResults(query, storeKeys)
- loadRecord(recordType, dataHash, id)
- loadRecords(recordTypes, dataHashes, ids)
- materializeParentRecord(childStoreKey)
- materializeRecord(storeKey)
- parentStoreKeyExists(storeKey)
- peekStatus(storeKey)
- pushDestroy(recordType, id, storeKey)
- pushError(recordType, id, error, storeKey)
- pushRetrieve(recordType, id, dataHash, storeKey)
- queryFor(storeKey)
- readDataHash(storeKey)
- readEditableDataHash(storeKey)
- readEditableProperty(storeKey, propertyName)
- readError(storeKey)
- readQueryError(query)
- readStatus(storeKey)
- recordArrayWillDestroy(recordArray)
- recordDidChange(recordType, id, storeKey, key, if)
- recordsDidChange(recordTypes, ids, storeKeys)
- recordsFor(recordType)
- recordTypeFor(storeKey)
- refreshQuery(query)
- refreshRecord(recordType, id, storeKey, callback)
- refreshRecords(recordTypes, ids, storeKeys, callback)
- registerChildToParent(parentStoreKey, childStoreKey, path)
- removeDataHash(storeKey, status)
- replaceIdFor(storeKey, newPrimaryKey)
- replaceRecordTypeFor(storeKey, recordType)
- reset()
- retrieveRecord(recordType, id, storeKey, isRefresh, callback)
- retrieveRecords(recordTypes, ids, storeKeys, isRefresh, callback)
- statusString(storeKey)
- storeKeyEditState(storeKey)
- storeKeyExists(recordType, primaryKey)
- storeKeyFor(recordType, primaryKey)
- storeKeys()
- storeKeysFor(recordType)
- toString()
- unloadRecord(recordType, id, storeKey, newStatus)
- unloadRecords(recordTypes, ids, storeKeys, newStatus)
- unregisterChildFromParent(childStoreKey)
- writeDataHash(storeKey, hash, status)
- writeStatus(storeKey, newStatus, error)
Field Detail
CHAIN_CONFLICT_ERROR ErrorStandard error raised if you try to commit changes from a nested store and there is a conflict.
A set of storeKeys
that need to be committed back to the data source. If
you call commitRecords()
without passing any other parameters, the keys
in this set will be committed instead.
- Default value:
- NO
The data source is the persistent storage that will provide data to the store and save changes. You normally will set your data source when you first create your store in your application.
Array indicates whether a data hash is possibly in use by an external record for editing. If a data hash is editable then it may be modified at any time and therefore chained stores may need to clone the attributes before keeping a copy of them.
Note that this is kept as an array because it will be stored as a dense array on some browsers, making it faster.
Data hash state indicates the hash is tracking changes from the parent store and is not editable.
- Default value:
- NO
Data hash state indicates the hash no longer tracks changes from a parent store, but it is not editable.
An (optional) name of the store, which can be useful during debugging, especially if you have multiple nested stores.
Standard error if you try to retrieve a record in a nested store that is dirty. (This is allowed on the main store, but not in nested stores.)
Standard error if you try to perform an operation on a nested store that is only supported in root stores.
An array of all the chained stores that current rely on the receiver store.
Standard error if you try to perform an operation on a nested store without a parent.
A hash of SC.Error
objects associated with queries (indexed by the GUID
of the query).
Errors passed from the data source in the call to
dataSourceDidErrorQuery()
are stored here.
An array of SC.Error
objects associated with individual records in the
store (indexed by store keys).
Errors passed form the data source in the call to dataSourceDidError
() are
stored here.
Instance Method Detail
Cancels an inflight request for the passed record. Depending on the server implementation, this could cancel an entire request, causing other records to also transition their current state.
Cancels an inflight request for the passed records. Depending on the server implementation, this could cancel an entire request, causing other records to also transition their current state.
Convenience method. Creates a CascadeDataSource
with the passed
data source arguments and sets the CascadeDataSource
as the data source
for the receiver.
- Parameters:
- dataSource SC.DataSource...
- one or more data source arguments
- Returns:
- SC.Store
- receiver
Returns a new nested store instance that can be used to buffer changes
until you are ready to commit them. When you are ready to commit your
changes, call commitChanges()
or destroyChanges()
and then destroy()
when you are finished with the chained store altogether.
store = MyApp.store.chain();
.. edit edit edit
store.commitChanges().destroy();
- Parameters:
- attrs Hash
- optional attributes to set on new store
- newStoreClass Class
- optional the class of the newly-created nested store (defaults to SC.NestedStore)
- Returns:
- SC.NestedStore
- new nested store chained to receiver
Creates an autonomous nested store that is connected to the data source.
Use this kind of nested store to ensure that all records that are committed into the main store are first of all committed to the server.
For example,
nestedStore = store.chainAutonomousStore();
// ... commit all changes from the nested store to the remote data store
nestedStore.commitRecords();
// or commit the changes of a nested store's record to the remote data store ...
nestedRecord.commitRecord();
Resolving nested store commits with the main store
When the committed changes are deemed successful (either by observing the status of the modified
record(s) or by using callbacks with commitRecord
/commitRecords), the changes can be passed back
to the main store.
In the case that the commits are all successful, simply commit to the main store using
commitSuccessfulChanges
. Note, that using commitSuccessfulChanges
rather than the standard
commitChanges
ensures that only clean changes propagate back to the main store.
For example,
nestedStore.commitSuccessfulChanges();
In the case that some or all of the commits fail, you can still use commitSuccessfulChanges
to
update only those commits that have succeeded in the main store or wait until all commits have
succeeded. Regardless, it will be up to your application to act on the failures and work with
the user to resolve all failures until the nested store records are all clean.
- Parameters:
- attrs Hash Optional
- attributes to set on new store
- newStoreClass Class Optional
- the class of the newly-created nested store (defaults to SC.NestedStore)
- Returns:
- SC.NestedStore
- new nested store chained to receiver
Commits the passed store key or id. Based on the current state of the record, this will ask the data source to perform the appropriate action on the store key.
You have to pass either the id or the storeKey
otherwise it will return
NO
.
- Parameters:
- recordType SC.Record
- the expected record type
- id String
- the id of the record to commit
- storeKey Number
- the storeKey of the record to commit
- params Hash
- optional additional params that will passed down to the data source
- callback Function|Array
- function or array of functions
- Returns:
- Boolean
- if the action was successful.
Commits the passed store keys or ids. If no storeKey
s are given,
it will commit any records in the changelog.
Based on the current state of the record, this will ask the data source to perform the appropriate actions on the store keys.
Creates a new record instance with the passed recordType
and dataHash
.
You can also optionally specify an id or else it will be pulled from the
data hash.
Example:
MyApp.Record
= SC.Record.extend(
{
attrA: SC.Record.attr(String
, { defaultValue
: 'def' }),
isAttrB
: SC.Record.attr(Boolean
, { key: 'attr_b' }),
primaryKey
: 'pKey'
});
// If you don't provide a value and have designated a defaultValue
, the
// defaultValue
will be used.
MyApp.store.createRecord(MyApp.Record).get('attributes')
;
{ attrA: 'def' }
// If you use a key on an attribute, you can specify the key name or the
// attribute name when creating the record, but if you specify both, only
// the key name will be used.
MyApp.store.createRecord(MyApp.Record
, { isAttrB
: YES
}).get('attributes');
{ attr_b:
YES
}MyApp.store.createRecord(MyApp.Record
, { attr_b: YES }).get('attributes'); { attr_b: YES }MyApp.store.createRecord(MyApp.Record
, {isAttrB
: NO, attr_b: YES }).get('attributes'); { attr_b: YES }
Note that the record will not yet be saved back to the server. To save
a record to the server, call commitChanges()
on the store.
Creates an array of new records. You must pass an array of dataHash
es
plus a recordType
and, optionally, an array of ids. This will create an
array of record instances with the same record type.
If you need to instead create a bunch of records with different data types
you can instead pass an array of recordType
s, one for each data hash.
Call this method whenever you modify some editable data hash to register with the Store that the attribute values have actually changed. This will do the book-keeping necessary to track the change across stores including managing locks.
Called by a dataSource
when it cancels an inflight operation on a
record. This will transition the record back to it non-inflight state.
- Parameters:
- storeKey Number
- record store key to cancel
- Returns:
- SC.Store
- receiver
Called by your data source if it cancels fetching the results of a query. This will put any RecordArray's back into its original state (READY or EMPTY).
Called by a data source when it creates or commits a record. Passing an
optional id will remap the storeKey
to the new record id. This is
required when you commit a record that does not have an id yet.
Called by a data source when it has destroyed a record. This will transition the record to the proper state.
- Parameters:
- storeKey Number
- record store key to cancel
- Returns:
- SC.Store
- receiver
Called by your data source if it encountered an error loading the query. This will put the query into an error state until you try to refresh it again.
Called by your data source whenever you finish fetching the results of a
query. This will put the record array for the query into a READY_CLEAN
state if it was previously loading or refreshing.
Handling REMOTE queries
Note that if the query is REMOTE, then you must first load the results
into the store using loadRecords()
and pass the ordered array of store
keys returned by loadRecords()
into this method.
For example,
storeKeys = store.loadRecords(MyApp.SomeType, body.contacts);
store.dataSourceDidFetchQuery(query, storeKeys);
Automatic updates
When you call this method the record array for the query will notify that its contents have changed. If the query is LOCAL then the contents will update automatically to include any new records you added to the store. If the query is REMOTE the contents will update to be the ordered records for the passed in store keys.
Incremental loading for REMOTE queries
If you want to support incremental loading, then pass an SC.SparseArray
object to hold the store keys. This will allow you to load results
incrementally and provide more store keys as you do.
See the SC.SparseArray
documentation for more information.
Destroys a record, removing the data hash from the store and adding the record to the destroyed changelog. If you try to destroy a record that is already destroyed then this method will have no effect. If you destroy a record that does not exist or an error then an exception will be raised.
Destroys a group of records. If you have a set of record ids, destroying them this way can be faster than retrieving each record and destroying it individually.
You can pass either a single recordType
or an array of recordType
s. If
you pass a single recordType
, then the record type will be used for each
record. If you pass an array, then each id must have a matching record
type in the array.
You can optionally pass an array of storeKey
s instead of the recordType
and ids. In this case the first two parameters will be ignored. This
is usually only used by low-level internal methods. You will not usually
destroy records this way.
Finds a single record instance with the specified recordType
and id or
an array of records matching some query conditions.
Finding a Single Record
If you pass a single recordType
and id, this method will return an
actual record instance. If the record has not been loaded into the store
yet, this method will ask the data source to retrieve it. If no data
source indicates that it can retrieve the record, then this method will
return null
.
Note that if the record needs to be retrieved from the server, then the
record instance returned from this method will not have any data yet.
Instead it will have a status of SC.Record.READY_LOADING
. You can
monitor the status property to be notified when the record data is
available for you to use it.
Find a Collection of Records
If you pass only a record type or a query object, you can instead find
all records matching a specified set of conditions. When you call
find()
in this way, it will create a query if needed and pass it to the
data source to fetch the results.
If this is the first time you have fetched the query, then the store will
automatically ask the data source to fetch any records related to it as
well. Otherwise you can refresh the query results at anytime by calling
refresh()
on the returned RecordArray
.
You can detect whether a RecordArray is fetching from the server by checking its status.
Examples
Finding a single record:
MyApp.store.find(MyApp.Contact, "23"); // returns MyApp.Contact
Finding all records of a particular type:
MyApp.store.find(MyApp.Contact); // returns SC.RecordArray of contacts
Finding all contacts with first name John:
var query = SC.Query.local(MyApp.Contact, "firstName = %@", "John");
MyApp.store.find(query); // returns SC.RecordArray of contacts
Finding all contacts using a remote query:
var query = SC.Query.remote(MyApp.Contact);
MyApp.store.find(query); // returns SC.RecordArray filled by server
Delivers any pending changes to materialized records. Normally this happens once, automatically, at the end of the RunLoop. If you have updated some records and need to update records immediately, however, you may call this manually.
- Returns:
- SC.Store
- receiver
Convenience method. Sets the current data source to the passed property.
This will also set the store property on the dataSource
to the receiver.
If you are using this from the core.js
method of your app, you may need to
just pass a string naming your data source class. If this is the case,
then your data source will be instantiated the first time it is requested.
- Parameters:
- dataSource SC.DataSource|String
- the data source
- Returns:
- SC.Store
- receiver
Used to determine if a nested store belongs directly or indirectly to the receiver.
- Parameters:
- storeKey Number
- the store key
- Returns:
- String
- primaryKey value
Convenience method can be called by the store or other parts of your
application to load a record into the store. This method will take a
recordType
and a data hashes and either add or update the
record in the store.
The loaded records will be in an SC.Record.READY_CLEAN
state, indicating
they were loaded from the data source and do not need to be committed
back before changing.
This method will check the state of the storeKey
and call either
pushRetrieve()
or dataSourceDidComplete()
. The standard state constraints
for these methods apply here.
The return value will be the storeKey
used for the push. This is often
convenient to pass into loadQuery()
, if you are fetching a remote query.
If you are upgrading from a pre SproutCore 1.0 application, this method
is the closest to the old updateRecord()
.
Convenience method can be called by the store or other parts of your
application to load records into the store. This method will take a
recordType
and an array of data hashes and either add or update the
record in the store.
The loaded records will be in an SC.Record.READY_CLEAN
state, indicating
they were loaded from the data source and do not need to be committed
back before changing.
This method will check the state of each storeKey
and call either
pushRetrieve()
or dataSourceDidComplete()
. The standard state
constraints for these methods apply here.
The return value will be the storeKeys
used for each push. This is often
convenient to pass into loadQuery()
, if you are fetching a remote query.
If you are upgrading from a pre SproutCore 1.0 application, this method
is the closest to the old updateRecords()
.
- Parameters:
- childStoreKey
Given a storeKey
, return a materialized record. You will not usually
call this method yourself. Instead it will used by other methods when
you find records by id or perform other searches.
If a recordType
has been mapped to the storeKey
, then a record instance
will be returned even if the data hash has not been requested yet.
Each Store instance returns unique record instances for each storeKey
.
- Parameters:
- storeKey Number
- The storeKey for the dataHash.
- Returns:
- SC.Record
- Returns a record instance.
- Parameters:
- storeKey Number
- The store key of the parent
Reads the current status for the storeKey
without actually locking the
record. Usually you won't need to use this method. It is mostly used
internally.
- Parameters:
- storeKey Number
- the store key
- Returns:
- Number
- status
Call by the data source whenever you want to push a deletion into the store.
Call by the data source whenever you want to push an error into the store.
Call by the data source whenever you want to push new data out of band into the store.
Given a storeKey
, returns the query object associated with the key. If
no query is associated with the storeKey
, returns null
.
- Parameters:
- storeKey Number
- the store key
- Returns:
- SC.Query
- query query object
Returns the data hash for the given storeKey
. This will also 'lock'
the hash so that further edits to the parent store will no
longer be reflected in this store until you reset.
- Parameters:
- storeKey Number
- key to retrieve
- Returns:
- Hash
- data hash or null
Returns the data hash for the storeKey
, cloned so that you can edit
the contents of the attributes if you like. This will do the extra work
to make sure that you only clone the attributes one time.
If you use this method to modify data hash, be sure to call
dataHashDidChange()
when you make edits to record the change.
- Parameters:
- storeKey Number
- the store key to retrieve
- Returns:
- Hash
- the attributes hash
Reads a property from the hash - cloning it if needed so you can modify
it independently of any parent store. This method is really only well
tested for use with toMany
relationships. Although it is public you
generally should not call it directly.
- Parameters:
- storeKey Number
- The store key of the record.
- Returns:
- SC.Error
- SC.Error or undefined if no error associated with the record.
Reads the current status for a storeKey
. This will also lock the data
hash. If no status is found, returns SC.RECORD_EMPTY
.
- Parameters:
- storeKey Number
- the store key
- Returns:
- Number
- status
Called by the record array just before it is destroyed. This will de-register it from receiving future notifications.
You should never call this method yourself. Instead call destroy()
on
the RecordArray
directly.
- Parameters:
- recordArray SC.RecordArray
- the record array
- Returns:
- SC.Store
- receiver
Notes that the data for the given record id has changed. The record will be committed to the server the next time you commit the root store. Only call this method on a record in a READY state of some type.
Mark a group of records as dirty. The records will be committed to the server the next time you commit changes on the root store. If you have a set of record ids, marking them dirty this way can be faster than retrieving each record and destroying it individually.
You can pass either a single recordType
or an array of recordType
s. If
you pass a single recordType
, then the record type will be used for each
record. If you pass an array, then each id must have a matching record
type in the array.
You can optionally pass an array of storeKey
s instead of the recordType
and ids. In this case the first two parameters will be ignored. This
is usually only used by low-level internal methods.
Array of all records currently in the store with the specified type. This method only reflects the actual records loaded into memory and therefore is not usually needed at runtime. However you will often use this method for testing.
- Parameters:
- storeKey Number
- the store key
- Returns:
- SC.Record
- record instance
Called by the record array whenever it needs the data source to refresh
its contents. Nested stores will actually just pass this along to the
parent store. The parent store will call fetch()
on the data source.
You should never call this method yourself. Instead call refresh()
on
the RecordArray
directly.
Refreshes a record from the server. If the record has already been loaded
in the store, then this method will request a refresh from the
dataSource
. Otherwise it will attempt to retrieve the record.
Refreshes a set of records from the server. If the records has already been loaded
in the store, then this method will request a refresh from the
dataSource
. Otherwise it will attempt to retrieve them.
- Parameters:
- parentStoreKey
- childStoreKey
- path
Removes the data hash from the store. This does not imply a deletion of
the record. You could be simply unloading the record. Either way,
removing the dataHash
will be synced back to the parent store but not to
the server.
Note that you can optionally pass a new status to go along with this. If
you do not pass a status, it will change the status to SC.RECORD_EMPTY
(assuming you just unloaded the record). If you are deleting the record
you may set it to SC.Record.DESTROYED_CLEAN
.
Be sure to also call dataHashDidChange()
to register this change.
Swaps the primaryKey
mapped to the given storeKey
with the new
primaryKey
. If the storeKey
is not currently associated with a record
this will raise an exception.
Swaps the recordType
recorded for a given storeKey
. Normally you
should not call this method directly as it can damage the store behavior.
This method is used by other store methods to set the recordType
for a
storeKey
.
Resets the store content. This will clear all internal data for all records, resetting them to an EMPTY state. You generally do not want to call this method yourself, though you may override it.
- Returns:
- SC.Store
- receiver
Retrieves a record from the server. If the record has already been loaded
in the store, then this method will simply return. Otherwise if your
store has a dataSource
, this will call the dataSource
to retrieve the
record. Generally you will not need to call this method yourself.
Instead you can just use find()
.
This will not actually create a record instance but it will initiate a
load of the record from the server. You can subsequently get a record
instance itself using materializeRecord()
.
Retrieves a set of records from the server. If the records has
already been loaded in the store, then this method will simply return.
Otherwise if your store has a dataSource
, this will call the
dataSource
to retrieve the record. Generally you will not need to
call this method yourself. Instead you can just use find()
.
This will not actually create a record instance but it will initiate a
load of the record from the server. You can subsequently get a record
instance itself using materializeRecord()
.
- Parameters:
- storeKey Number
- Returns:
- String
Returns the current edit status of a store key. May be one of
EDITABLE
or LOCKED
. Used mostly for unit testing.
- Parameters:
- storeKey Number
- the store key
- Returns:
- Number
- edit status
Given a primaryKey
value for the record, returns the associated
storeKey
. As opposed to storeKeyFor()
however, this method
will NOT generate a new storeKey
but returned undefined
.
Given a recordType
and primaryKey
, find the storeKey
. If the
primaryKey
has not been assigned a storeKey
yet, it will be added.
Finds all storeKey
s in this store
and returns an array.
- Returns:
- Array
- set of storeKeys
Finds all storeKey
s of a certain record type in this store
and returns an array.
Unloads a record, removing the data hash from the store. If you try to unload a record that is already destroyed then this method will have no effect. If you unload a record that does not exist or an error then an exception will be raised.
Unloads a group of records. If you have a set of record ids, unloading them this way can be faster than retrieving each record and unloading it individually.
You can pass either a single recordType
or an array of recordType
s. If
you pass a single recordType
, then the record type will be used for each
record. If you pass an array, then each id must have a matching record
type in the array.
You can optionally pass an array of storeKey
s instead of the recordType
and ids. In this case the first two parameters will be ignored. This
is usually only used by low-level internal methods. You will not usually
unload records this way.
Unregister the Child Record from its Parent. This will cause the Child Record to be removed from the store.
- Parameters:
- childStoreKey Number
- storeKey to unregister
Replaces the data hash for the storeKey
. This will lock the data hash
and mark them as cloned. This will also call dataHashDidChange()
for
you.
Note that the hash you set here must be a different object from the
original data hash. Once you make a change here, you must also call
dataHashDidChange()
to register the changes.
If the data hash does not yet exist in the store, this method will add it. Pass the optional status to edit the status as well.