forked from platanus/angular-restmod
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtutorial-building.html
More file actions
394 lines (330 loc) · 16.1 KB
/
Copy pathtutorial-building.html
File metadata and controls
394 lines (330 loc) · 16.1 KB
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>$restmod tutorialModel Building</title>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/sunlight.default.css">
<link type="text/css" rel="stylesheet" href="styles/site.spacelab.css">
</head>
<body>
<div class="container-fluid">
<div class="navbar navbar-fixed-top ">
<div class="navbar-inner">
<a class="brand" href="index.html">$restmod</a>
<ul class="nav">
<li class="dropdown">
<a href="namespaces.list.html" class="dropdown-toggle" data-toggle="dropdown">Namespaces<b
class="caret"></b></a>
<ul class="dropdown-menu ">
<li>
<a href="constants.html">constants</a>
</li>
<li>
<a href="providers.html">providers</a>
</li>
<li>
<a href="services.html">services</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="classes.list.html" class="dropdown-toggle" data-toggle="dropdown">Classes<b
class="caret"></b></a>
<ul class="dropdown-menu ">
<li>
<a href="CommonApi.html">CommonApi</a>
</li>
<li>
<a href="constants.Utils.html">Utils</a>
</li>
<li>
<a href="Model.html">Model</a>
</li>
<li>
<a href="ModelBuilder.html">ModelBuilder</a>
</li>
<li>
<a href="ModelCollection.html">ModelCollection</a>
</li>
<li>
<a href="providers.$restmodProvider.html">$restmodProvider</a>
</li>
<li>
<a href="services.$restmod.html">$restmod</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="mixins.list.html" class="dropdown-toggle" data-toggle="dropdown">Mixins<b
class="caret"></b></a>
<ul class="dropdown-menu ">
<li>
<a href="DebouncedModel.html">DebouncedModel</a>
</li>
<li>
<a href="DirtyModel.html">DirtyModel</a>
</li>
<li>
<a href="PagedModel.html">PagedModel</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="tutorials.list.html" class="dropdown-toggle" data-toggle="dropdown">Tutorials<b
class="caret"></b></a>
<ul class="dropdown-menu ">
<li>
<a href="tutorial-building.html">Model Building</a>
</li>
<li>
<a href="tutorial-model_.html">Using Generated Models</a>
</li>
<li>
<a href="tutorial-relations.html">Using Relations</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="main">
<section>
<header>
<h2>Model Building</h2>
</header>
<article>
<p>Model building in restmod is done via the <code>$restmod</code> service.</p>
<p>The service provides two methods: <code>model</code> is used to build new models and <code>mixin</code> to build mixins. More on <strong>mixins</strong> later.</p>
<h2>Simple model building</h2>
<p>Basic model building looks like this</p>
<pre><code class="lang-javascript">angular.module('test').factory('MyModel', function($restmod) {
return $restmod.model('/model-base-url');
});</code></pre>
<p>It's recommended to put models inside factories, this is usefull later when defining relations and inheritance, since the angular $injector is used by these features. It's also the angular way of doing things. This guide will skip the factory code in examples for simplicity sake.</p>
<p>The first and only required parameter is the base url for the model, if null is given then the model is considered unbound.</p>
<p>Unbound models can only be fetched/created/updated when bound to other models (via a relation).</p>
<p>Refer to <a href="tutorial-model_.html">Using Generated Models</a> for information about how to use the generated model.</p>
<h2>Advanced model building</h2>
<p>Restmod offers two ways of customizing a model's behaviour, <strong>definition blocks</strong> and <strong>definition objects</strong>, both are used by passing the required parameters to the <code>model</code> function after the base url parameter.</p>
<h3>Model definition blocks</h3>
<p>If a function is given to <code>model</code>, then the function is called in the builder context (<code>this</code> points to the builder).</p>
<pre><code class="lang-javascript">$restmod.model('/model-base-url', function() {
this.attrMask('createdBy', SyncMask.DECODE) // Set an attribute mask
.attrDefault('createdBy', 'This is read only') // Set an attribute default value
.beforeSave(function() { // Set a model hook
alert('model will be saved!')
});
});</code></pre>
<p>Even though the preferred way of customizing a model is using the definition object, some builder methods like <code>beforeCreate</code>can only be accesed through the builder context.</p>
<p>For more information about available builder functions check <em>The Builder DSL</em> section below and the ModelBuilder class documentation.</p>
<h3>The model definition object</h3>
<p>It is also posible to provide an plain object to the <code>model</code> method. This object will be treated as a model definition.</p>
<p>A model assigns one or more modifiers to model instance properties.</p>
<pre><code class="lang-javascript">$restmod.model('api/bikes', {
created_at: { serialize: 'RailsDate' }, // set a serializer
createdBy: { init: 'This is readonly', mask: SyncMask.DECODE }, // set a default value and a mask
parts: { hasMany: 'Part' } // set a relation with another model built by a factory called 'Part'
});</code></pre>
<p>Property modifiers map directly to builder DSL functions, so using</p>
<pre><code class="lang-javascript">$restmod.model('', {
prop: { init: 20 }
});</code></pre>
<p>Is equivalent to</p>
<pre><code class="lang-javascript">$restmod.model('', function() {
this.attrDefault('prop', 20);
}</code></pre>
<p>For more information about available property modifiers check the <a href="ModelBuilder.html">ModelBuilder</a> class documentation.</p>
<p>Some plugins also add additional modifiers.</p>
<p>Property modifiers map directly to builder DSL methods, but not every builder method is available as a property behavior modifier. The model definition object only provides a subset of the available builder DSL methods.</p>
<h3>The mixin chain</h3>
<p>You are not restricted to use only one building method, multiple object and function blocks can be passed to the <code>model</code> function and are processed sequentially by the model builder. This is called the mixin chain.</p>
<p>In addition to definition objects and functions, the mixin chain also allows for model or mixin names, if a name is given, the model/mixin is looked up using the injector and its mixin chain is injected in the current chain.</p>
<pre><code class="lang-javascript">// Example using multiple mixins
$restmod.model('api/bikes',
'Vehicle', // inject Vehicle mixin chain first
// Then a model definition object
{
brakes: { serialize: 'Brakes' },
},
// Finally a model definition block
function() {
this.beforeCreate(function() {
});
})</code></pre>
<h4>The global mixin chain</h4>
<p>It is also posible to define a common mixin chain for all models in the application, this is done using the <code>pushModelBase</code> method available at the $restmodProvider provider in the configuration stage.</p>
<h2>The builder DSL</h2>
<h3>Defaults</h3>
<p>A default value can be given to any model property, this is done via de <code>init</code> property modifier. Check the <a href="ModelBuilder.html#attrDefault">ModelBuilder#attrDefault</a> docs for more information.</p>
<pre><code class="lang-javascript">var Model = $restmod.model(null, {
owner: { init: 'anonymous' }
};</code></pre>
<p>If a function is provided then it will be evaluated every time a model instance is created and its return value assigned to the property.</p>
<h3>Property transformation</h3>
<p>It is posible to specify how a given property is serialized or deserialized using the <code>encode</code> or <code>decode</code> property modifiers respectively. This modifiers take a filter name or a function and applies it to the property value during transformation. Check the <a href="ModelBuilder.html#attrDecoder">ModelBuilder#attrDecoder</a> and <a href="ModelBuilder.html#attrEncoder">ModelBuilder#attrEncoder</a> docs for more information.</p>
<pre><code class="lang-javascript">var Model = $restmod.model(null, {
createdAt: { encode: 'date', param: 'yyyy-MM-dd' }, // use the angular date filter to serialize
sku: { decode: function(v) { return v.replace(/[^\d]+/,''); } } // use an inline function to deserialize
});</code></pre>
<p>Another way of specifying how a property is serialized is using a serializer. A serializer is an object that defines al least a decode or an encode method (or both). The methods are called with the property value and are expected to return a new value. The <code>serialize</code> modifier can be used to define a Serializer, if a <em>string</em> is given then the angular injector is used to load the required serializer by appending 'Serializer' to the given string. Check the <a href="ModelBuilder.html#attrSerializer">ModelBuilder#attrSerializer</a> docs for more information.</p>
<pre><code class="lang-javascript">var Model = $restmod.model(null, {
createdAt: { serialize: 'date' }, // will search for a 'DateSerializer' in the current injector.
sku: {
// use an inline serializer
serialize: {
encode: function(v) { return v + '!' },
decode: function(v) { return v.replace(/[^\d]+/,''); }
}
}
});</code></pre>
<h3>Masks</h3>
<p>Masks can be used to prevent certain properties to be sent to or loaded from the server. They specify a set actions a property is masked from. Available mask values are enumerated in the SyncMask constant and can be combined using bit operations.</p>
<p>Masks can be specified using the <code>mask</code> or <code>ignore</code> property modifiers. Check the <a href="ModelBuilder.html#attrMask">ModelBuilder#attrMask</a> docs for more information.</p>
<pre><code class="lang-javascript">var Model = $restmod.model(null, {
viewed: { mask: true }, // the property value is never sent to or loaded from the server.
createdAt: { mask: SyncMask.ENCODE }, // this property value is never sent to the server (but it is loaded from)
createdBy: { mask: SyncMask.ENCODE_UPDATE } // this property value is not sent to the server during an update
});</code></pre>
<h3>Defining relations</h3>
<p>Relations can be specified between models using the <code>hasMany</code> and <code>hasOne</code> modifiers. Check the ModelBuilder#hasMany and {@link ModelBuilder#hasOne} docs for more information.</p>
<pre><code class="lang-javascript">angular.module('test')
.factory('Part', function($restmod) {
return $restmod.model(null);
})
.factory('Bike', function($restmod) {
return $restmode.model('/bikes', {
parts: { hasMany: 'Part' }
});
});</code></pre>
<p>More about relations in the <a href="tutorial-model_.html">Using Generated Models</a> guide.</p>
<h3>Hooks</h3>
<p>A model behavior can be modified using a wide range of hooks provided by restmod. The hooks are placed on model's lifecycle key points and can be used to modify/extend the model behavior.</p>
<pre><code class="lang-javascript">var Model = $restmod.model(null, function() {
// make a property undefined after every save.
this.on('after-save', function() {
delete this.sendOnce;
});
};</code></pre>
<p>Notes:
<em> The before-request hooks can be used to modify the request before is sent.
</em> The after-request[-error] hooks can be used to modify the request before processed by model.
* The model builder provides a set of shorthand methods to subscribe to the most common hooks</p>
<p>Additional hooks can be called by user defined methods using the <code>$callback</code> method</p>
<h4>Object lifecycle hooks</h4>
<p>For <code>$fetch</code>:</p>
<ul>
<li>before-fetch</li>
<li>before-request</li>
<li>after-request[-error]</li>
<li>after-feed (only called if no errors)</li>
<li>after-fetch[-error]</li>
</ul>
<p>For <code>$fetch</code> on a collection:</p>
<ul>
<li>before-fetch-many</li>
<li>before-request</li>
<li>after-request[-error]</li>
<li>after-feed (only called if no errors)</li>
<li>after-fetch-many[-error]</li>
</ul>
<p>For <code>$save</code> when creating:</p>
<ul>
<li>before-render</li>
<li>before-save</li>
<li>before-create</li>
<li>before-request</li>
<li>after-request[-error]</li>
<li>after-feed (only called if no errors)</li>
<li>after-create[-error]</li>
<li>after-save[-error]</li>
</ul>
<p>For <code>$save</code> when updating:</p>
<ul>
<li>before-render</li>
<li>before-save</li>
<li>before-update</li>
<li>before-request</li>
<li>after-request[-error]</li>
<li>after-feed (only called if no errors)</li>
<li>after-update[-error]</li>
<li>after-save[-error]</li>
</ul>
<p>For <code>$destroy</code>:</p>
<ul>
<li>before-destroy</li>
<li>before-request</li>
<li>after-request[-error]</li>
<li>after-destroy[-error]</li>
</ul>
<h3>Adding new methods and actions</h3>
<p>Methods can be added or overriden both in model instances and model collections using the builder <a href="ModelBuilder.html#define">ModelBuilder#define</a> and the <a href="ModelBuilder.html#classDefine">ModelBuilder#classDefine</a> respectively. Methods that override another method through these functions will have access to the this.$super property that points to the old method (prebound, so its not necessary to call this.$super.call).</p>
<pre><code class="lang-javascript">$restmod.model(null, function() {
// add the $create method to model instances
this.define('$create', function() {
this.id = undefined;
return this.$save();
});
// override model collection's $fetch method
this.classDefine('$fetch', function() {
this.$super({ forceParam: 'This parameter will be included in all fetch requests' });
});
});</code></pre>
<p>It is also posible to add or override a model <strong>instance</strong> method using a definition object</p>
<pre><code class="lang-javascript">$restmod.model(null, {
$create: function() {
this.id = undefined;
return this.$save();
}
});</code></pre>
<h3>Extending the DSL</h3>
<p><strong>UNDER CONSTRUCTION</strong></p>
<p>DSL extension is done using the mixin mechanism and the <code>extend</code> builder method.</p>
<p>For example, building a simple extension will look like this:</p>
<p>The using the extension is only a matter of putting the extension's mixin in a model's mixin chain (before it is used).</p>
<p><strong>UNDER CONSTRUCTION</strong></p>
</article>
</section>
</div>
<div class="clearfix"></div>
<footer>
<span class="jsdoc-message">
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a>
on 2014-05-20T18:42:31-04:00 using the <a href="https://github.com/terryweiss/docstrap">DocStrap template</a>.
</span>
</footer>
</div>
<br clear="both">
</div>
</div>
<script src="scripts/sunlight.js"></script>
<script src="scripts/sunlight.javascript.js"></script>
<script src="scripts/sunlight-plugin.doclinks.js"></script>
<script src="scripts/sunlight-plugin.linenumbers.js"></script>
<script src="scripts/sunlight-plugin.menu.js"></script>
<script src="scripts/jquery.min.js"></script>
<script src="scripts/jquery.scrollTo.js"></script>
<script src="scripts/jquery.localScroll.js"></script>
<script src="scripts/bootstrap-dropdown.js"></script>
<script src="scripts/toc.js"></script>
<script> Sunlight.highlightAll({lineNumbers: true, showMenu: true, enableDoclinks: true}); </script>
<script>
$( function () {
$( "#toc" ).toc( {
anchorName : function(i, heading, prefix) {
return $(heading).attr("id") || ( prefix + i );
},
selectors : "h1,h2,h3,h4",
showAndHide : false,
scrollTo : 60
} );
$( "#toc>ul" ).addClass( "nav nav-pills nav-stacked" );
$( "#main span[id^='toc']" ).addClass( "toc-shim" );
} );
</script>
</body>
</html>