Getting started
mg
uses regular Backbone models, but adds a few additional properties, a model registry and a model cache. To set up mg
hydration, define additional properties on the model and set the sync
function. For example:
var Post = Backbone.Model.extend({ sync: mg.sync('Post') }); mg.define('Post', Post);
The sync
function is added so that mg
can intercept model.sync()
, allowing it to detect when new models are created.
The mg.define()
call registers the model with hydration, so that relations can find the right model class and metadata.
Defining relations
To define relations, define the type of the related model on .rels["keyname"]
. For example, to have Post.author
be hydrated as an instance of Person
:
var Post = Backbone.Model.extend({ rels: { author: { type: 'Person' } } }); mg.define('Post', Post);
mg
supports both one-to-one, one-to-many and many-to-one relations. You do not need to explicitly define the type of relation, as it will be inferred from the JSON data.
For example, given the following JSON data, a one-to-one relation is detected:
mg.hydrate('Post', { id: 1, author: 1000 }, function(err, model) { ... });
This would be hydrated with .get('author')
set to the Person with id=1000
. If that model is not available from the local cache, it is fetched before returning the hydrated model.
Given the following JSON data, a one-to-many relation is detected:
mg.hydrate('Post', { id: 1, author: [ 200, 300 ] }, function(err, model) { ... });
Alternatively, the content of the array can be objects with an id attribute (controlled via Backbone's .idAttribute
):
mg.hydrate('Post', { id: 1, author: [ { id: 200 }, { id: 300 } ] }, function(err, model) { ... });
Both of these would be hydrated with .get('author')
set to a Backbone.Collection
instance containing the Person models with id=200
and id=300
.
Fetching models: the find API
The find API makes backend calls to find model ids, and returns fully hydrated models.
The URL is determined using model.url
, following the Backbone conventions. Here are some examples, see the BB docs as well. Using urlRoot:
var Post = mg.define('Post', Backbone.Model.extend({ urlRoot: '/posts/' }));
Or using a function:
var Post = mg.define('Post', Backbone.Model.extend({ url: function() { return '/posts/' + encodeURIComponent(this.id) + '?exclude=foo'; } }));
Models are cached in-memory. This means that you should always use .findById()
, since it will retrive the model from cache if possible.
findById(name, id, onDone)
: retrieves a model by id. Reads the model from model.url
+ /
+ id
.
findOne(name, conditions, onDone)
: retrieves a single model by any condition. Reads the model from model.url
+ ?
+ condition=value
.
find(name, conditions, onDone)
/ : retrieves a model by a set of conditions.
Alternatively, you can use model.fetch()
??
Hydration API
The hydration API allows you to take data loaded via some other channel, and hydrate it.
hydrate(name, data, onDone)
: hydrate an existing JSON blob (with related model hydration).
Hydration fetches all related models and finally calls onDone
with err, result
.
Defining data types for hydration
To define data types for models and validation:
field: { type: Number|Boolean|RegExp|Date|'Person', default: (value) }
When fields have a defined data type, the hydration layer ensure that the values are of the correct type (e.g. ensuring that numbers are numbers, booleans are booleans and RegExps are regexps).
Setting the collection class
By default, collections of Post models are contained inside a Backbone.Collection. However, your can define a different container class if you prefer (e.g. with Post-specific collection methods).
var Post = Backbone.Model.extend({ collection: 'Posts', }); mg.define('Post', Post); var Posts = Backbone.Collection.extend({ // additional property special: true }); mg.define('Posts', Posts);
Validation API
field: { validation: RegExp|function|array of RegExp/function, }
Validation is optional and separate from saving. Built in validators (inspired by mongoose):
required: true
is builtin.- Numbers have min and max validators
- Strings have enum and match validators
For example:
rels: { type: Date }
To validate:
var err = model.validate();
Creating / reading / updating / deleting
How mg
hooks into CRUD:
- Create:
mg
uses the.sync
function to get notified of newly created instances once the backend returns a response that contains an id. These models are added to the cache, and any streaming collections are notified. - Read:
mg
provides the.find
and.hydrate
APIs for reading in and hydrating related models - Update: the normal
Backbone.sync
behavior takes place. Sincemg
ensures that only one instance of a class + id pair exists on the client side, any updates trigger events on the same instance of the model. For properties that are models or collections of models,mg
hooks into the.save
and triggers a save on the related collection or model (one level deep). - Delete:
mg
should also remove the model from all collections and remove it from the cache.
Save API
Saving collection add/remove actions; should be done when the model containing the relation collection is saved.
For properties that are models or collections of models, mg
hooks into the .save
and triggers a save on the related collection or model (one level deep).
Cache API
.store(name, value)
: inserts a JSON structure into the cache.
For example, to store a JSON structure in order to preload a model:
cache.store('test', { id: 7000, name: 'bar' });
.fetch(name, uri, onDone)
: fetches a JSON structure from a remote URL.
.keys(name)
: returns all the cache keys (e.g. model ids).
.get(name, id, onDone)
: fetches a JSON structure by id (either remote or local).
.local(name, id)
: returns true
if a model is locally cached.
If a request is already pending to a particular URL, then the cache will not start a new request. Instead, the
Remapping
module.exports = { // inline define Post: mg.define('Post', Backbone.Model.extend({ sync: mg.sync('Post'), plural: 'posts' })), // easier getter posts: function(conditions, onDone) { return mg.find('Post', conditions, onDone); } };
Streaming collections
Streaming collections make it easy to track all instances of a specific type.
stream(name, conditions, onLoaded)