Class: SC.Record
Extends
SC.Object.
A Record is the core model class in SproutCore. It is analogous to NSManagedObject in Core Data and EOEnterpriseObject in the Enterprise Objects Framework (aka WebObjects), or ActiveRecord::Base in Rails.
To create a new model class, in your SproutCore workspace, do:
$ sc-gen model MyApp.MyModel
This will create MyApp.MyModel
in clients/my_app/models/my_model.js.
The core attributes hash is used to store the values of a record in a format that can be easily passed to/from the server. The values should generally be stored in their raw string form. References to external records should be stored as primary keys.
Normally you do not need to work with the attributes hash directly. Instead you should use get/set on normal record properties. If the property is not defined on the object, then the record will check the attributes hash instead.
You can bulk update attributes from the server using the
updateAttributes()
method.
Polymorphic Records
SC.Record
also supports polymorphism, which allows subclasses of a record type to share a common
identity. Polymorphism is similar to inheritance (i.e. a polymorphic subclass inherits its parents
properties), but differs in that polymorphic subclasses can be considered to be "equal" to each
other and their superclass. This means that any memmber of the polymorphic class group should be
able to stand in for any other member.
These examples may help identify the difference. First, let's look at the classic inheritance model,
// This is the "root" class. All subclasses of MyApp.Person will be unique from MyApp.Person.
MyApp.Person = SC.Record.extend({});
// As a subclass, MyApp.Female inherits from a MyApp.Person, but is not "equal" to it.
MyApp.Female = MyApp.Person.extend({
isFemale: true
});
// As a subclass, MyApp.Male inherits from a MyApp.Person, but is not "equal" to it.
MyApp.Male = MyApp.Person.extend({
isMale: true
});
// Load two unique records into the store.
MyApp.store.createRecord(MyApp.Female, { guid: '1' });
MyApp.store.createRecord(MyApp.Male, { guid: '2' });
// Now we can see that these records are isolated from each other.
var female = MyApp.store.find(MyApp.Person, '1'); // Returns an SC.Record.EMPTY record.
var male = MyApp.store.find(MyApp.Person, '2'); // Returns an SC.Record.EMPTY record.
// These records are MyApp.Person only.
SC.kindOf(female, MyApp.Female); // false
SC.kindOf(male, MyApp.Male); // false
Next, let's make MyApp.Person
a polymorphic class,
// This is the "root" polymorphic class. All subclasses of MyApp.Person will be able to stand-in as a MyApp.Person.
MyApp.Person = SC.Record.extend({
isPolymorphic: true
});
// As a polymorphic subclass, MyApp.Female is "equal" to a MyApp.Person.
MyApp.Female = MyApp.Person.extend({
isFemale: true
});
// As a polymorphic subclass, MyApp.Male is "equal" to a MyApp.Person.
MyApp.Male = MyApp.Person.extend({
isMale: true
});
// Load two unique records into the store.
MyApp.store.createRecord(MyApp.Female, { guid: '1' });
MyApp.store.createRecord(MyApp.Male, { guid: '2' });
// Now we can see that these records are in fact "equal" to each other. Which means that if we
// search for "people", we will get "males" & "females".
var female = MyApp.store.find(MyApp.Person, '1'); // Returns record.
var male = MyApp.store.find(MyApp.Person, '2'); // Returns record.
// These records are MyApp.Person as well as their unique subclass.
SC.kindOf(female, MyApp.Female); // true
SC.kindOf(male, MyApp.Male); // true
Defined in: record.js
- See:
- SC.RecordAttribute
- Since:
- SproutCore 1.0
Field Summary
- SC.Record.BAD_STATE_ERROR
- SC.Record.BUSY
- SC.Record.BUSY_COMMITTING
- SC.Record.BUSY_CREATING
- SC.Record.BUSY_DESTROYING
- SC.Record.BUSY_ERROR
- SC.Record.BUSY_LOADING
- SC.Record.BUSY_REFRESH
- SC.Record.BUSY_REFRESH_CLEAN
- SC.Record.BUSY_REFRESH_DIRTY
- SC.Record.CLEAN
- SC.Record.DESTROYED
- SC.Record.DESTROYED_CLEAN
- SC.Record.DESTROYED_DIRTY
- SC.Record.DIRTY
- SC.Record.EMPTY
- SC.Record.ERROR
- SC.Record.fixtures
- SC.Record.GENERIC_ERROR
- SC.Record.ignoreUnknownProperties
- isParentRecord
- SC.Record.isPolymorphic
- isRecord
- nestedRecordNamespace
- SC.Record.NOT_FOUND_ERROR
- primaryKey
- SC.Record.READY
- SC.Record.READY_CLEAN
- SC.Record.READY_DIRTY
- SC.Record.READY_NEW
- SC.Record.RECORD_EXISTS_ERROR
- relationships
- store
- storeKey
- Fields borrowed from SC.Object:
- concatenatedProperties, isObject, nextProperty, object, property, target, toInvalidate
- Fields borrowed from SC.Observable:
- isObservable
Class Methods
- attr(type, opts)
- fetch(recordType, opts)
- find(store, id)
- storeKeyExists(id)
- storeKeyFor(id)
- storeKeysById()
- toMany(recordType, opts)
- toOne(recordType, opts)
Instance Methods
- attributes()
- beginEditing()
- commitRecord(params, recordOnly, callback)
- createNestedRecord(recordType, hash, psk, path)
- destroy(recordOnly)
- endEditing(key)
- errorObject()
- errorValue()
- generateIdForChild(childRecord)
- id(key, value)
- isDestroyed()
- isEditable(key, value)
- isError()
- isLoaded()
- isNestedRecord()
- normalize(includeNull)
- parentRecord()
- propagateToAggregates()
- readAttribute(key)
- readOnlyAttributes()
- recordDidChange(key)
- refresh(recordOnly, callback)
- registerNestedRecord(value, key, path)
- status()
- storeDidChangeProperties(statusOnly, key)
- toJSON()
- unknownProperty(key, value)
- unregisterNestedRecord(path)
- writeAttribute(key, value, ignoreDidChange)
Field Detail
SC.Record.BAD_STATE_ERROR SC.ErrorError for when you try to modify a record while it is in a bad state.
Generic state for records that have been submitted to data source
Use a logical AND (single &
) to test record status
- Default value:
- 0x0800
State for records that have been modified and submitted to server
Use a logical AND (single &
) to test record status
- Default value:
- 0x0810
State for new records that were created and submitted to the server; waiting on response from server
Use a logical AND (single &
) to test record status
- Default value:
- 0x0808
State for records that have been destroyed and submitted to server
Use a logical AND (single &
) to test record status
- Default value:
- 0x0840
State for records that are still loading data from the server
Use a logical AND (single &
) to test record status
- Default value:
- 0x0804
State for records that have requested a refresh from the server.
Use a logical AND (single &
) to test record status.
- Default value:
- 0x0820
State for records that have requested a refresh from the server.
Use a logical AND (single &
) to test record status
- Default value:
- 0x0821
State for records that have requested a refresh from the server.
Use a logical AND (single &
) to test record status
- Default value:
- 0x0822
Generic state for records with no local changes.
Use a logical AND (single &
) to test record status
- Default value:
- 0x0001
Generic state for records that have been destroyed
Use a logical AND (single &
) to test record status
- Default value:
- 0x0400
State for records that have been destroyed and committed to server
Use a logical AND (single &
) to test record status
- Default value:
- 0x0401
State for records that have been destroyed but not yet committed to server
Use a logical AND (single &
) to test record status
- Default value:
- 0x0402
Generic state for records with local changes.
Use a logical AND (single &
) to test record status
- Default value:
- 0x0002
State for records that are still loaded.
A record instance should never be in this state. You will only run into
it when working with the low-level data hash API on SC.Store
. Use a
logical AND (single &
) to test record status
- Default value:
- 0x0100
State for records in an error state.
Use a logical AND (single &
) to test record status
- Default value:
- 0x1000
Whether to ignore unknown properties when they are being set on the record object. This is useful if you want to strictly enforce the model schema and not allow dynamically expanding it by setting new unknown properties
- Default value:
- NO
- Default value:
- NO
If true, then searches for records of this type will return subclass instances. For example:
Person = SC.Record.extend()
;
Person.isPolymorphic
= true;
Male = Person.extend()
;
Female = Person.extend()
;
Using SC.Store#find, or a toOne or toMany relationship on Person will then return records of
type Male and Female. Polymorphic record types must have unique GUIDs across all subclasses.
- Default value:
- NO
- Default value:
- YES
- Default value:
- null
This is the primary key used to distinguish records. If the keys match, the records are assumed to be identical.
- Default value:
- 'guid'
Generic state for records that are loaded and ready for use
Use a logical AND (single &
) to test record status
- Default value:
- 0x0200
State for records that are loaded and ready for use with no local changes
Use a logical AND (single &
) to test record status
- Default value:
- 0x0201
State for records that are loaded and ready for use with local changes
Use a logical AND (single &
) to test record status
- Default value:
- 0x0202
State for records that are new - not yet committed to server
Use a logical AND (single &
) to test record status
- Default value:
- 0x0203
If set, this should be an array of active relationship objects that need
to be notified whenever the underlying record properties change.
Currently this is only used by toMany
relationships, but you could
possibly patch into this yourself also if you are building your own
relationships.
Note this must be a regular Array - NOT any object implementing SC.Array.
- Default value:
- null
The store that owns this record. All changes will be buffered into this store and committed to the rest of the store chain through here.
This property is set when the record instance is created and should not be changed or else it will break the record behavior.
- Default value:
- null
This is the store key for the record, it is used to link it back to the
dataHash
. If a record is reused, this value will be replaced.
You should not edit this store key but you may sometimes need to refer to this store key when implementing a Server object.
- Default value:
- null
Class Method Detail
Helper method returns a new SC.RecordAttribute
instance to map a simple
value or to-one relationship. At the very least, you should pass the
type class you expect the attribute to have. You may pass any additional
options as well.
Use this helper when you define SC.Record
subclasses.
MyApp.Contact = SC.Record.extend({
firstName: SC.Record.attr(String, { isRequired: YES })
});
- Parameters:
- type Class
- the attribute type
- opts Hash
- the options for the attribute
- Returns:
- SC.RecordAttribute
- created instance
Returns an SC.RecordAttribute
that describes a fetched attribute. When
you reference this attribute, it will return an SC.RecordArray
that uses
the type as the fetch key and passes the attribute value as a param.
Use this helper when you define SC.Record
subclasses.
MyApp.Group = SC.Record.extend({
contacts: SC.Record.fetch('MyApp.Contact')
});
- Parameters:
- recordType SC.Record|String
- The type of records to load
- opts Hash
- the options for the attribute
- Returns:
- SC.RecordAttribute
- created instance
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.
- Parameters:
- id String
- a record id
- Returns:
- Number
- a storeKey.
Given a primaryKey
value for the record, returns the associated
storeKey
. If the primaryKey
has not been assigned a storeKey
yet, it
will be added.
For the inverse of this method see SC.Store.idFor()
and
SC.Store.recordTypeFor()
.
- Parameters:
- id String
- a record id
- Returns:
- Number
- a storeKey.
Returns all storeKeys
mapped by Id for this record type. This method is used mostly by the
SC.Store
and the Record to coordinate. You will rarely need to call this method yourself.
Note that for polymorpic record classes, all store keys are kept on the top-most polymorphic superclass. This ensures that store key by id requests at any level return only the one unique store key.
Will return one of the following:
SC.ManyAttribute
that describes a record array backed by an array of guids stored in the underlying JSON.SC.ChildrenAttribute
that describes a record array backed by a array of hashes.You can edit the contents of this relationship.
For
SC.ManyAttribute
, If you set the inverse andisMaster: NO
key, then editing this array will modify the underlying data, but the inverse key on the matching record will also be edited and that record will be marked as needing a change.
- Parameters:
- recordType SC.Record|String
- The type of record to create
- opts Hash
- the options for the attribute
- Returns:
- SC.ManyAttribute|SC.ChildrenAttribute
- created instance
Will return one of the following:
SC.SingleAttribute
that converts the underlying ID to a single record. If you modify this property, it will rewrite the underlying ID. It will also modify the inverse of the relationship, if you set it.SC.ChildAttribute
that you can edit the contents of this relationship.
- Parameters:
- recordType SC.Record|String
- the type of the record to create
- opts Hash
- additional options
- Returns:
- SC.SingleAttribute|SC.ChildAttribute
- created instance
Instance Method Detail
This will return the raw attributes that you can edit directly. If you
make changes to this hash, be sure to call beginEditing()
before you get
the attributes and endEditing()
afterwards.
Defers notification of record changes until you call a matching
endEditing()
method. This method is called automatically whenever you
set an attribute, but you can call it yourself to group multiple changes.
Calls to beginEditing()
and endEditing()
can be nested.
- Returns:
- SC.Record
- receiver
Lets you commit this specific record to the store which will trigger the appropriate methods in the data source for you.
- Parameters:
- params Hash
- optional additional params that will passed down to the data source
- recordOnly boolean
- optional param if you want to only commit a single record if it has a parent.
- callback Function
- optional callback that the store will fire once the datasource finished committing
- Returns:
- SC.Record
- receiver
Deletes the record along with any dependent records. This will mark the
records destroyed in the store as well as changing the isDestroyed
property on the record to YES
. If this is a new record, this will avoid
creating the record in the first place.
- Parameters:
- recordOnly boolean
- optional param if you want to only THIS record even if it is a child record.
- Returns:
- SC.Record
- receiver
Notifies the store of record changes if this matches a top level call to
beginEditing()
. This method is called automatically whenever you set an
attribute, but you can call it yourself to group multiple changes.
Calls to beginEditing()
and endEditing()
can be nested.
Returns the current error object only if the record is in an error state.
If no explicit error object has been set, returns SC.Record.GENERIC_ERROR.
Returns the receiver if the record is in an error state. Returns null otherwise.
Override this function if you want to have a special way of creating ids for your child records
Returns the id for the record instance. The id is used to uniquely
identify this record instance from all others of the same type. If you
have a primaryKey set on this class, then the id will be the value of the
primaryKey` property on the underlying JSON hash.
- Parameters:
- key
- value
YES
when the record is in an editable state. You can use this property to
quickly determine whether attempting to modify the record would raise an
exception or not.
This property is both readable and writable. Note however that if you
set this property to YES
but the status of the record is anything but
SC.Record.READY
, the return value of this property may remain NO
.
- Parameters:
- key
- value
Returns YES
whenever the status is SC.Record.ERROR.
This will allow you
to put the UI into an error state.
YES
when the record's contents have been loaded for the first time. You
can use this to quickly determine if the record is ready to display.
Normalizing a record will ensure that the underlying hash conforms to the record attributes such as their types (transforms) and default values.
This method will write the conforming hash to the store and return the materialized record.
By normalizing the record, you can use .attributes()
and be
assured that it will conform to the defined model. For example, this
can be useful in the case where you need to send a JSON representation
to some server after you have used .createRecord()
, since this method
will enforce the 'rules' in the model such as their types and default
values. You can also include null values in the hash with the
includeNull
argument.
This will also ensure that any aggregate records are also marked dirty if this record changes.
Should not have to be called manually.
Reads the raw attribute from the underlying data hash. This method does not transform the underlying attribute at all.
This will return the raw attributes that you cannot edit directly. It is
useful if you want to efficiently look at multiple attributes in bulk. If
you would like to edit the attributes, see the attributes
property
instead.
You can invoke this method anytime you need to make the record as dirty.
This will cause the record to be committed when you commitChanges()
on the underlying store.
If you use the writeAttribute()
primitive, this method will be called
for you.
If you pass the key that changed it will ensure that observers are fired
only once for the changed property instead of allPropertiesDidChange()
Refresh the record from the persistent store. If the record was loaded from a persistent store, then the store will be asked to reload the record data from the server. If the record is new and exists only in memory then this call will have no effect.
Registers a child record with this parent record.
If the parent already knows about the child record, return the cached instance. If not, create the child record instance and add it to the child record cache.
All records generally have a life cycle as they are created or loaded into memory, modified, committed and finally destroyed. This life cycle is managed by the status property on your record.
The status of a record is modelled as a finite state machine. Based on the current state of the record, you can determine which operations are currently allowed on the record and which are not.
In general, a record can be in one of five primary states:
SC.Record.EMPTY
, SC.Record.BUSY
, SC.Record.READY
,
SC.Record.DESTROYED
, SC.Record.ERROR
. These are all described in
more detail in the class mixin (below) where they are defined.
Called by the store whenever the underlying data hash has changed. This will notify any observers interested in data hash properties that they have changed.
If you try to get/set a property not defined by the record, then this method will be called. It will try to get the value from the set of attributes.
This will also check is ignoreUnknownProperties
is set on the recordType
so that they will not be written to dataHash
unless explicitly defined
in the model schema.
Unregisters a child record from its parent record.
Since accessing a child (nested) record creates a new data hash for the child and caches the child record and its relationship to the parent record, it's important to clear those caches when the child record is overwritten or removed. This function tells the store to remove the child record from the store's various child record caches.
You should not need to call this function directly. Simply setting the child record property on the parent to a different value will cause the previous child record to be unregistered.
- Parameters:
- path String
- The property path of the child record.
Updates the passed attribute with the new value. This method does not
transform the value at all. If instead you want to modify an array or
hash already defined on the underlying json, you should instead get
an editable version of the attribute using editableAttribute()
.