forked from agsdot/mithril
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mithril.request.html
608 lines (576 loc) · 32.7 KB
/
mithril.request.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
<!doctype html>
<html>
<head>
<title>m.request - Mithril</title>
<meta name="description" value="Mithril.js - a Javascript Framework for Building Brilliant Applications" />
<link href="lib/prism/prism.css" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<header>
<nav class="container">
<a href="index.html" class="logo"><span>○</span> Mithril</a>
<a href="getting-started.html">Guide</a>
<a href="mithril.html">API</a>
<a href="community.html">Community</a>
<a href="http://lhorie.github.io/mithril-blog">Learn</a>
<a href="mithril.min.zip">Download</a>
<a href="http://github.com/lhorie/mithril.js" target="_blank">Github</a>
</nav>
</header>
<main>
<section class="content">
<div class="container">
<div class="row">
<div class="col(3,3,12)">
<h2 id="api">API (v0.2.0)</h2>
<h3 id="core">Core</h3>
<ul>
<li><a href="mithril.html" title="A utility to create virtual elements">m</a></li>
<li><a href="mithril.component.html" title="Parameterizes a component">m.component</a></li>
<li><a href="mithril.mount.html" title="Renders a component">m.mount</a></li>
<li><a href="mithril.prop.html" title="A getter-setter utility">m.prop</a></li>
<li><a href="mithril.withAttr.html" title="A event handler factory utility">m.withAttr</a></li>
</ul>
<h3 id="routing">Routing</h3>
<ul>
<li><a href="mithril.route.html" title="A routing utility">m.route</a>
<ul>
<li><a href="mithril.route.html#defining-routes" title="Defines what routes exist">m.route(rootElement, defaultRoute, routes)</a></li>
<li><a href="mithril.route.html#redirecting" title="Redirects to a route">m.route(path, params, replaceHistory)</a></li>
<li><a href="mithril.route.html#reading-current-route" title="Read the current route">m.route()</a></li>
<li><a href="mithril.route.html#mode-abstraction" title="Routing mode abstraction">m.route(element)</a></li>
<li><a href="mithril.route.html#mode" title="Whether routing uses location hash, querystring or pathname">m.route.mode</a></li>
<li><a href="mithril.route.html#param" title="Read an argument from a parameterized route">m.route.param</a></li>
<li><a href="mithril.route.html#buildQueryString" title="Serialize data into querystring format">m.route.buildQueryString</a></li>
<li><a href="mithril.route.html#parseQueryString" title="Read an argument from a parameterized route">m.route.parseQueryString</a></li>
</ul>
</li>
</ul>
<h3 id="data">Data</h3>
<ul>
<li><a href="mithril.request.html" title="A high-level AJAX utility">m.request</a></li>
<li><a href="mithril.deferred.html" title="A Promise factory">m.deferred</a></li>
<li><a href="mithril.sync.html" title="A Promise aggregator">m.sync</a></li>
</ul>
<h3 id="html">HTML</h3>
<ul>
<li><a href="mithril.trust.html" title="A method to unescape HTML">m.trust</a></li>
</ul>
<h3 id="rendering">Rendering</h3>
<ul>
<li><a href="mithril.render.html" title="The lowest level rendering method">m.render</a></li>
<li><a href="mithril.redraw.html" title="A high-level explicit rendering method">m.redraw</a>
<ul>
<li><a href="mithril.redraw.html#strategy" title="A flag that drives the rendering strategy for the next redraw">m.redraw.strategy(strategy)</a></li>
</ul>
</li>
<li><a href="mithril.computation.html" title="Utilities to integrate asynchronous contexts to the rendering system">m.startComputation / m.endComputation</a></li>
</ul>
<h3 id="data">Testing</h3>
<ul>
<li><a href="mithril.deps.html" title="The dependency injector">m.deps</a></li>
</ul>
<h2 id="archive">History</h2>
<ul>
<li><a href="roadmap.html">Roadmap</a></li>
<li><a href="change-log.html">Change log</a></li>
</ul>
</div>
<div class="col(9,9,12)">
<h2 id="m-request">m.request</h2>
<hr>
<ul>
<li><a href="#basic-usage">Basic usage</a></li>
<li><a href="#processing-web-service-data">Processing-web-service-data</a></li>
<li><a href="#bind-redirection-code">Bind redirection code</a></li>
<li><a href="#binding-errors">Binding errors</a></li>
<li><a href="#queuing-operations">Queuing operations</a></li>
<li><a href="#casting-the-response-data-to-a-class">Casting the Response Data to a Class</a></li>
<li><a href="#unwrapping-response-data">Unwrapping Response Data</a></li>
<li><a href="#using-different-data-transfer-formats">Using Different Data Transfer Formats</a></li>
<li><a href="#file-uploads-with-formdata">File uploads with FormData</a></li>
<li><a href="#using-variable-data-formats">Using variable data formats</a></li>
<li><a href="#extracting-metadata-from-the-response">Extracting Metadata from the Response</a></li>
<li><a href="#custom-request-rejections">Custom request rejections</a></li>
<li><a href="#configuring-the-underlying-xmlhttprequest">Configuring the underlying XMLHttpRequest</a></li>
<li><a href="#aborting-a-request">Aborting a request</a></li>
<li><a href="#using-json-p">Using JSON-P</a></li>
<li><a href="#rendering-before-web-service-requests-finish">Rendering before web service requests finish</a></li>
<li><a href="#signature">Signature</a></li>
</ul>
<hr>
<p>This is a high-level utility for working with web services, which allows writing asynchronous code relatively procedurally.</p>
<p>By default, it assumes server responses are in JSON format and optionally instantiates a class with the response data.</p>
<p>It provides a number of useful features out of the box:</p>
<ul>
<li>The ability to get an early reference to a container that will hold the asynchronous response</li>
<li>The ability to queue operations to be performed after the asynchronous request completes</li>
<li>The ability to "cast" the response to a class of your choice</li>
<li>The ability to unwrap data in a response that includes metadata properties</li>
</ul>
<hr>
<h3 id="basic-usage">Basic usage</h3>
<p>The basic usage pattern for <code>m.request</code> returns an <a href="mithril.prop.html"><code>m.prop</code></a> getter-setter, which is populated when the AJAX request completes.</p>
<p>The returned getter-setter can be thought of as a box: you can pass this reference around cheaply, and you can "unwrap" its value when needed.</p>
<pre><code class="lang-javascript">var users = m.request({method: "GET", url: "/user"});
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: "John"}, {name: "Mary"}]
</code></pre>
<p>Note that this getter-setter holds an <em>undefined</em> value until the AJAX request completes. Attempting to unwrap its value early will likely result in errors.</p>
<p>The returned getter-setter also implements the <a href="mithril.deferred.html">promise</a> interface (also known as a <em>thennable</em>): this is the mechanism you should always use to queue operations to be performed on the data from the web service.</p>
<p>The simplest use case of this feature is to implement functional value assignment via <code>m.prop</code> (i.e. the same thing as above). You can bind a pre-existing getter-setter by passing it in as a parameter to a <code>.then</code> method:</p>
<pre><code class="lang-javascript">var users = m.prop([]); //default value
m.request({method: "GET", url: "/user"}).then(users)
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: "John"}, {name: "Mary"}]
</code></pre>
<p>This syntax allows you to bind intermediate results before piping them down for further processing, for example:</p>
<pre><code class="lang-javascript">var users = m.prop([]); //default value
var doSomething = function() { /*...*/ }
m.request({method: "GET", url: "/user"}).then(users).then(doSomething)
</code></pre>
<p>While both basic assignment syntax and thennable syntax can be used to the same effect, typically it's recommended that you use the assignment syntax whenever possible, as it's easier to read.</p>
<p>The thennable mechanism is intended to be used in three ways:</p>
<ul>
<li>in the model layer: to process web service data in transformative ways (e.g. filtering a list based on a parameter that the web service doesn't support)</li>
<li>in the controller layer: to bind redirection code upon a condition</li>
<li>in the controller layer: to bind error messages</li>
</ul>
<h4 id="processing-web-service-data">Processing web service data</h4>
<p>This step is meant to be done in the model layer. Doing it in the controller level is also possible, but philosophically not recommended, because by tying logic to a controller, the code becomes harder to reuse due to unrelated controller dependencies.</p>
<p>In the example below, the <code>listEven</code> method returns a getter-setter that resolves to a list of users containing only users whose id is even.</p>
<pre><code class="lang-javascript">//model
var User = {}
User.listEven = function() {
return m.request({method: "GET", url: "/user"}).then(function(list) {
return list.filter(function(user) {return user.id % 2 == 0});
});
}
//controller
var controller = function() {
return {users: User.listEven()}
}
</code></pre>
<h4 id="bind-redirection-code">Bind redirection code</h4>
<p>This step is meant to be done in the controller layer. Doing it in the model level is also possible, but philosophically not recommended, because by tying redirection to the model, the code becomes harder to reuse due to overly tight coupling.</p>
<p>In the example below, we use the previously defined <code>listEven</code> model method and queue a controller-level function that redirects to another page if the user list is empty.</p>
<pre><code class="lang-javascript">//controller
var controller = function() {
return {
users: User.listEven().then(function(users) {
if (users.length == 0) m.route("/add");
})
}
}
</code></pre>
<h4 id="binding-errors">Binding errors</h4>
<p>Mithril thennables take two functions as optional parameters: the first parameter is called if the web service request completes successfully. The second one is called if it completes with an error.</p>
<p>Error binding is meant to be done in the controller layer. Doing it in the model level is also possible, but generally leads to more code in order to connect all the dots.</p>
<p>In the example below, we bind an error getter-setter to our previous controller so that the <code>error</code> variable gets populated if the server throws an error.</p>
<pre><code class="lang-javascript">//controller
var controller = function() {
this.error = m.prop("")
this.users = User.listEven().then(function(users) {
if (users.length == 0) m.route("/add");
}, this.error)
}
</code></pre>
<p>If the controller doesn't already have a success callback to run after a request resolves, you can still bind errors like this:</p>
<pre><code class="lang-javascript">//controller
var controller = function() {
this.error = m.prop("")
this.users = User.listEven().then(null, this.error)
}
</code></pre>
<hr>
<h3 id="queuing-operations">Queuing Operations</h3>
<p>As you saw, you can chain operations that act on the response data. Typically this is required in three situations:</p>
<ul>
<li>in model-level methods if client-side processing is needed to make the data useful for a controller or view.</li>
<li>in the controller, to redirect after a model service resolves.</li>
<li>in the controller, to bind error messages</li>
</ul>
<p>In the example below, we take advantage of queuing to debug the AJAX response data prior to doing further processing on the user list</p>
<pre><code class="lang-javascript">//a FP-friendly console.log
var log = function(value) {
console.log(value)
return value
}
var users = m.request({method: "GET", url: "/user"})
.then(log)
.then(function(users) {
//add one more user to the response
return users.concat({name: "Jane"})
})
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: "John"}, {name: "Mary"}, {name: "Jane"}]
</code></pre>
<hr>
<h3 id="casting-the-response-data-to-a-class">Casting the Response Data to a Class</h3>
<p>It's possible to auto-cast a JSON response to a class. This is useful when we want to control access to certain properties in an object, as opposed to exposing all the fields in POJOs (plain old Javascript objects) for arbitrary processing.</p>
<p>In the example below, <code>User.list</code> returns a list of <code>User</code> instances.</p>
<pre><code class="lang-javascript">var User = function(data) {
this.name = m.prop(data.name);
}
User.list = function() {
return m.request({method: "GET", url: "/user", type: User});
}
var users = User.list();
//assuming the response contains the following data: `[{name: "John"}, {name: "Mary"}]`
//then when resolved (e.g. in a view), `users` will contain a list of User instances
//i.e. users()[0].name() == "John"
</code></pre>
<hr>
<h3 id="unwrapping-response-data">Unwrapping Response Data</h3>
<p>Often, web services return the relevant data wrapped in objects that contain metadata.</p>
<p>Mithril allows you to unwrap the relevant data, by providing two callback hooks: <code>unwrapSuccess</code> and <code>unwrapError</code>.</p>
<p>These hooks allow you to unwrap different parts of the response data depending on whether it succeed or failed.</p>
<pre><code class="lang-javascript">var users = m.request({
method: "GET",
url: "/user",
unwrapSuccess: function(response) {
return response.data;
},
unwrapError: function(response) {
return response.error;
}
});
//assuming the response is: `{data: [{name: "John"}, {name: "Mary"}], count: 2}`
//then when resolved (e.g. in a view), the `users` getter-setter will contain a list of users
//i.e. users() //[{name: "John"}, {name: "Mary"}]
</code></pre>
<hr>
<h3 id="using-different-data-transfer-formats">Using Different Data Transfer Formats</h3>
<p>By default, <code>m.request</code> uses JSON to send and receive data to web services. You can override this by providing <code>serialize</code> and <code>deserialize</code> options:</p>
<pre><code class="lang-javascript">var users = m.request({
method: "GET",
url: "/user",
serialize: mySerializer,
deserialize: myDeserializer
});
</code></pre>
<p>One typical way to override this is to receive as-is responses. The example below shows how to receive a plain string from a txt file.</p>
<pre><code class="lang-javascript">var file = m.request({
method: "GET",
url: "myfile.txt",
deserialize: function(value) {return value;}
});
</code></pre>
<hr>
<h3 id="file-uploads-with-formdata">File uploads with FormData</h3>
<p>To use the HTML5 FormData object as the payload for a request, you need to override the <code>serialize</code> option. By default, <code>serialize</code> converts an object to JSON, but in the case of a FormData payload, you want to pass the object intact.</p>
<pre><code class="lang-javascript">//assume the file comes from an HTML5 drag-n-drop event
var file = e.dataTransfer.files[0]
var data = new FormData();
data.append("file", file)
m.request({
method: "POST",
url: "/upload",
serialize: function(data) {return data}
})
</code></pre>
<hr>
<h3 id="using-variable-data-formats">Using variable data formats</h3>
<p>By default, Mithril assumes both success and error responses are in JSON format, but some servers may not return JSON responses when returning HTTP error codes (e.g. 404)</p>
<p>You can get around this issue by using <code>extract</code></p>
<pre><code class="lang-javascript">var nonJsonErrors = function(xhr) {
return xhr.status > 200 ? JSON.stringify(xhr.responseText) : xhr.responseText
}
m.request({method: "GET", url: "/foo/bar.x", extract: nonJsonErrors})
.then(function(data) {}, function(error) {console.log(error)})
</code></pre>
<hr>
<h3 id="extracting-metadata-from-the-response">Extracting Metadata from the Response</h3>
<p>The <code>extract</code> method can be used to read metadata from HTTP response headers or the status field of an XMLHttpRequest.</p>
<pre><code class="lang-javascript">var extract = function(xhr, xhrOptions) {
if (xhrOptions.method == "HEAD") return xhr.getResponseHeader("x-item-count")
else return xhr.responseText
}
m.request({method: "POST", url: "/foo", extract: extract});
</code></pre>
<hr>
<h3 id="custom-request-rejections">Custom request rejections</h3>
<p>If you want to be able to handle a condition as an error in a promise rejection handler, you can throw an <code>Error</code> from <code>extract</code> to reject the promise.</p>
<p>This is useful, for example, if you received invalid JSON from the server in production and you want to display a message to the user saying that the server is offline.</p>
<pre><code class="lang-javascript">var extract = function(xhr, xhrOptions) {
try {
return JSON.stringify(xhr.responseText)
}
catch (e) {
//e instanceof SyntaxError == true
//by default `e` would be caught by Mithril's promise exception monitor and rethrown to the console
//this new error follows Promises/A+ specifications and triggers a rejection in the downstream promises without hitting the console.
throw new Error("Server is offline")
}
}
m.request({method: "POST", url: "/foo", extract: extract});
</code></pre>
<p>You can read more about the <a href="mithril.deferred.html#unchecked-error-handling">promise exception monitor here</a>.</p>
<hr>
<h3 id="configuring-the-underlying-xmlhttprequest">Configuring the underlying XMLHttpRequest</h3>
<p>The <code>config</code> option can be used to arbitrarily configure the native XMLHttpRequest instance and to access properties that would not be accessible otherwise.</p>
<p>The example below shows how to configure a request where the server expects requests to have a <code>Content-Type: application/json</code> header</p>
<pre><code class="lang-javascript">var xhrConfig = function(xhr) {
xhr.setRequestHeader("Content-Type", "application/json");
}
m.request({method: "POST", url: "/foo", config: xhrConfig});
</code></pre>
<hr>
<h3 id="aborting-a-request">Aborting a request</h3>
<p>The <code>config</code> option can also be used to retrieve the <code>XMLHttpRequest</code> instance for aborting the request. This idiom can also be used to attach <code>onprogress</code> event handlers.</p>
<pre><code class="lang-javascript">var transport = m.prop();
m.request({method: "POST", url: "/foo", config: transport});
//the `transport` getter-setter contains an instance of XMLHttpRequest
transport().abort();
</code></pre>
<hr>
<h3 id="using-json-p">Using JSON-P</h3>
<p>To make JSON-P requests, add the <code>dataType</code> option instead of <code>method</code>. You should not add the <code>callback</code> querystring parameter; Mithril already does that internally.</p>
<pre><code class="lang-javascript">m.request({dataType: "jsonp", url: "/api/User"});
</code></pre>
<p>Some services (e.g. Flickr) don't follow the convention of calling the <code>callback</code> parameter <code>callback</code>. In order to specify the name of the querystring parameter that indicates the callback function, use the <code>callbackKey</code> option:</p>
<pre><code class="lang-javascript">m.request({
dataType: "jsonp",
callbackKey: "jsoncallback",
url: "http://api.flickr.com/services/feeds/photos_public.gne?tags=monkey&tagmode=any&format=json"
});
</code></pre>
<hr>
<h3 id="rendering-before-web-service-requests-finish">Rendering before web service requests finish</h3>
<p>By default, Mithril waits for web service requests to complete before attempting a redraw. This ensures that data being accessed in the view isn't nullable as a result of asynchronous data not being available yet.</p>
<p>However, sometimes we do want to be able to redraw before a web service request completes, either because one web service out of many is slow, or because we don't need its response in order to redraw.</p>
<p>Setting the <code>background</code> option to <code>true</code> prevents a request from affecting redrawing. This means it's possible for a view to attempt to use data before it is available. You can specify an initial value for the <code>m.request</code> getter-setter in order to avoid having to write defensive code against potential null reference exceptions:</p>
<pre><code class="lang-javascript">var demo = {}
demo.controller = function() {
return {
users: m.request({method: "GET", url: "/api/user", background: true, initialValue: []})
}
}
//in the view
demo.view = function(ctrl) {
//This view gets rendered before the request above completes
//Calling .map doesn't throw an error because we defined the initial value to be an empty array, instead of undefined
return ctrl.users().map(function() {
return m("div", user.name)
})
}
</code></pre>
<hr>
<h3 id="signature">Signature</h3>
<p><a href="how-to-read-signatures.html">How to read signatures</a></p>
<pre><code class="lang-clike">Promise request(Options options)
where:
Promise :: GetterSetter { Promise then(any successCallback(any value), any errorCallback(any value)) }
GetterSetter :: any getterSetter([any value])
Options :: XHROptions | JSONPOptions
XHROptions :: Object {
String method,
String url,
[String user,]
[String password,]
[Object<any> data,]
[Boolean background,]
[any initialValue,]
[any unwrapSuccess(any data, XMLHttpRequest xhr),]
[any unwrapError(any data, XMLHttpRequest xhr),]
[String serialize(any dataToSerialize),]
[any deserialize(String dataToDeserialize),]
[any extract(XMLHttpRequest xhr, XHROptions options),]
[void type(Object<any> data),]
[XMLHttpRequest? config(XMLHttpRequest xhr, XHROptions options)]
}
JSONPOptions :: Object {
String dataType,
String url,
String callbackKey,
Object<any> data
}
</code></pre>
<ul>
<li><p><strong>XHROptions options</strong></p>
<p>A map of options for the XMLHttpRequest</p>
<ul>
<li><p><strong>String method</strong></p>
<p>The HTTP method. Must be either <code>"GET"</code>, <code>"POST"</code>, <code>"PUT"</code>, <code>"DELETE"</code>, <code>"HEAD"</code> or <code>"OPTIONS"</code></p>
</li>
<li><p><strong>String url</strong></p>
<p>The URL to request. If the URL is not in the same domain as the application, the target server must be configured to accept cross-domain requests from the application's domain, i.e. its responses must include the header <code>Access-Control-Allow-Origin: *</code>.</p>
</li>
<li><p><strong>String user</strong> (optional)</p>
<p>A user for HTTP authentication. Defaults to <code>undefined</code></p>
</li>
<li><p><strong>String password</strong> (optional)</p>
<p>A password for HTTP authentication. Defaults to <code>undefined</code></p>
</li>
<li><p><strong>Object<any> data</strong> (optional)</p>
<p>Data to be sent. It's automatically placed in the appropriate section of the request with the appropriate serialization based on <code>method</code></p>
</li>
<li><p><strong>Boolean background</strong> (optional)</p>
<p>Determines whether the <code>m.request</code> can affect template rendering. Defaults to false.</p>
<p>If this option is set to true, then the request does NOT call <a href="mithril.computation.html"><code>m.startComputation</code> / <code>m.endComputation</code></a> internally, and therefore the completion of the request does not trigger an update of the view, even if data has been changed. This option is useful for running operations in the background (i.e. without user intervention).</p>
<p>In order to force a redraw after a background request, use <a href="mithril.redraw.html"><code>m.redraw</code></a>, or <code>m.startComputation</code> / <code>m.endComputation</code>.</p>
<pre><code class="lang-javascript">var demo = {}
demo.controller = function() {
var users = m.request({method: "GET", url: "/api/users", background: true, initialValue: []})
users.then(m.redraw)
return {users: users}
}
demo.view = function(ctrl) {
//this view renders twice (once immediately, and once after the request above completes)
return m("div", [
ctrl.users().map(function(user) {
return m("div", user.name)
})
])
}
</code></pre>
<p>It's strongly recommended that you set an <code>initialValue</code> option in ALL requests if you set the <code>background</code> option to true.</p>
<p>When calling multiple background AJAX requests, it's recommended that you use <a href="mithril.sync.html"><code>m.sync</code></a> to batch redraw once at the end of all requests, as opposed to repeatedly redrawing after every request:</p>
<pre><code class="lang-javascript">var demo = {}
demo.controller = function() {
var users = m.request({method: "GET", url: "/api/users", background: true, initialValue: []})
var projects = m.request({method: "GET", url: "/api/projects", background: true, initialValue: []})
m.sync([users, projects]).then(m.redraw)
return {users: users, projects: projects}
}
</code></pre>
<p>Make sure to add null checks if your request value can be null</p>
<pre><code class="lang-javascript">var demo = {}
demo.controller = function() {
var user = m.request({method: "GET", url: "/api/users/1", background: true, initialValue: null})
user.then(m.redraw)
return {user: user}
}
demo.view = function(ctrl) {
return m("div", [
//in the first redraw, there's no user, so ensure we don't throw an error
ctrl.user ? ctrl.user.name : "no user"
])
}
</code></pre>
</li>
<li><p><strong>any initialValue</strong> (optional)</p>
<p>The value that populates the returned getter-setter before the request completes. This is useful when using the <code>background</code> option, in order to avoid the need for null checks in views that may be attempting to access the returned getter-setter before the asynchronous request resolves.</p>
<p>It is strongly recommended that you always set this option to avoid future surprises.</p>
</li>
<li><p><strong>any unwrapSuccess(any data, XMLHttpRequest xhr)</strong> (optional)</p>
<p>A preprocessor function to unwrap the data from a success response in case the response contains metadata wrapping the data.</p>
<p>The default value (if this parameter is falsy) is the identity function <code>function(value) {return value}</code></p>
<p>For example, if the response is <code>{data: [{name: "John"}, {name: "Mary"}]}</code> and the unwrap function is <code>function(response) {return response.data}</code>, then the response will be considered to be <code>[{name: "John"}, {name: "Mary"}]</code> when processing the <code>type</code> parameter</p>
<ul>
<li><p><strong>Object<any> | Array<any> data</strong></p>
<p>The data to unwrap</p>
</li>
<li><p><strong>returns Object<any> | Array<any> unwrappedData</strong></p>
<p>The unwrapped data</p>
</li>
</ul>
</li>
<li><p><strong>any unwrapError(any data, XMLHttpRequest xhr)</strong> (optional)</p>
<p>A preprocessor function to unwrap the data from an error response in case the response contains metadata wrapping the data.</p>
<p>The default value (if this parameter is falsy) is the identity function <code>function(value) {return value}</code></p>
<ul>
<li><p><strong>Object<any> | Array<any> data</strong></p>
<p>The data to unwrap</p>
</li>
<li><p><strong>returns Object<any> | Array<any> unwrappedData</strong></p>
<p>The unwrapped data</p>
</li>
</ul>
</li>
<li><p><strong>String serialize(any dataToSerialize)</strong> (optional)</p>
<p>Method to use to serialize the request data</p>
<p>The default value (if this parameter is falsy) is <code>JSON.stringify</code></p>
<ul>
<li><p><strong>any dataToSerialize</strong></p>
<p>Data to be serialized</p>
</li>
<li><p><strong>returns String serializedData</strong></p>
</li>
</ul>
</li>
<li><p><strong>any deserialize(String dataToDeserialize)</strong> (optional)</p>
<p>Method to use to deserialize the response data</p>
<p>The default value (if this parameter is falsy) is <code>JSON.parse</code></p>
<ul>
<li><p><strong>String dataToDeserialize</strong></p>
<p>Data to be deserialized</p>
</li>
<li><p><strong>returns any deserializedData</strong></p>
</li>
</ul>
</li>
<li><p><strong>any extract(XMLHttpRequest xhr, XHROptions options)</strong> (optional)</p>
<p>Method to use to extract the data from the raw XMLHttpRequest. This is useful when the relevant data is either in a response header or the status field.</p>
<p>If this parameter is falsy, the default value is a function that returns <code>xhr.responseText</code>.</p>
</li>
<li><p><strong>void type(Object<any> data)</strong> (optional)</p>
<p>The response object (or the child items if this object is an Array) will be passed as a parameter to the class constructor defined by <code>type</code></p>
<p>If this parameter is falsy, the deserialized data will not be wrapped.</p>
<p>For example, if <code>type</code> is the following class:</p>
<pre><code class="lang-javascript">var User = function(data) {
this.name = m.prop(data.name);
}
</code></pre>
<p>And the data is <code>[{name: "John"}, {name: "Mary"}]</code>, then the response will contain an array of two User instances.</p>
</li>
<li><p><strong>XMLHttpRequest? config(XMLHttpRequest xhr, XHROptions options)</strong> (optional)</p>
<p>An initialization function that runs after <code>open</code> and before <code>send</code>. Useful for adding request headers and when using XHR2 features, such as the XMLHttpRequest's <code>upload</code> property.</p>
<ul>
<li><p><strong>XMLHttpRequest xhr</strong></p>
<p>The XMLHttpRequest instance.</p>
</li>
<li><p><strong>XHROptions options</strong></p>
<p>The <code>options</code> parameter that was passed into <code>m.request</code> call</p>
</li>
<li><p><strong>returns XMLHttpRequest? xhr</strong></p>
<p>You may return an XHR-like object (e.g. a XDomainRequest instance) to override the provided XHR instance altogether.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>returns Promise promise</strong></p>
<p>returns a promise that can bind callbacks which get called on completion of the AJAX request.</p>
</li>
</ul>
<hr>
<ul>
<li><p><strong>JSONPOptions options</strong></p>
<p>A map of options for JSONP requests</p>
<ul>
<li><p><strong>String dataType</strong></p>
<p>Must be the string "jsonp"</p>
</li>
<li><p><strong>String url</strong></p>
<p>The URL to request. If the URL is not in the same domain as the application, the target server must be configured to accept cross-domain requests from the application's domain, i.e. its responses must include the header <code>Access-Control-Allow-Origin: *</code>.</p>
</li>
<li><p><strong>String callbackKey</strong></p>
<p>The name of the querystring key that defines the name of the callback function to be called by the response. Defaults to "callback"</p>
<p>This option is useful for web services that use uncommon conventions for defining jsonp callbacks (e.g. foo.com/?jsonpCallback=doSomething)</p>
</li>
<li><p><strong>Object<any> data</strong> (optional)</p>
<p>Data to be sent. It's automatically placed in the appropriate section of the request with the appropriate serialization based on <code>method</code></p>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
Released under the <a href="http://opensource.org/licenses/MIT" target="_blank">MIT license</a>
<br />© 2014 Leo Horie
</div>
</footer>
<script src="lib/prism/prism.js"></script>
</body>
</html>