forked from PaulUithol/Backbone-relational
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
1071 lines (949 loc) · 42.1 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!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/prism.css">
<link rel="stylesheet" type="text/css" href="static/css/style.css">
<title>Backbone-relational.js</title>
</head>
<body>
<div id="sidebar" class="interface">
<a class="toc_title" href="#">
Backbone-relational.js <span class="version">(0.7.1)</span>
</a>
<ul class="toc_section">
<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 class="toc_section">
<li>
<a href="#RelationalModel-relations">relations</a>
<ul class="toc_section">
<li><a href="#Relation-relatedModel">relatedModel</a></li>
<li><a href="#Relation-key">key</a></li>
<li><a href="#Relation-type">type</a></li>
<li><a href="#Relation-keySource">keySource</a></li>
<li><a href="#Relation-keyDestination">keyDestination</a></li>
<li><a href="#Relation-collectionType">collectionType</a></li>
<li><a href="#Relation-collectionKey">collectionKey</a></li>
<li><a href="#Relation-includeInJSON">includeInJSON</a></li>
<li><a href="#Relation-createModels">createModels</a></li>
<li><a href="#Relation-reverseRelation">reverseRelation</a></li>
</ul>
</li>
<li><a href="#RelationalModel-subModelTypes">subModelTypes</a></li>
<li><a href="#RelationalModel-subModelTypeAttribute">subModelTypeAttribute</a></li>
<li><a href="#RelationalModel-getRelation">getRelation</a></li>
<li><a href="#RelationalModel-getRelations">getRelations</a></li>
<li><a href="#RelationalModel-fetchRelated">fetchRelated</a></li>
<li><a href="#RelationalModel-toJSON">toJSON</a></li>
<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-events"><strong>Catalog of Events</strong></a></li>
</ul>
<a class="toc_title" href="#Relation">
Backbone.Relation
</a>
<ul class="toc_section">
<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 class="toc_section">
<li><a href="#Store-addModelScope">addModelScope</a></li>
<li><a href="#Store-reset">reset</a></li>
</ul>
<a class="toc_title" href="#examples">
Examples
</a>
<a class="toc_title" href="#under-the-hood">
Under the Hood
</a>
</div>
<div class="container">
<h1 id="header">
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
<a href="http://backbonejs.org/#Model-save">save()</a> or <a href="http://backbonejs.org/#Model-fetch">fetch()</a>
on each of the changed models to sync with the server, but that quickly turns into a tediuous process and results in multiple requests.
Instead, we can configure relationships between our models, and then with a single
<a href="http://backbonejs.org/#Model-save">save()</a> or <a href="http://backbonejs.org/#Model-fetch">
fetch()</a>, sync the model and all of its related models.
</p>
<p>
Backbone-relational is hosted on <a href="https://github.com/PaulUithol/Backbone-relational">GibHub</a>,
and is available under the <a href="https://github.com/PaulUithol/Backbone-relational/blob/master/LICENSE.txt">MIT license</a>.
</p>
<h2 id="Downloads">
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.7.1/backbone-relational.js">Latest Release (0.7.1)</a></td>
<td class="text"><i>~56kb, Full source, 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>(> 0.9.10)</small>,
which itself requires <a href="http://underscorejs.org">Underscore.js</a> <small>(> 1.4.3)</small> and
<a href="http://jquery.com">jQuery</a> <small>(> 1.4.2)</small> or
<a href="http://zeptojs.com/">Zepto</a>.
</p>
<h2 id="Introduction">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="#Relation-type"><q>type</q></a>, <a href="#Relation-key"><q>key</q></a>,
and <a href="#Relation-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="#Relation-includeInJSON"><q>includeInJSON</q></a> option.
</li>
<li>
Automatically convert nested objects in a model's attributes into model instances using the <a href="#Relation-createModels"><q>createModels</q></a>
option.
</li>
<li>
Lazily retrieve a set of related models through the <a href="#RelationalModel-fetchRelated"><q>fetchRelated</q></a>
method.
</li>
<li>
Determine the type of HasMany collections with <a href="#Relation-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 key itself on HasMany and HasOne relations with <a href="#RelationalModel-events">update:
<key></a>.
</li>
</ul>
<h2 id="Installation">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>
<h2 id="RelationalModel">
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 along with the subclass prototype 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-getRelation">getRelation</a></li>
<li><a href="#RelationalModel-getRelations">getRelations</a></li>
<li><a href="#RelationalModel-fetchRelated">fetchRelated</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>
</ul>
<h3 id="RelationalModel-properties">
Properties
</h3>
<h4 class="code" id="RelationalModel-relations">
relations<code>relation[]</code>
</h4>
<p>
Each <strong>Backbone.RelationalModel</strong> can contain an array of relation definitions. Each relation supports a number of
options, of which <a href="#Relation-relatedModel"><q>relatedModel</q></a>, <a href="#Relation-key"><q>key</q></a> and
<a href="#Relation-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>
<h4 class="code" id="Relation-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>
<h4 class="code" id="Relation-key">
key<code>relation.key</code>
</h4>
<p>
Required. A string that references an attribute name on <a href="#Relation-relatedModel"><q>relatedModel</q></a>.
</p>
<h4 class="code" id="Relation-type">
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>
<pre class="language-javascript"><code class="language-javascript"><!--
-->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>
<h4 class="code" id="Relation-keySource">
keySource<code>relation.keySource</code>
</h4>
<p>
A string that references an attribute to deserialize data for <a href="#Relation-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 it's 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="#Relation-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>
<h4 class="code" id="Relation-keyDestination">
keyDestination<code>relation.keyDestination</code>
</h4>
<p>
A string that references an attribute to serialize <a href="#Relation-relatedModel"><q>relatedModel</q></a> into.
</p>
<p>
Used to override key (and <a href="#Relation-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>
<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>
<h4 class="code" id="Relation-collectionType">
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-fetchRelated"><q>fetchRelated</q></a> to fetch all missing models in one request, instead of
firing a separate request for each.
</p>
<h4 class="code" id="Relation-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>
<h4 class="code" id="Relation-collectionOptions">
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>
<h4 class="code" id="Relation-includeInJSON">
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>
<h4 class="code" id="Relation-createModels">
createModels<code>relation.createModels</code>
</h4>
<p>
A boolean. Default: <q>true</q>.
</p>
<p>
Specifies whether models be created from nested objects or not.
</p>
<h4 class="code" id="Relation-reverseRelation">
reverseRelation<code>relation.reverseRelation</code>
</h4>
<p>
An object specifying the relation pointing back to this model from <a href="#Relation-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="#Relation-key"><q>key</q></a>; <a href="#Relation-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>
<h4 class="code" id="Relation-autoFetch">
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-fetchRelated"><q>fetchRelated</q></a>. The
value of the property can also be an object. In that case the related model is
automatically fetched and the object is passed to <a href="#RelationalModel-fetchRelated"><q>
fetchRelated</q></a> as the options parameter.
</p>
<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>
<h4 class="code" id="RelationalModel-subModelTypes">
subModelTypes
<code>relationalModel.subModelTypes(attributes<string|number|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 it's 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>
<h4 class="code" id="RelationalModel-subModelTypeAttribute">
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="#Relation-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="#Relation-relatedModel"><q>relatedModel</q></a> has one or more submodels.
</p>
<h3 id="RelationalModel-instance-methods">
Instance methods
</h3>
<h4 class="code" id="RelationalModel-getRelation">
getRelation<code>relationModel.getRelation( name<string> )</code>
</h4>
<p>
Returns: <q>Backbone.Relation</q> A single initialized relation on the model.
</p>
<h4 class="code" id="RelationalModel-getRelations">
getRelations<code>relationModel.getRelations()</code>
</h4>
<p>
Returns: <q>Backbone.Relation[]</q> The set of initialized relations on the model.
</p>
<h4 class="code" id="RelationalModel-fetchRelated">
fetchRelated<code>relationalModel.fetchRelated(key<string>, [options<object>], [update<boolean>])</code>
</h4>
<p>
Returns: <q>deferred[]</q> An array of request objects.
</p>
<p>
Fetch models from the server that were referenced in the model's attributes, but have not been found/created yet.
This can be used specifically for lazy-loading scenarios. Setting update to true guarantees that the model will
be fetched from the server and any model that already exists in the store will be updated with the retrieved data.
The options 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 additional model that is to be fetched from the server.
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 fetchRelated on. The <a href="#Relation-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>
<h4 class="code" id="RelationalModel-toJSON">
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="#Relation-includeInJSON"><q>includeInJSON</q></a>, <a href="#Relation-keySource"><q>keySource</q></a>,
and <a href="#Relation-keyDestination"><q>keyDestination</q></a> options.
</p>
<h3 id="RelationalModel-static-methods">
Static methods
</h3>
<h4 class="code" id="RelationalModel-setup">
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 operate on the model type itself, not on an instance.
</p>
<pre class="language-javascript"><code class="language-javascript"><!--
-->class MyModel extends Backbone.RelationalModel
relations: [
// etc
]
MyModel.setup()
</code></pre>
<h4 class="code" id="RelationalModel-build">
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 operate on the model type itself, not on an instance.
</p>
<h4 class="code" id="RelationalModel-findOrCreate">
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.
If no model is found, a new model will be created if <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>update</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>
</dl>
<p class="warning">
Note: this is a static method. It operate on the model type itself, not on an instance.
</p>
<h4 id="RelationalModel-events">
Catalog of Events
</h4>
<p>
This is the complete list of all of the events that Backbone-relational makes available to you, on top of
the events already found in Backbone.
</p>
<ul class="small">
<li>
<strong>"add"</strong> - triggered on addition to a HasMany relation.
</li>
<li>
<strong>"add:<key>"</strong><code>(addedModel<Backbone.Model>, related<Backbone.Collection>)</code>
</li>
<li>
<strong>"remove"</strong> - triggered on removal from a HasMany relation.
</li>
<li>
<strong>"remove:<key>"</strong><code>(removedModel<Backbone.Model>, related<Backbone.Collection>)</code>
</li>
<li>
<strong>"update"</strong> - triggered on changes to the key itself on HasMany and HasOne relations.
</li>
<li>
<strong>"update:<key>"</strong><code>(model<Backbone.Model>, related<Backbone.Model|Backbone.Collection>)</code>
</li>
</ul>
<h2 id="Relation">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="#Relation-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="#Relation-reverseRelation">reverseRelation</a>, the type
will be <strong>HasOne</strong>.
</p>
<h2 id="Store">Backbone.Relational.Store</h2>
<p>
The <strong>Store</strong> is a global model cache. Per application, one instance is created, which is accessible as
<q>Backbone.store</q> (much like <q>Backbone.History</q>).
</p>
<h4 class="code" id="Store-addModelScope">
addModelScope<code>Backbone.store.addModelScope( scope<object> )</code>
</h4>
<p>
When working in an environment without a shared global scope (like <q>window</q> is in a browser), 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-reset">
reset<code>Backbone.store.reset()</code>
</h4>
<p>
Reset the <q>store</q> to it's 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>
<h2 id="examples">Examples</h2>
<p>
A tutorial by antoviaque: <a href="http://antoviaque.org/docs/tutorials/backbone-relational-tutorial/">http://antoviaque.org/docs/tutorials/backbone-relational-tutorial/</a>
(<a href="https://github.com/antoviaque/backbone-relational-tutorial">and the 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: '[email protected]' }
});
// 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.fetchRelated( '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 ) {
// create a View?
console.debug( 'add %o', model );
})
.on( 'remove:occupants', function( model, coll ) {
// destroy a View?
console.debug( 'remove %o', model );
});
// Use the 'update' event to listen for changes on a HasOne relation (like 'Person.livesIn').
paul.on( 'update:livesIn', function( model, attr ) {
console.debug( 'update 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 `update: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 'update: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>
<h2 id="under-the-hood">Under the Hood</h2>
<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>