/index.html
HTML | 1663 lines | 1494 code | 158 blank | 11 comment | 0 complexity | 69ad0e913af9a87a436c298eca91ba96 MD5 | raw file
- <!DOCTYPE HTML>
- <html xmlns="http://www.w3.org/1999/html">
- <head>
- <meta http-equiv="content-type" content="text/html;charset=UTF-8">
- <meta http-equiv="X-UA-Compatible" content="chrome=1">
- <meta name="viewport" content="width=device-width">
- <link rel="icon" href="static/images/favicon.ico">
-
- <link href="http://fonts.googleapis.com/css?family=Arimo" rel="stylesheet" type="text/css">
- <link rel="stylesheet" type="text/css" href="static/css/style.css">
-
- <title>Backbone-relational.js</title>
- <!--[if lt IE 9]>
- <script>
- document.createElement('header');
- document.createElement('nav');
- document.createElement('section');
- document.createElement('article');
- document.createElement('aside');
- document.createElement('footer');
- document.createElement('hgroup');
- </script>
- <![endif]-->
- </head>
- <body>
- <div id="sidebar">
- <a class="toc_title" href="#">
- Backbone-relational.js <span class="version">(0.8.8)</span>
- </a>
- <ul>
- <li class="link_out"><a href="https://github.com/PaulUithol/Backbone-relational">GitHub Repository</a></li>
- </ul>
- <a class="toc_title" href="#introduction">
- Introduction
- </a>
- <a class="toc_title" href="#installation">
- Installation
- </a>
- <a class="toc_title" href="#RelationalModel">
- Backbone.RelationalModel
- </a>
- <ul>
- <li>
- <a href="#RelationalModel-relations">relations</a>
- <ul>
- <li><a href="#relations-key">key</a></li>
- <li><a href="#relations-relatedModel">relatedModel</a></li>
- <li><a href="#relations-type">type</a></li>
- <li><a href="#relations-includeInJSON">includeInJSON</a></li>
- <li><a href="#relations-autoFetch">autoFetch</a></li>
- <li><a href="#relations-collectionType">collectionType</a></li>
- <li><a href="#relations-collectionKey">collectionKey</a></li>
- <li><a href="#relations-collectionOptions">collectionOptions</a></li>
- <li><a href="#relations-createModels">createModels</a></li>
- <li><a href="#relations-keySource">keySource</a></li>
- <li><a href="#relations-keyDestination">keyDestination</a></li>
- <li><a href="#relations-parse">parse</a></li>
- <li><a href="#relations-reverseRelation">reverseRelation</a></li>
- </ul>
- </li>
- </ul>
- <ul>
- <li><a href="#RelationalModel-subModelTypes">subModelTypes</a></li>
- <li><a href="#RelationalModel-subModelTypeAttribute">subModelTypeAttribute</a></li>
- </ul>
- <ul>
- <li><a href="#RelationalModel-getAsync">getAsync</a></li>
- <li><a href="#RelationalModel-getIdsToFetch">getIdsToFetch</a></li>
- <li><a href="#RelationalModel-getRelation">getRelation</a></li>
- <li><a href="#RelationalModel-getRelations">getRelations</a></li>
- <li><a href="#RelationalModel-set">set</a></li>
- <li><a href="#RelationalModel-toJSON">toJSON</a></li>
- </ul>
- <ul>
- <li><a href="#RelationalModel-setup">setup</a></li>
- <li><a href="#RelationalModel-build">build</a></li>
- <li><a href="#RelationalModel-findOrCreate">findOrCreate</a></li>
- <li><a href="#RelationalModel-find">find</a></li>
- <li><a href="#RelationalModel-findModel">findModel</a></li>
- </ul>
- <ul>
- <li><a href="#RelationalModel-events"><strong>Catalog of Events</strong></a></li>
- </ul>
- <a class="toc_title" href="#Relation">
- Backbone.Relation
- </a>
- <ul>
- <li><a href="#Relation-HasOne">HasOne</a></li>
- <li><a href="#Relation-HasMany">HasMany</a></li>
- </ul>
- <a class="toc_title" href="#Store">
- Backbone.Store
- </a>
- <ul>
- <li><a href="#Store-addModelScope">addModelScope</a></li>
- <li><a href="#Store-removeModelScope">removeModelScope</a></li>
- <li><a href="#Store-reset">reset</a></li>
- </ul>
- <a class="toc_title" href="#examples">
- Examples
- </a>
- <a class="toc_title" href="#change-log">
- Change Log
- </a>
- <a class="toc_title" href="#under-the-hood">
- Under the Hood
- </a>
- </div>
- <div class="container">
- <section>
- <h1>
- Backbone-relational.js
- </h1>
- <p>
- When developing any medium to large-scale web application, you often get to the point where
- an action by a user can cause a number of different models to change on the client and the server.
- </p>
- <p>
- You can try to keep updating both sides of a relation manually for every action, and individually call
- <q><a href="http://backbonejs.org/#Model-save">save</a></q> or <q><a href="http://backbonejs.org/#Model-fetch">fetch</a></q>
- on each of the changed models to sync with the server, but that quickly turns into a tedious process and
- results in multiple requests.
- </p>
- <p>
- Using Backbone-relational, we can configure relationships between our models, and sync the model and all of its related models with a single
- call to <q><a href="http://backbonejs.org/#Model-save">save</a></q>, <q><a href="#RelationalModel-getAsync">getAsync</a></q> or <q><a href="http://backbonejs.org/#Model-fetch">fetch()</a></q>
- after setting up a model's <q><a href="#RelationalModel-relations">relations</a></q>.
- </p>
- <p>
- Backbone-relational is hosted on <a href="https://github.com/PaulUithol/Backbone-relational">GitHub</a>,
- and is available under the <a href="https://github.com/PaulUithol/Backbone-relational/blob/master/LICENSE.txt">MIT license</a>.
- </p>
- <p>
- Thanks to <a href="http://progressiveplanning.com">Progressive Planning</a> for allowing me the time to
- work on Backbone-relational!
- </p>
- </section>
- <section id="downloads">
- <h2>
- Downloads & Dependencies
- <span style="padding-left: 7px; font-size:11px; font-weight: normal;" class="interface">
- (Right-click, and use "Save As")
- </span>
- </h2>
- <table>
- <tr>
- <td><a class="punch" href="https://raw.github.com/PaulUithol/Backbone-relational/0.8.8/backbone-relational.js">Latest Release (0.8.8)</a></td>
- <td class="text"><i>~70kb. Full source, uncompressed, lots of comments.</i></td>
- </tr>
- <tr>
- <td><a class="punch" href="https://raw.github.com/PaulUithol/Backbone-relational/master/backbone-relational.js">Development Version</a></td>
- </tr>
- </table>
- <p>
- Backbone-relational depends on <a href="http://backbonejs.org/">Backbone.js</a> <small>(>= 1.0.0)</small>,
- which itself requires <a href="http://underscorejs.org">Underscore.js</a> <small>(> 1.4.4)</small> and
- <a href="http://jquery.com">jQuery</a> <small>(> 1.7.0)</small> or
- <a href="http://zeptojs.com/">Zepto</a>.
- </p>
- </section>
- <section id="introduction">
- <h2>Introduction</h2>
- <p>
- Backbone-relational.js provides one-to-one, one-to-many and many-to-one relations
- between models for Backbone. To use relations, extend <a href="#RelationalModel"><strong>Backbone.RelationalModel</strong></a>
- (instead of a regular <a href="http://backbonejs.org/#Model">Backbone.Model</a>) and define a
- <a href="#RelationalModel-relations"><q>relations</q></a> property, containing an array of option objects.
- Each relation must define (at least) the <a href="#relations-type"><q>type</q></a>, <a href="#relations-key"><q>key</q></a>,
- and <a href="#relations-relatedModel"><q>relatedModel</q></a>. Available relation types are
- <a href="#Relation-HasOne"><q>Backbone.HasOne</q></a> and <a href="#Relation-HasMany"><q>Backbone.HasMany</q></a>.
- </p>
- <p>
- Backbone-relational's main features include:
- </p>
- <ul>
- <li>
- Bidirectional relations, which notify related models of changes through events.
- </li>
- <li>
- Control how relations are serialized using the <a href="#relations-includeInJSON"><q>includeInJSON</q></a> option.
- </li>
- <li>
- Automatically convert nested objects in a model's attributes into model instances (using the
- <a href="#relations-createModels"><q>createModels</q></a> option, which is <q>true</q> by default).
- </li>
- <li>
- Lazily retrieve a set of related models through the <a href="#RelationalModel-getAsync"><q>getAsync</q></a>
- method.
- </li>
- <li>
- Determine the type of HasMany collections with <a href="#relations-collectionType"><q>collectionType</q></a>.
- </li>
- </ul>
- <p>
- You can also bind new events to a <strong>Backbone.RelationalModel</strong> for an:
- </p>
- <ul>
- <li>
- <strong>addition</strong> to a HasMany relation with <a href="#RelationalModel-events">add:<key></a>.
- </li>
- <li>
- <strong>removal</strong> from a HasMany relation with <a href="#RelationalModel-events">remove:<key></a>.
- </li>
- <li>
- <strong>reset</strong> of a HasMany relation with <a href="#RelationalModel-events">reset:<key></a>.
- </li>
- <li>
- <strong>changes</strong> to the contents of a HasMany or HasOne relations with <a href="#RelationalModel-events">change:
- <key></a>.
- </li>
- </ul>
- </section>
- <section id="installation">
- <h2>Installation</h2>
- <p>
- Backbone-relational depends on <a href="http://backbonejs.org/">Backbone.js</a> (and thus on
- <a href="http://underscorejs.org">Underscore.js</a>). Include Backbone-relational right after Backbone
- and Underscore:
- </p>
- <pre class="language-markup"><code class="language-markup"><!--
- --><script type="text/javascript" src="./js/underscore.js"></script>
- <!-- --><script type="text/javascript" src="./js/backbone.js"></script>
- <!-- --><script type="text/javascript" src="./js/backbone-relational.js"></script>
- </code></pre>
- <p class="notice">
- Note for <strong>CoffeeScript</strong> users: due to the way the <q>extends</q> keyword is implemented in
- CoffeeScript, you may have to make an extra call to <a href="#RelationalModel-setup"><q>setup</q></a> for
- your models. See the <a href="#RelationalModel-setup"><q>setup</q></a> documentation for details.
- </p>
- </section>
- <section id="RelationalModel">
- <h2>
- Backbone.RelationalModel
- </h2>
- <p>
- When using Backbone-relational, each model defining (or receiving) <q>relations</q> must extend
- <strong>Backbone.RelationalModel</strong> in order to function. <strong>Backbone.RelationalModel</strong>
- introduces a couple of new methods, events and properties. It's important to know which are properties,
- which are methods of an instance, and which operate on the type itself.
- These three subcategories are detailed below.
- </p>
- <p>
- <strong>Properties</strong> can be defined when extending Backbone.RelationalModel, or a subclass thereof.
- </p>
- <ul class="small">
- <li><a href="#RelationalModel-relations">relations</a></li>
- <li><a href="#RelationalModel-subModelTypes">subModelTypes</a></li>
- <li><a href="#RelationalModel-subModelTypeAttribute">subModelTypeAttribute</a></li>
- </ul>
- <p>
- <strong>Instance methods</strong> operate on an instance of a type.
- </p>
- <ul class="small">
- <li><a href="#RelationalModel-getAsync">getAsync</a></li>
- <li><a href="#RelationalModel-getIdsToFetch">getIdsToFetch</a></li>
- <li><a href="#RelationalModel-getRelation">getRelation</a></li>
- <li><a href="#RelationalModel-getRelations">getRelations</a></li>
- <li><a href="#RelationalModel-set">set</a></li>
- <li><a href="#RelationalModel-toJSON">toJSON</a></li>
- </ul>
- <p>
- <strong>Static methods</strong> operate on the type itself, as opposed to operating on model instances.
- </p>
- <ul class="small">
- <li><a href="#RelationalModel-setup">setup</a></li>
- <li><a href="#RelationalModel-build">build</a></li>
- <li><a href="#RelationalModel-findOrCreate">findOrCreate</a></li>
- <li><a href="#RelationalModel-find">find</a></li>
- <li><a href="#RelationalModel-findModel">findModel</a></li>
- </ul>
- <h3 id="RelationalModel-properties">
- Properties
- </h3>
- <h4 class="code" id="RelationalModel-relations">
- relations<code>relation[]</code>
- </h4>
- <p>
- A <strong>Backbone.RelationalModel</strong> may contain an array of relation definitions. Each relation supports a number of
- options, of which <a href="#relations-relatedModel"><q>relatedModel</q></a>, <a href="#relations-key"><q>key</q></a> and
- <a href="#relations-type"><q>type</q></a> are mandatory. A relation could look like the following:
- </p>
- <pre class="language-javascript"><code id="example-zoo" class="language-javascript runnable"><!--
- -->Zoo = Backbone.RelationalModel.extend({
- relations: [{
- type: Backbone.HasMany,
- key: 'animals',
- relatedModel: 'Animal',
- collectionType: 'AnimalCollection',
- reverseRelation: {
- key: 'livesIn',
- includeInJSON: 'id'
- // 'relatedModel' is automatically set to 'Zoo'; the 'relationType' to 'HasOne'.
- }
- }]
- });
- Animal = Backbone.RelationalModel.extend({
- urlRoot: '/animal/'
- });
- AnimalCollection = Backbone.Collection.extend({
- model: Animal
- });
- // We've now created a fully managed relation. When you add or remove model from `zoo.animals`,
- // or update `animal.livesIn`, the other side of the relation will automatically be updated.
- var artis = new Zoo( { name: 'Artis' } );
- var lion = new Animal( { species: 'Lion', livesIn: artis } );
- // `animals` in `artis` now contains `lion`
- alert( artis.get( 'animals' ).pluck( 'species' ) );
- </code></pre>
- <pre class="language-javascript nomargin"><code class="language-javascript runnable" data-setup="#example-zoo"><!--
- -->var amersfoort = new Zoo( { name: 'Dierenpark Amersfoort', animals: [ lion ] } );
- // `lion` now livesIn `amersfoort`, and `animals` in `artis` no longer contains `lion`
- alert( lion.get( 'livesIn' ).get( 'name' ) + ', ' + artis.get( 'animals' ).length );
- </code></pre>
- <section id="relations-key">
- <h4 class="code">
- key<code>relation.key</code>
- </h4>
- <p>
- Required. A string that references an attribute name on <a href="#relations-relatedModel"><q>relatedModel</q></a>.
- </p>
- <h4 class="code" id="relations-relatedModel">
- relatedModel<code>relation.relatedModel</code>
- </h4>
- <p>
- Required. A string that can be resolved to an object on the global scope, or a reference to a
- <strong>Backbone.RelationalModel</strong>. Also see <a href="#Store-addModelScope"><q>addModelScope</q></a>.
- </p>
- </section>
- <section id="relations-type">
- <h4 class="code">
- type<code>relation.type</code>
- </h4>
- <p>
- Required. A string that references a <a href="#Relation"><q>Backbone.Relation</q></a> type by name ("HasOne" or "HasMany"),
- or a direct reference to a relation type.
- </p>
- <p>
- You can model a one-to-one or a many-to-one relationship by declaring <q>type</q> as the string "HasOne", or by
- directly referencing <a href="#Relation-HasOne"><q>Backbone.HasOne</q></a>. A HasOne relation contains a single
- <strong>Backbone.RelationalModel</strong>. The default <q>reverseRelation.type</q> for a "HasOne" relation is
- "HasMany". This can be set to "HasOne" instead, to create a one-to-one relation.
- </p>
- <p>
- You can model a one-to-many relationship by declaring <q>type</q> as the string "HasMany", or by directly
- referencing <a href="#Relation-HasMany"><q>Backbone.HasMany</q></a>. A HasMany relation contains a Backbone.Collection,
- containing zero or more <strong>Backbone.RelationalModel</strong>s. The default <q>reverseRelation.type</q>
- for a HasMany relation is HasOne; this is the only option here, since many-to-many is not supported directly.
- </p>
- <p>
- It is possible model a many-to-many relationship using two <a href="#Relation-HasMany"><q>Backbone.HasMany</q></a>
- relations, with a link model in between:
- </p>
- </section>
- <pre class="language-javascript"><code class="language-javascript" id="example-job"><!--
- -->Person = Backbone.RelationalModel.extend({
- relations: [{
- type: 'HasMany',
- key: 'jobs',
- relatedModel: 'Job',
- reverseRelation: {
- key: 'person'
- }
- }]
- });
- // A link object between 'Person' and 'Company'
- Job = Backbone.RelationalModel.extend({
- defaults: {
- 'startDate': null,
- 'endDate': null
- }
- })
- Company = Backbone.RelationalModel.extend({
- relations: [{
- type: 'HasMany',
- key: 'employees',
- relatedModel: 'Job',
- reverseRelation: {
- key: 'company'
- }
- }]
- });
- </code></pre>
- <section id="relations-includeInJSON">
- <h4 class="code">
- includeInJSON<code>relation.includeInJSON</code>
- </h4>
- <p>
- A boolean, a string referencing one of the model's attributes, or an array of strings referencing model
- attributes. Default: <q>true</q>.
- </p>
- <p>
- Determines how the contents of a relation will be serialized following a call to the
- <a href="#RelationalModel-toJSON"><q>toJSON</q></a> method. If you specify a:
- </p>
- <ul>
- <li>
- <strong>Boolean</strong>: a value of true serializes the full set of attributes on the related model(s).
- Set to false to exclude the relation completely.
- </li>
- <li>
- <strong>String</strong>: include a single attribute from the related model(s). For example, 'name', or
- <q>Backbone.Model.prototype.idAttribute</q> to include ids.
- </li>
- <li>
- <strong>String[]</strong>: includes the specified attributes from the related model(s).
- </li>
- </ul>
- <p>
- Specifying <q>true</q> will cascade, meaning the relations of nested model will get serialized as well,
- until either a different value is found for <q>includeInJSON</q> or we encounter a model that has already
- been serialized.
- </p>
- </section>
- <section id="relations-autoFetch">
- <h4 class="code">
- autoFetch<code>relation.autoFetch</code>
- </h4>
- <p>
- A boolean or an object. Default: <q>false</q>.
- </p>
- <p>
- If this property is set to <q>true</q>, when a model is instantiated the related model is
- automatically fetched using <a href="#RelationalModel-getAsync"><q>getAsync</q></a>. The
- value of the property can also be an object. In that case the object is passed to
- <a href="#RelationalModel-getAsync"><q>getAsync</q></a> as the options parameter.
- </p>
- <p>
- Note that <q>autoFetch</q> operates independently from other `fetch` operations,
- including those that may have fetched the current model.
- </p>
- </section>
- <pre class="language-javascript"><code class="language-javascript"><!--
- -->var Shop = Backbone.RelationalModel.extend({
- relations: [
- {
- type: Backbone.HasMany,
- key: 'customers',
- relatedModel: 'Customer',
- autoFetch: true
- },
- {
- type: Backbone.HasOne,
- key: 'address',
- relatedModel: 'Address',
- autoFetch: {
- success: function( model, response ) {
- //...
- },
- error: function( model, response ) {
- //...
- }
- }
- }
- ]
- });
- </code></pre>
- <section id="relations-collectionType">
- <h4 class="code">
- collectionType<code>relation.collectionType</code>
- </h4>
- <p>
- A string that can be resolved to an object type on the global scope, or a reference to a
- <strong>Backbone.Collection</strong> type.
- </p>
- <p>
- Determine the type of collections used for a HasMany relation. If you define a
- url(models<Backbone.Model[]>) function on the specified collection, this enables
- <a href="#RelationalModel-getAsync"><q>getAsync</q></a> to fetch all missing models in one request,
- instead of firing a separate request for each.
- </p>
- <h4 class="code" id="relations-collectionKey">
- collectionKey<code>relation.collectionKey</code>
- </h4>
- <p>
- A string or a boolean. Default: <q>true</q>.
- </p>
- <p>
- Used to create a back reference from the <strong>Backbone.Collection</strong> used for a HasMany relation to the model on
- the other side of this relation. By default, the relation's key attribute will be used to create a reference to
- the RelationalModel instance from the generated collection. If you set <q>collectionKey</q> to a string,
- it will use that string as the reference to the RelationalModel, rather than the
- relation's key attribute. If you don't want this behavior at all, set <q>collectionKey</q> to <q>false</q>
- (or any falsy value) and this reference will not be created.
- </p>
- </section>
- <section id="relations-collectionOptions">
- <h4 class="code">
- collectionOptions<code>relation.collectionOptions</code>
- </h4>
- <p>
- An options hash, or a function that accepts an instance of a <strong>Backbone.RelationalModel</strong> and returns an options
- hash.
- </p>
- <p>
- Used to provide options for the initialization of the collection in the 'Many'-end of a HasMany relation. Can be
- an options hash or a function that should take the instance in the 'One'-end of the 'HasMany' relation and return
- an options hash.
- </p>
- </section>
- <section id="relations-createModels">
- <h4 class="code">
- createModels<code>relation.createModels</code>
- </h4>
- <p>
- A boolean. Default: <q>true</q>.
- </p>
- <p>
- Specifies whether models will be created from nested objects or not.
- </p>
- </section>
- <section id="relations-keySource">
- <h4 class="code">
- keySource<code>relation.keySource</code>
- </h4>
- <p>
- A string that references an attribute to deserialize data for <a href="#relations-relatedModel"><q>relatedModel</q></a> from.
- </p>
- <p>
- Used to override key when determining what data to use when (de)serializing a relation, since the data backing
- your relations may use different naming conventions. For example, a Rails backend may provide the keys suffixed
- with <q>_id</q> or <q>_ids</q>. The behavior for <q>keySource</q> corresponds to the following rules:
- </p>
- <p>
- When a relation is instantiated, the contents of the <q>keySource</q> are used as its initial data. The
- application uses the regular key attribute to interface with the relation and the models in it; the
- <q>keySource</q> is not available as an attribute for the model. So you may be provided with data containing
- <q>animal_ids</q>, while you want to access this relation as <q>zoo.get('animals')</q>.
- </p>
- <p class="warning">
- Note that setting <q>keySource</q> will set <a href="#relations-keyDestination"><q>keyDestination</q></a>
- to the same value, if it isn't specified itself.
- This means that when saving zoo, the animals attribute will be serialized back into the <q>animal_ids</q> key.
- </p>
- <p class="warning">
- WARNING: when using a keySource, you should not use that attribute name for other purposes.
- </p>
- </section>
- <section id="relations-keyDestination">
- <h4 class="code">
- keyDestination<code>relation.keyDestination</code>
- </h4>
- <p>
- A string that references an attribute to serialize <a href="#relations-relatedModel"><q>relatedModel</q></a> into.
- </p>
- <p>
- Used to override key (and <a href="#relations-keySource"><q>keySource</q></a>) when determining what attribute to be
- written into when serializing a relation, since the server backing your relations may use different naming
- conventions. For example, a Rails backend may expect the keys to be suffixed with _attributes for nested
- attributes.
- </p>
- <p>
- When calling <a href="#RelationalModel-toJSON"><q>toJSON</q></a> on a model (either via
- <strong>Backbone.Sync</strong>, or directly), the data in the key attribute is transformed and assigned to the
- <q>keyDestination</q>.
- </p>
- <p>
- So you may want a relation to be serialized into the animals_attributes key, while you want to access this
- relation as <q>zoo.get( 'animals' );</q>.
- </p>
- <p class="warning">
- WARNING: when using a <q>keyDestination</q>, you should not use that attribute name for other purposes.
- </p>
- </section>
- <pre class="language-javascript"><code class="language-javascript runnable" data-setup="#example-zoo"><!--
- -->var FarmAnimal = Animal.extend();
- // This `Farm` is confused, like legacy stuff can be. It wants its data back on a completely
- // different key than it supplies it on. We want to use a different one in our app as well.
- var Farm = Backbone.RelationalModel.extend({
- relations: [{
- type: Backbone.HasMany,
- key: 'animals',
- keySource: 'livestock',
- keyDestination: 'pets',
- relatedModel: FarmAnimal,
- reverseRelation: {
- key: 'farm',
- includeInJSON: 'name'
- }
- }]
- });
- // Create a `Farm`; parse `species`, add to `animals`, output goes to `pets`.
- var farm = new Farm( { name: 'Old MacDonald', livestock: [ { species: 'Sheep' } ] } );
- farm.get( 'animals' ).add( { species: 'Cow' } );
- alert( JSON.stringify( farm.toJSON(), null, 4 ) );
- </code></pre>
- <section id="relations-parse">
- <h4 class="code">
- parse<code>relation.parse</code>
- </h4>
- <p>
- A boolean. Default: <q>false</q>.
- </p>
- <p>
- If you have a relation where the models should be parsed when data is being set, specify `parse: true`.
- </p>
- </section>
- <section id="relations-reverseRelation">
- <h4 class="code">
- reverseRelation<code>relation.reverseRelation</code>
- </h4>
- <p>
- An object specifying the relation pointing back to this model from <a href="#relations-relatedModel"><q>relatedModel</q></a>.
- </p>
- <p>
- If the relation should be bidirectional, specify the details for the reverse relation here. It's only mandatory
- to supply a <a href="#relations-key"><q>key</q></a>; <a href="#relations-relatedModel"><q>relatedModel</q></a> is automatically
- set. The default type for a <q>reverseRelation</q> is HasMany for a
- HasOne relation (which can be overridden to HasOne in order to create a one-to-one relation), and HasOne for a
- HasMany relation. In this case, you cannot create a <q>reverseRelation</q> with
- type HasMany as well; please see Many-to-many relations on how to model these type of relations.
- </p>
- <p class="warning">
- Note that if you define a relation (plus a reverseRelation) on a model, but don't actually create an instance
- of that model, it is possible <q>initializeRelations</q> will never get called, and the reverseRelation
- will not be initialized. This can happen when <q>extend</q> has been overridden, or redefined as in CoffeeScript.
- See <a href="#RelationalModel-setup">setup</a>.
- </p>
- </section>
- <section id="RelationalModel-subModelTypes">
- <h4 class="code">
- subModelTypes<code>relationalModel.subModelTypes(attributes<object>, [options<object>])</code>
- </h4>
- <p>
- An object. Default: <q>{}</q>.
- </p>
- <p>
- A mapping that defines what submodels exist for the model (the superModel) on which
- <q>subModelTypes</q> is defined. The keys are used to match the
- <a href="#RelationalModel-subModelTypeAttribute"><q>subModelTypeAttribute</q></a> when deserializing, and the values
- determine what type of submodel should be created for a key. When building model instances from data, we need to
- determine what kind of object we're dealing with in order to create instances of the right subModel type. This
- is done by finding the model for which the key is equal to the value of the
- <a href="#RelationalModel-subModelTypeAttribute"><q>subModelTypeAttribute</q></a> attribute on the passed in data.
- </p>
- <p>
- Each subModel is considered to be a proper submodel of its superclass (the model type you're extending), with a
- shared id pool. This means that when looking for an object of the supermodel's type, objects of a submodel's type
- can be returned as well, as long as the id matches. In effect, any relations pointing to the supermodel will look
- for instances of its submodels as well.
- </p>
- <pre class="language-javascript"><code class="language-javascript runnable" data-setup="#example-zoo"><!--
- -->Mammal = Animal.extend({
- subModelTypes: {
- 'primate': 'Primate',
- 'carnivore': 'Carnivore'
- }
- });
- Primate = Mammal.extend();
- Carnivore = Mammal.extend();
- MammalCollection = AnimalCollection.extend({
- model: Mammal
- });
- // Create a collection that contains a 'Primate' and a 'Carnivore'.
- var mammals = new MammalCollection([
- { id: 3, species: 'chimp', type: 'primate' },
- { id: 5, species: 'panther', type: 'carnivore' }
- ]);
- var chimp = mammals.get( 3 );
- alert( 'chimp is an animal? ' + ( chimp instanceof Animal ) + '\n' +
- 'chimp is a carnivore? ' + ( chimp instanceof Carnivore ) + '\n' +
- 'chimp is a primate? ' + ( chimp instanceof Primate ) );
- </code></pre>
- <p>
- Suppose that we have an Mammal model and a Primate model extending Mammal. If we have a Primate object with id 3,
- this object will be returned when we have a relation pointing to a Mammal with id 3, as Primate is regarded a
- specific kind of Mammal; it's just a Mammal with possibly some primate-specific properties or methods.
- </p>
- <p class="warning">
- Note that this means that there cannot be any overlap in ids between instances of Mammal and Primate, as the
- Primate with id 3 will be the Mammal with id 3.
- </p>
- </section>
- <section id="RelationalModel-subModelTypeAttribute">
- <h4 class="code">
- subModelTypeAttribute<code>relationalModel.subModelTypeAttribute</code>
- </h4>
- <p>
- A string. Default: <q>type</q>.
- </p>
- <p>
- The <q>subModelTypeAttribute</q> is a references an attribute on the data
- used to instantiate <a href="#relations-relatedModel"><q>relatedModel</q></a>. The attribute that will be checked to
- determine the type of model that should be built when a raw object of attributes is set as the related value,
- and if the <a href="#relations-relatedModel"><q>relatedModel</q></a> has one or more submodels.
- </p>
- </section>
- <h3 id="RelationalModel-instance-methods">
- Instance methods
- </h3>
- <section id="RelationalModel-getAsync">
- <h4 class="code">
- getAsync<code>relationalModel.getAsync(attr<string>, [options<object>], [refresh<boolean>])</code>
- </h4>
- <p>
- Returns: <q>jQuery.Deferred</q> A <a href="http://api.jquery.com/category/deferred-object/">jQuery promise</a>
- </p>
- <p>
- Get an attribute's value asynchronously. If available, the local value will be returned.
- If <q>attr</q> is a relation and one or more of its models are not available, they will be fetched.
- </p>
- <p>
- This can be used specifically for lazy-loading scenarios.
- Only models are referenced in the attributes but have not been found/created yet are fetched.
- Setting <q>options.refresh</q> to true will fetch all model(s) from the server.
- In that case, any model that already exists will be updated with the retrieved data.
- The <q>options</q> object specifies options to be passed to <a href="http://backbonejs.org/#Sync">Backbone.Sync</a>.
- </p>
- <p>
- By default, a separate request will be fired for each model that is to be fetched from the server (if `key` references a collection).
- However, if your server/API supports it, you can fetch the set of models in one request by specifying a
- collectionType for the relation you call getAsync on. The <a href="#relations-collectionType"><q>collectionType</q></a>
- should have an overridden <a href="http://backbonejs.org/#Collection-url"><q>url</q></a>
- method that allows it to construct a url for an array of models. See <a href="#example-person">this example</a>
- or <a href="https://github.com/PaulUithol/backbone-tastypie">Backbone-tastypie</a> for an example.
- </p>
- </section>
- <section id="RelationalModel-getIdsToFetch">
- <h4 class="code">
- getRelations<code>relationModel.getIdsToFetch(attr<string|Backbone.Relation>, )</code>
- </h4>
- <p>
- Returns: <q>Array</q> A list of the ids that will be fetched when calling <q>getAsync</q>.
- </p>
- </section>
- <section id="RelationalModel-getRelation">
- <h4 class="code">
- getRelation<code>relationModel.getRelation(attr<string>)</code>
- </h4>
- <p>
- Returns: <q>Backbone.Relation</q> A single initialized relation on the model.
- </p>
- </section>
- <section id="RelationalModel-getRelations">
- <h4 class="code">
- getRelations<code>relationModel.getRelations()</code>
- </h4>
- <p>
- Returns: <q>Backbone.Relation[]</q> The set of initialized relations on the model.
- </p>
- </section>
- <section id="RelationalModel-set">
- <h4 class="code">
- set<code>set(key<string>, value, [options<object>]) <strong>or</strong> set(attributes<object>, [options<object>])</code>
- </h4>
- <p>
- Returns: <q>Backbone.RelationalModel</q> The model instance.
- </p>
- <p>
- The <q>set</q> method is overridden so that setting a value on an "relational" attribute will update that relation.
- This is especially important to keep in mind for <q>HasMany</q> relations (which are backed by a <q>Backbone.Collection</q>).
- For these, calling <q>set</q> can be thought of as being equivalent to calling <q>update</q> on the collection itself,
- including how the options are handled.
- </p>
- <p>
- Additional <q>options</q> for a <q>HasMany</q> relation:
- </p>
- <dl>
- <dt><q>add</q></dt>
- <dd>Default: <q>true</q>. If true, models specified in the arguments but not yet present in the relation will be added to the relation.</dd>
- <dt><q>merge</q></dt>
- <dd>Default: <q>true</q>. If true, existing models will be updated with the given attributes.</dd>
- <dt><q>remove</q></dt>
- <dd>Default: <q>true</q>. If true, models present in the relation but not specified in the arguments will be removed.</dd>
- </dl>
- </section>
- <section id="RelationalModel-toJSON">
- <h4 class="code">
- toJSON<code>relationModel.toJSON(name<string>)</code>
- </h4>
- <p>
- Returns: <q>Object</q> The JSON representation of the model.
- See <a href="http://backbonejs.org/#Model-toJSON">Backbone.Model.toJSON</a>.
- </p>
- <p>
- The regular <q>toJSON</q> function has been overridden and modified to serialize (nested) relations
- according to their <a href="#relations-includeInJSON"><q>includeInJSON</q></a>, <a href="#relations-keySource"><q>keySource</q></a>,
- and <a href="#relations-keyDestination"><q>keyDestination</q></a> options.
- </p>
- </section>
- <h3 id="RelationalModel-static-methods">
- Static methods
- </h3>
- <section id="RelationalModel-setup">
- <h4 class="code">
- setup<code>relationModel.setup()</code>
- </h4>
- <p>
- Returns: <q>Backbone.RelationalModel.constuctor</q> The type.
- </p>
- <p>
- Initialize the relations and submodels for the model type. Normally, this happens automatically, but it doesn't if
- you're using CoffeeScript and using the syntax <q>class MyModel extends Backbone.RelationalModel</q> instead of
- the JavaScript equivalent of <q>MyModel = Backbone.RelationalModel.extend()</q>.
- </p>
- <p>
- This has advantages in CoffeeScript, but it also means that <q>Backbone.Model.extend</q> will not get called.
- Instead, CoffeeScript generates piece of code that would normally achieve the same. However, <q>extend</q> is also
- the method that Backbone-relational overrides to set up relations as you're defining your <q>Backbone.RelationalModel</q> subclass.
- </p>
- <p>
- In this case, you should call <q>setup</q> manually after defining your subclass CoffeeScript-style. For example:
- </p>
- <p class="warning">
- Note: this is a static method. It operates on the model type itself, not on an instance of it.
- </p>
- </section>
- <pre class="language-javascript"><code class="language-javascript"><!--
- -->class Animal extends Backbone.RelationalModel
- urlRoot: "/animal"
- class Mammal extends Animal
- subModelTypes:
- "primate": "Primate"
- "carnivore": "Carnivore"
- relations: [
- # More relations
- ]
- Mammal.setup()
- class Primate extends Mammal
- class Carnivore extends Mammal
- chimp = Mammal.build( { id: 3, species: "chimp", type: "primate" } )
- </code></pre>
- <section id="RelationalModel-build">
- <h4 class="code">
- build<code>relationalModel.build(attributes<object>, [options<object>])</code>
- </h4>
- <p>
- Returns: <q>Backbone.RelationalModel</q> A model instance.
- </p>
- <p>
- Create an instance of a model, taking into account what submodels have been defined.
- </p>
- <p class="warning">
- Note: this is a static method. It operates on the model type itself, not on an instance.
- </p>
- </section>
- <section id="RelationalModel-findOrCreate">
- <h4 class="code">
- findOrCreate
- <code>relationalModel.findOrCreate(attributes<string|number|object>, [options<object>])</code>
- </h4>
- <p>
- Returns: <q>Backbone.RelationalModel</q> A model instance.
- </p>
- <p>
- Search for a model instance in the <a href="#Store">Backbone.Relational.store</a>, and return the model if found.
- A new model will be created if no model is found, <q>attributes</q> is an object, and <q>options.create</q> is <q>true</q>.
- </p>
- <p>
- Accepted <q>options</q>:
- </p>
- <dl>
- <dt><q>create</q></dt>
- <dd>Default: <q>true</q>. If true, a new model will be created if an instance matching <q>attributes</q> isn't found in the store.</dd>
- <dt><q>merge</q></dt>
- <dd>Default: <q>true</q>. If true, a found model will be updated with <q>attributes</q> (if <q>attributes</q> is an <q>object</q>).</dd>
- <dt><q>parse</q></dt>
- <dd>Default: <q>false</q>. If true, <q>attributes</q> will be parsed first. Please note this will cause <q>Model.parse</q> to be called
- as a function (<q>this</q> will not point to a model), instead of as a method.</dd>
- </dl>
- <p class="warning">
- Note: this is a static method. It operates on the model type itself, not on an instance of it.
- </p>
- </section>
- <section id="RelationalModel-find">
- <h4 class="code">
- find
- <code>relationalModel.find(attributes<string|number|object>, [options<object>])</code>
- </h4>
- <p>
- Returns: <q>Backbone.RelationalModel</q> A model instance.
- </p>
- <p>
- A shortcut for <a href="#RelationalModel-findOrCreate"><q>findOrCreate</q></a> that uses <q>create: false</q>.
- Accepts the same options as <q>findOrCreate</q> (except for <q>create</q>).
- </p>
- <p class="warning">
- Note: this is a static method. It operates on the model type itself, not on an instance of it.
- </p>
- </section>
- <section id="RelationalModel-findModel">
- <h4 class="code">
- findModel
- <code>relationalModel.findModel(attributes<string|number|object>)</code>
- </h4>
- <p>
- Returns: <q>Backbone.RelationalModel</q> A model instance.
- </p>
- <p>
- A hook to override the matching when updating (or creating) a model.
- The default implementation is to look up the model by id in the store:
- <q>return Backbone.Relational.store.find( this, attributes );</q>
- </p>
- <p>
- Custom behavior is useful in cases where (a collection of) nested data gets saved to the server.
- Consider saving the following model:
- </p>
- <pre class="language-javascript"><code class="language-javascript runnable" data-setup="#example-zoo"><!--
- -->var zoo = new Zoo( { id: 1, name: 'Artis', animals: [
- { species: 'Giraffe' },
- { species: 'Camel' }
- ] } );
- alert( JSON.stringify( zoo.toJSON(), null, 4 ) );
- </code></pre>
- <p>
- Normally, whatever you use as server-side logic will respond by creating two animals, and assigning them
- an id. The response will be used by Backbone-relational to update existing models.
- However, updating a model starts by looking up the local model with the same id; and in this case,
- Backbone-relational does not know which local models corresponds to which created animal with an id.
- A simple fix in this case would be to add a fallback option for the matching by using the animal's
- species. Do note you'd want to use a more robust method usually, such as using a new model's <q>cid</q>.
- </p>
- <pre class="language-javascript"><code class="language-javascript runnable" data-setup="#example-zoo"><!--
- -->Zoo.findModel = function( attributes ) {
- // Try to find an instance of 'this' model type in the store
- var model = Backbone.Relational.store.find( this, attributes );
- if ( !model && _.isObject( attributes ) ) {
- var coll = Backbone.Relational.store.getCollection( this );
- model = coll.find( function( m ) {
- return m.species === attributes.species;
- });
- }
- return model;
- };
- </code></pre>
- <p class="warning">
- Note: this is a static method. It operates on the model type itself, not on an instance of it.
- </p>
- </section>
- <section id="RelationalModel-events">
- <h4>
- Catalog of Events
- </h4>
- <p>
- Backbone-relational makes a couple of additional events available to you, on top of the events already found in Backbone.
- </p>
- <ul class="small">
- <li>
- An <strong>"add"</strong> event is triggered on addition to a HasMany relation. Bind to:<br/>
- <q>add:<key></q> →<code>function(addedModel<Backbone.Model>, related<Backbone.Collection>)</code>
- </li>
- <li>
- A <strong>"remove"</strong> event is triggered on removal from a HasMany relation. Bind to:<br/>
- <q>remove:<key></q> →<code>function(removedModel<Backbone.Model>, related<Backbone.Collection>)</code>
- </li>
- <li>
- A <strong>"change"</strong> event is triggered on changes to the contents of both HasOne and HasMany relations. Bind to:<br/>
- <q>change:<key></q> →<code>function(model<Backbone.Model>, related<Backbone.Model|Backbone.Collection>)</code>
- </li>
- </ul>
- </section>
- </section>
-
- <section id="Relation">
- <h2 >Backbone.Relation</h2>
- <p>
- Each <a href="#RelationalModel-relations">relation</a> definition on a model is used to create in instance of a <q>Backbone.Relation</q>; either
- a <q>Backbone.HasOne</q> or a <q>Backbone.HasMany</q>.
- </p>
- <h4 class="code" id="Relation-HasOne">
- Backbone.HasOne
- </h4>
- <p>
- Defines a <strong>HasOne</strong> relation. When defining a <a href="#relations-reverseRelation">reverseRelation</a>, the default type
- will be <strong>HasMany</strong>. However, this can also be set to <strong>HasOne</strong> to define a one-to-one relation.
- </p>
- <h4 class="code" id="Relation-HasMany">
- Backbone.HasMany
- </h4>
- <p>
- Defines a <strong>HasMany</strong> relation. When defining a <a href="#relations-reverseRelation">reverseRelation</a>, the type
- will be <strong>HasOne</strong>.
- </p>
- </section>
- <section id="Store">
- <h2 >Backbone.Store</h2>
- <p>
- <strong>Backbone.Store</strong> is a global model cache. Per application, one instance is created (much like <q>Backbone.History</q>),
- which is accessible as <q>Backbone.Relational.store</q>.
- </p>
- <h4 class="code" id="Store-addModelScope">
- addModelScope<code>Backbone.Relational.store.addModelScope(scope<object>)</code>
- </h4>
- <p>
- Add a namespace on which models and collections are defined. This is especially useful when working in an
- environment without a shared global scope (like <q>window</q> is in a browser), where you'll need to tell
- the <q>store</q> where your models are defined, so it can resolve them to create and maintain relations.
- </p>
- <h4 class="code" id="Store-removeModelScope">
- removeModelScope<code>Backbone.Relational.store.removeModelScope()</code>
- </h4>
- <p>
- Remove a scope. This allows you to remove a scope you added previously, or to remove the default 'global'
- scope (<q>window</q> in the browser) scope to prevent Backbone-relational from resolving objects on it.
- </p>
- <h4 class="code" id="Store-reset">
- reset<code>Backbone.Relational.store.reset()</code>
- </h4>
- <p>
- Reset the <q>store</q> to its original state. This will disable relations for all models created up to this point,
- remove added model scopes, and removed all internal store collections.
- </p>
- <h4 class="code" id="Store-unregister">
- unregister<code>Backbone.Relational.store.unregister(type<Backbone.RelationalModel|Backbone.RelationalModel.constructor|Backbone.Collection>)</code>
- </h4>
- <p>
- Unregister a single model or a collection. Unregistering a model will remove a model from any relations it's involved in.
- Internally, unregister is called when a model has been destroyed. It can also be called explicitly to on models
- you don't want Backbone-relational to consider for relations anymore, for example to free up models used as (temporary) search results.
- </p>
- </section>
- <section id="examples">
- <h2 >Examples</h2>
- <p>
- <a href="http://antoviaque.org/docs/tutorials/backbone-relational-tutorial/">A tutorial by antoviaque</a>,
- and the <a href="https://github.com/antoviaque/backbone-relational-tutorial">accompanying git repository</a>.
- </p>
- <p>
- A basic working example to get you started:
- </p>
-
- <pre class="language-javascript"><code id="example-person-run1" class="language-javascript runnable" data-setup="#example-person"><!--
- -->var paul = new Person({
- id: 'person-1',
- name: 'Paul',
- user: { id: 'user-1', login: 'dude', email: 'me@gmail.com' }
- });
- // A User object is automatically created from the JSON; so 'login' returns 'dude'.
- paul.get('user').get('login');
- var ourHouse = new House({
- id: 'house-1',
- location: 'in the middle of the street',
- occupants: ['person-1', 'person-2', 'person-5']
- });
- // 'ourHouse.occupants' is turned into a Backbone.Collection of Persons.
- // The first person in 'ourHouse.occupants' will point to 'paul'.
- ourHouse.get('occupants').at(0); // === paul
- // If a collection is created from a HasMany relation, it contains a reference
- // back to the originator of the relation
- ourHouse.get('occupants').livesIn; // === ourHouse
- // The `occupants` relation on 'House' has been defined as a HasMany, with a reverse relation
- // to `livesIn` on 'Person'. So, 'paul.livesIn' will automatically point back to 'ourHouse'.
- paul.get('livesIn'); // === ourHouse
- // You can control which relations get serialized to JSON, using the 'includeInJSON'
- // property on a Relation. Also, each object will only get serialized once to prevent loops.
- alert( JSON.stringify( paul.get('user').toJSON(), null, '\t' ) );
- </code></pre>
- <pre class="language-javascript nomargin"><code id="example-person-run2" class="language-javascript runnable" data-setup="#example-person-run1"><!--
- -->// Load occupants 'person-2' and 'person-5', which don't exist yet, from the server
- ourHouse.getAsync( 'occupants' );
- // Use the `add` and `remove` events to listen for additions/removals on a HasMany relation.
- // Here, we listen for changes to `ourHouse.occupants`.
- ourHouse
- .on( 'add:occupants', function( model, coll ) {
- console.log( 'add %o', model );
- // Do something. Create a View?
- })
- .on( 'remove:occupants', function( model, coll ) {
- console.log( 'remove %o', model );
- // Do somehting. Destroy a View?
- });
- // Use the 'update' event to listen for changes on a HasOne relation (like 'Person.livesIn').
- paul.on( 'change:livesIn', function( model, attr ) {
- console.log( 'change `livesIn` to %o', attr );
- });
- // Modifying either side of a bi-directional relation updates the other side automatically.
- // Take `paul` out or `ourHouse`; this triggers `remove:occupants` on `ourHouse`,
- // and `change:livesIn` on `paul`
- ourHouse.get( 'occupants' ).remove( paul );
- alert( 'paul.livesIn=' + paul.get( 'livesIn' ) );
- </code></pre>
- <pre class="language-javascript nomargin"><code id="example-person-run3" class="language-javascript runnable" data-setup="#example-person-run2"><!--
- -->// Move into `theirHouse`; triggers 'add:occupants' on ourHouse, and 'change:livesIn' on paul
- theirHouse = new House( { id: 'house-2' } );
- paul.set( { 'livesIn': theirHouse } );
- alert( 'theirHouse.occupants=' + theirHouse.get( 'occupants' ).pluck( 'name' ) );
- </code></pre>
- <p>This is achieved using the following relations and models:</p>
- <pre class="language-javascript"><code class="language-javascript" id="example-person"><!--
- -->House = Backbone.RelationalModel.extend({
- // The 'relations' property, on the House's prototype. Initialized separately for each
- // instance of House. Each relation must define (as a minimum) the 'type', 'key' and
- // 'relatedModel'. Options include 'includeInJSON', 'createModels' and 'reverseRelation'.
- relations: [
- {
- type: Backbone.HasMany, // Use the type, or the string 'HasOne' or 'HasMany'.
- key: 'occupants',
- relatedModel: 'Person',
- includeInJSON: Backbone.Model.prototype.idAttribute,
- collectionType: 'PersonCollection',
- reverseRelation: {
- key: 'livesIn'
- }
- }
- ]
- });
- Person = Backbone.RelationalModel.extend({
- relations: [
- { // Create a (recursive) one-to-one relationship
- type: Backbone.HasOne,
- key: 'user',
- relatedModel: 'User',
- reverseRelation: {
- type: Backbone.HasOne,
- key: 'person'
- }
- }
- ],
- initialize: function() {
- // do whatever you want :)
- }
- });
- PersonCollection = Backbone.Collection.extend({
- url: function( models ) {
- // Logic to create a url for the whole collection, or a set of models.
- // See the tests, or Backbone-tastypie, for an example.
- return '/person/' + ( models ? 'set/' + _.pluck( models, 'id' ).join(';') + '/' : '' );
- }
- });
- User = Backbone.RelationalModel.extend();
- </code></pre>
- </section>
- <section id="change-log">
- <h2>Change Log</h2>
- <h4>Master
- <small>
- <span class="date">(future)</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/compare/0.8.8...master">diff</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/master/backbone-relational.js">download</a>
- </small>
- </h4>
- <ul>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/commit/42b158d8ad5697a955a2c1e615824109dfc52234"><q>42b158d</q></a>
- Add <q>getIdsToFetch</q> to <q>Backbone.RelationalModel</q>.
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/commit/b7e71237d1218eec41ce69e1cf56e5eecdc96bef"><q>b7e7123</q></a>
- <q>getAsync</q> (the successor of <q>fetchRelated</q>) now return a single promise, instead of an array of request objects.
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/467"><q>#467</q></a>:
- Improve lazy loading implemenatation. Add <q>getAsync</q> to <q>Backbone.RelationalModel</q>, which always
- return a single promise that resolves with the attribute's content, and remove <q>fetchRelated</q>.
- </li>
- </ul>
- <h4>0.8.8
- <small>
- <span class="date">(1 April 2014)</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/compare/0.8.7...0.8.8">diff</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/0.8.8/backbone-relational.js">download</a>
- </small>
- </h4>
- <ul>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/411"><q>#411</q></a>:
- Don't add models without an id to the store.
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/419"><q>#419</q></a>:
- Proper return values for single models on collection methods for Backbone 1.1.0
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/commit/4113c69d3752fcac75f7a1959c77349dd4672afa"><q>4113c69</q></a>
- `Backbone.Relational.store.unregister` now also accepts a collection or a model type
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/427"><q>#427</q></a>:
- Fix firing explicit change events
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/215"><q>#215</q></a>
- Add direct support for AMD, CommonJS, require, etc.
- </li>
- </ul>
- <h4>0.8.7
- <small>
- <span class="date">(17 January 2014)</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/compare/0.8.6...0.8.7">diff</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/0.8.7/backbone-relational.js">download</a>
- </small>
- </h4>
- <ul>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/commit/8371089ac8bb92a00c2de38822fea7fa9f555b04"><q>8371089</q></a>:
- Change return types for <q>Collection</q> methods to match Backbone 1.1.0
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/pull/376"><q>#376</q></a>:
- Include ids of unregistered models (not fetched or otherwise) in <q>toJSON</q>
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/commit/8371089ac8bb92a00c2de38822fea7fa9f555b04"><q>8371089</q></a>:
- Add <q>findModel</q>.
- </li>
- </ul>
- <h4>0.8.6
- <small>
- <span class="date">(16 August 2013)</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/compare/0.8.5...0.8.6">diff</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/0.8.6/backbone-relational.js">download</a>
- </small>
- </h4>
- <ul>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/pull/322"><q>#322</q></a>:
- Remove <q>keySource</q> after a <q>set</q> as well.
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/pull/345"><q>#345</q></a>:
- Add <a href="#RelationalModel-find"><q>find</q></a>, a shortcut to <q>findOrCreate</q> with <q>create: false</q>
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/pull/345"><q>#345</q></a>:
- Add lodash compatibility (doesn't have an explicit <q>findWhere</q>).
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/pull/349"><q>#349</q></a>:
- Event ordering: maintain the originally intended order when process gets called more than once.
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/pull/362"><q>#362</q></a>:
- Add support for deep <q>subModelType</q> hierarchies.
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/pull/380"><q>#380</q></a>:
- Fix <q>pop</q> on an empty collection.
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/pull/370"><q>#370</q></a>:
- <q>relations</q> can now be a property or a function.
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/commit/7f1dc51a820aa4a5e22303177cffbbc8e7c53d47"><q>7f1dc51</q></a>:
- <q>relatedModel</q> and <q>collectionType</q> can now be defined as a function as well.
- </li>
- </ul>
- <h4>0.8.5
- <small>
- <span class="date">(10 April 2013)</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/compare/0.8.0...0.8.5">diff</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/0.8.5/backbone-relational.js">download</a>
- </small>
- </h4>
- <ul>
- <li>Supports Backbone 1.0.0</li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/191"><q>#191</q></a>:
- if <q>includeInJSON</q> is equal to the model's <q>idAttribute</q>, "missing" models will be included
- in the JSON to avoid data loss.
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/201"><q>#201</q></a>:
- add <q>Backbone.Store.removeModelScope</q> method
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/273"><q>#273</q></a>:
- improve merging of relations between super/subModels.
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/295"><q>#295</q></a>:
- check (and error on) duplicate ids when explicitly setting the <q>idAttribute</q>.
- </li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/320"><q>#320</q></a>:
- use <q>merge: true</q> by default on <q>Collection.reset</q>.
- </li>
- </ul>
- <h4>0.8.0
- <small>
- <span class="date">5 March 2013</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/compare/0.7.1...0.8.0">diff</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/0.8.0/backbone-relational.js">download</a>
- </small>
- </h4>
- <ul>
- <li>
- General performance improvements, refactored <q>HasMany.onChange</q> to eliminate unnecessary events.
- </li>
- <li>
- Implemented the <q>add</q>, <q>merge</q> and <q>remove</q> options on <q>Collection.add</q> when working with RelationalModels.
- This also works when using <a href="#RelationalModel-set"><q>set</q></a> to change the key on nested relations.
- </li>
- <li>
- The <q>update</q> option on <a href="#RelationalModel-findOrCreate"><q>findOrCreate</q></a> has been
- renamed to <q>merge</q>, since its behavior corresponds with <q>merge</q> on <q>Collection.add</q>
- (and not with <q>update</q> on <q>Collection.reset</q>).
- </li>
- <li>
- The <q>update:<key></q> event has been removed, in favor of handling everything using "standard"
- <q>change:<key></q> events.
- </li>
- <li>
- <a href="#RelationalModel-findOrCreate"><q>findOrCreate</q></a> now takes a <q>parse</q> option,
- analogous to the Backbone.Model constructor. It defaults to <q>false</q>.
- </li>
- <li>Added a <a href="#relations-parse"><q>parse</q></a> option to relations.</li>
- </ul>
-
- <h4>0.7.1
- <small>
- <span class="date">17 Januari 2013</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/compare/0.7.0...0.7.1">diff</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/0.7.1/backbone-relational.js">download</a>
- </small>
- </h4>
- <ul>
- <li>Compatible with Backbone >= 0.9.10</li>
- <li>Implemented the <a href="#relations-autoFetch"><q>autoFetch</q></a> property for relations.</li>
- <li>Added the <q>update</q> option to <a href="#RelationalModel-findOrCreate"><q>findOrCreate</q></a>.</li>
- </ul>
-
- <h4>0.7.0
- <small>
- <span class="date">18 December 2012</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/compare/0.6.0...0.7.0">diff</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/0.7.0/backbone-relational.js">download</a>
- </small>
- </h4>
- <ul>
- <li>Compatible with Backbone >= 0.9.9</li>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/180"><q>#180</q></a>: no longer allow
- multiple instances of RelationalModels with the same type, and the same <q>id</q>.
- </li>
- </ul>
-
- <h4>0.6.0
- <small>
- <span class="date">02 August 2012</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/compare/0.5.0...0.6.0">diff</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/0.6.0/backbone-relational.js">download</a>
- </small>
- </h4>
- <ul>
- <li>
- <a href="https://github.com/PaulUithol/Backbone-relational/issues/60"><q>#60</q></a>: <q>keySource</q> option
- added to relations.
- </li>
- <li>
- <q>keyDestination</q> option added to relations.
- </li>
- <li>
- <q>collectionOptions</q> option added to relations.
- </li>
- <li>
- Added support for super/sub models.
- </li>
- <li>
- Added <q>Backbone.Store.addModelScope</q>.
- </li>
- </ul>
-
- <h4>0.5.0
- <small>
- <span class="date">17 February 2012</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/compare/0.4.0...0.5.0">diff</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/0.5.0/backbone-relational.js">download</a>
- </small>
- </h4>
- <ul>
- <li>
- Update nested models properly on <q>Collection.add</q>.
- </li>
- <li>
- <q>collectionKey</q> options added to relations.
- </li>
- <li>
- Support new Backbone syntax for <q>set</q> (with separate key, value arguments).
- </li>
- <li>
- Initialize reverseRelations on definition, instead of on creation of the first model.
- </li>
- </ul>
-
- <h4>0.4.0
- <small>
- <span class="date">23 July 2011</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/compare/37affc3d...0.4.0">diff</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/0.4.0/backbone-relational.js">download</a>
- </small>
- </h4>
- <ul>
- <li>
- <q>update<key></q> event added
- </li>
- <li>
- Override <q>Backbone.Collection._add</q> and <q>Backbone.Collection._remove</q> so relations update properly.
- </li>
- <li>
- Queue <q>change</q> and <q>change<key></q> events, so they won't fire before relations are updated.
- </li>
- <li>
- Added the <q>Backbone.RelationalModel.updateRelations</q> method.
- </li>
- </ul>
- <h4>First commit
- <small>
- <span class="date">11 April 2011</span> –
- <a href="https://github.com/PaulUithol/Backbone-relational/commit/37affc3d9cbf8b9ae2a1701a3a9e7782cd606a6f#diff-1">commit</a> –
- <a href="https://raw.github.com/PaulUithol/Backbone-relational/37affc3d9cbf8b9ae2a1701a3a9e7782cd606a6f/backbone-relational.js">download</a>
- </small>
- </h4>
- <p>
- The original version of Backbone-relational! This already contained much of the basics: <q>HasOne</q> and <q>HasMany</q>
- relations (including <q>reverseRelation</q>), <q>Backbone.RelationalModel</q> and <q>Backbone.Store</q>.
- </p>
- </section>
- <section id="under-the-hood">
- <h2 >Under the Hood</h2>
- <h3>The model Store</h3>
- <p>
- Each <strong>Backbone.RelationalModel</strong> registers itself with <strong>Backbone.Relational.Store</strong>
- upon creation, and is removed from the <q>store</q> when destroyed. When creating or updating an
- attribute that is a key in a relation, removed related objects are notified of their
- removal, and new related objects are looked up in the Store.
- </p>
- <p>
- Backbone-relational only allows the existence of one model instance for each model type <q>id</q>.
- This check is there to enforce there will only be one version of a model with a certain id at any given time
- (which is also the reason for the existence of Backbone.Relational.Store). This is necessary to enforce
- consistency and integrity of relations.
- </p>
- <p>
- If multiple versions were allowed, inadvertently manipulating or performing a save or destroy
- on another version of that model (which is still around on the client, and can for example still be bound
- to one or more views in your application, either on purpose or inadvertently) would save its state to the
- server, killing its relations, and the server response would set the same (incorrect) data on the 'current'
- version of the model on the client. By then, you'd be in trouble.
- </p>
- <p>
- Therefore, Backbone-relational simply does not allow this situation to occur. This is much safer than
- putting the burden on the developer to always make sure every older version of a model is completely
- decoupled from every other part of your application.
- It might be annoying to get an error every now and then, and sometimes inconvenient to have to use the
- factory method <q>findOrCreate</q>, but it's much better than subtle bugs that can lead to major data loss
- later on in the life cycle of your application.
- </p>
- <h3>Event queuing</h3>
- <p>
- Most of Backbone's default events are queued by Backbone-relational. This can complicate debugging since
- it 'breaks' the call stack, but is a necessary part of the abstraction offered by it.
- </p>
- <p>
- The event queuing kicks in every time (nested) relations need to be updated as the result of calling a
- Backbone method that modifies a model's attributes: on creation, and on <q>set</q>, <q>unset</q>, and <q>clear</q>.
- </p>
- <p>
- The basic problem is that when manipulating relations, you only want the events to fire once ALL relations
- have been updated; also those on models one, two, or more relations away. For example, when you have an
- event listener on <q>add</q>, you want to be able to use the model you receive as the first argument
- and its relations fully instead of getting raw JS objects:
- </p>
- <pre class="language-javascript"><code class="language-javascript runnable" data-setup="#example-job"><!--
- -->var p = new Person( { id: 'p1', name: 'Larry Page' } );
- p.on( 'add:jobs', function( job ) {
- // Here, you want the correct person and company to be set on job, instead of plain js.
- alert( job.get( 'person' ).get( 'name' ) + ' @ ' + job.get( 'company' ).get( 'name' ) );
- });
- p.get( 'jobs' ).add( { company: { name: 'Google' }, person: p.id } );
- </code></pre>
- <p>
- To achieve this, Backbone's events are prevented from firing <i>immediately</i> when <q>add</q>
- is called, but are delayed until the all (nested) relations have reached a stable state again. Events are
- then fired at the first available opportunity; as soon as the last model updating its relations unblocks
- the <q>Backbone.Relational.eventQueue</q>.
- </p>
- </section>
- </div>
- <div class="scripts">
- <script type="text/javascript" src="test/lib/jquery-1.9.1.js"></script>
- <script type="text/javascript" src="test/lib/underscore.js"></script>
- <script type="text/javascript" src="test/lib/backbone.js"></script>
- <script type="text/javascript" src="backbone-relational.js"></script>
- <script type="text/javascript" src="static/js/prism.js"></script>
- <script type="text/javascript">
- // Set up the "play" buttons for each runnable code example.
- $( function() {
- // Run a piece of example code. If an element has a `data-setup` attribute,
- // the element referred to gets executed first.
- function run( codeElem, silenceAlerts ) {
- codeElem = $( codeElem );
- var setup = codeElem.data( 'setup' );
- if ( setup ) {
- run( setup, true );
- }
- if ( silenceAlerts === true ) {
- var _alert = window.alert;
- window.alert = $.noop;
- }
- // Grab the code, and eval it. Nasty twist: remove all `var` statement at the start of a line;
- // this will make all eval'ed vars global, so vars from scripts used as setup can be used
- // by those that need 'em.
- var code = $( codeElem ).text();
- code = code.replace( /(^|\n)(var )/g, '\n' );
- //console.log( 'Executing:\n %s', code );
- eval( code );
- if ( silenceAlerts === true ) {
- window.alert = _alert;
- }
- }
- $( '.runnable' ).each( function() {
- var code = $( this );
- // Add a `runnable` button to each runnable piece of code
- var button = '<div class="run" title="Run"><a class="button orange">►</a></div>';
- $( button ).insertBefore( code ).bind( 'click', function() {
- Backbone.Relational.store.reset();
- run( code );
- });
- });
- });
- </script>
- </div>
- </body>
- </html>