From 12c546924cf57d1a10f2e0198d6f7f21bffe2427 Mon Sep 17 00:00:00 2001 From: nickolasgregory Date: Mon, 15 Sep 2014 19:54:02 +1000 Subject: [PATCH 1/6] Update practices.md --- docs/practices.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/practices.md b/docs/practices.md index 375372cd..996cac9c 100644 --- a/docs/practices.md +++ b/docs/practices.md @@ -95,7 +95,7 @@ In the unlikely case that you have another global variable called `m` in your pa Calling this method while using `m.module` or `m.route` should only be done if you have recurring asynchronous view updates (i.e. something that uses setInterval). -If you're integrating other non-recurring services (e.g. calling setTimeout), you should use [`m.startComputation` / `m.emdComputation`](mithril.computation.md) instead. +If you're integrating other non-recurring services (e.g. calling setTimeout), you should use [`m.startComputation` / `m.endComputation`](mithril.computation.md) instead. This is the most potentially expensive method in Mithril and should not be used at a rate faster than the rate at which the native `requestAnimationFrame` method fires (i.e. the rate at which browsers are comfortable calling recurring rendering-intensive code). Typically, this rate is around 60 calls per second. From 12aa447755f88bf7d1a266de60c516471d84a796 Mon Sep 17 00:00:00 2001 From: Barney Date: Mon, 10 Nov 2014 16:25:19 +0000 Subject: [PATCH 2/6] Disable versioning of auto-generated archive files by default Use `git add archive -f` to explicitly add them in --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b512c09d..aeab5a02 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules \ No newline at end of file +node_modules +archive From f75426c8e40a6b964c0fdef6a0f563a9b1765df0 Mon Sep 17 00:00:00 2001 From: Barney Date: Mon, 10 Nov 2014 16:29:52 +0000 Subject: [PATCH 3/6] Reverting docs --- docs/mithril.request.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/mithril.request.md b/docs/mithril.request.md index 8543fff4..be435f21 100644 --- a/docs/mithril.request.md +++ b/docs/mithril.request.md @@ -457,8 +457,6 @@ where: m.request({method: "GET", url: "/foo", background: true}) .then(m.redraw); //force redraw ``` - - The return value of a background `m.request` should not be exposed to controllers or views, since without the blocking effect of redraw-blocking effect of `m.startComputation` it is liable to be invoked before it resolves, which can lead to runtime errors. - **any unwrapSuccess(any data)** (optional) From f6a5436269247715901624df016ddf4a84f2b2bb Mon Sep 17 00:00:00 2001 From: Barney Date: Wed, 26 Nov 2014 15:12:15 +0000 Subject: [PATCH 4/6] Throw errors when user attempts to query route before it is initialized --- mithril.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mithril.js b/mithril.js index 91348bb4..64c9ef55 100644 --- a/mithril.js +++ b/mithril.js @@ -547,7 +547,9 @@ Mithril = m = new function app(window, undefined) { var redirect = function() {}, routeParams = {}, currentRoute m.route = function() { //m.route() - if (arguments.length === 0) return currentRoute + if (arguments.length === 0) { + return checkRouteInit() && currentRoute + } else if (arguments.length === 3 && type.call(arguments[1]) == sStr) { var root = arguments[0], defaultRoute = arguments[1], router = arguments[2] redirect = function(source) { @@ -592,7 +594,9 @@ Mithril = m = new function app(window, undefined) { else window.location[m.route.mode] = currentRoute } } - m.route.param = function(key) {return routeParams[key]} + m.route.param = function(key) { + return checkRouteInit() && routeParams[key] + } m.route.mode = "search" function normalizeRoute(route) {return route.slice(modes[m.route.mode].length)} function routeByValue(root, router, path) { @@ -655,6 +659,9 @@ Mithril = m = new function app(window, undefined) { function decodeSpace(string) { return decodeURIComponent(string.replace(/\+/g, " ")) } + function checkRouteInit(){ + return currentRoute == null || throw "Attempted to query m.route, but m.route was not initialized"; + } function reset(root) { var cacheKey = getCellCacheKey(root) clear(root.childNodes, cellCache[cacheKey]) From 18dd27a4936096df0221f43b8162f43a9974dbe6 Mon Sep 17 00:00:00 2001 From: Barney Carroll Date: Tue, 6 Jan 2015 17:34:55 +0000 Subject: [PATCH 5/6] Removed `decodeSpace` function: fixes #399 --- mithril.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mithril.js b/mithril.js index 867d4ed7..29fe3616 100644 --- a/mithril.js +++ b/mithril.js @@ -674,13 +674,10 @@ var m = (function app(window, undefined) { var pairs = str.split("&"), params = {}; for (var i = 0, len = pairs.length; i < len; i++) { var pair = pairs[i].split("="); - params[decodeSpace(pair[0])] = pair[1] ? decodeSpace(pair[1]) : "" + params[decodeURIComponent(pair[0])] = pair[1] ? decodeURIComponent(pair[1]) : "" } return params } - function decodeSpace(string) { - return decodeURIComponent(string.replace(/\+/g, " ")) - } function reset(root) { var cacheKey = getCellCacheKey(root); clear(root.childNodes, cellCache[cacheKey]); From c92d9f75518d39bc72d14161aa4e98e95a292c28 Mon Sep 17 00:00:00 2001 From: Barney Carroll Date: Tue, 6 Jan 2015 23:17:31 +0000 Subject: [PATCH 6/6] Merge in upstream docs --- docs/mithril.request.md | 57 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/docs/mithril.request.md b/docs/mithril.request.md index be435f21..3d4aa162 100644 --- a/docs/mithril.request.md +++ b/docs/mithril.request.md @@ -17,6 +17,7 @@ - [Configuring the underlying XMLHttpRequest](#configuring-the-underlying-xmlhttprequest) - [Aborting a request](#aborting-a-request) - [Using JSON-P](#using-json-p) +- [Rendering before web service requests finish](#rendering-before-web-service-requests-finish) - [Signature](#signature) --- @@ -383,6 +384,35 @@ m.request({ --- +### Rendering before web service requests finish + +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. + +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. + +Setting the `background` option to `true` 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 `m.request` getter-setter in order to avoid having to write defensive code against potential null reference exceptions: + +```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) + }) +} +``` + +--- + ### Signature [How to read signatures](how-to-read-signatures.md) @@ -401,6 +431,7 @@ where: [String password,] [Object data,] [Boolean background,] + [any initialValue,] [any unwrapSuccess(any data),] [any unwrapError(any data),] [String serialize(any dataToSerialize),] @@ -454,10 +485,32 @@ where: In order to force a redraw after a background request, use [`m.redraw`](mithril.redraw.md) ```javascript - m.request({method: "GET", url: "/foo", background: true}) - .then(m.redraw); //force redraw + var demo = {} + + demo.controller = function() { + return { + users: m.request({method: "GET", url: "/api/user", background: true, initialValue: []}).then(function(value) { + //force redraw + m.redraw() + return value + }) + } + } + + demo.view = function(ctrl) { + //this view renders twice (once immediately, and once after the request above completes) + return ctrl.users.map(function(user) { + return m("div", user.name) + }) + } ``` + It's recommended that you always set an `initialValue` when setting the `background` option to true. + + - **any initialValue** (optional) + + The value that populates the returned getter-setter before the request completes. This is useful when using the `background` 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. + - **any unwrapSuccess(any data)** (optional) A preprocessor function to unwrap the data from a success response in case the response contains metadata wrapping the data.