Last active
September 29, 2015 22:22
-
-
Save nickiaconis/9ab92bdc7bcd0f590635 to your computer and use it in GitHub Desktop.
Makes Ember's Model Hooks Run Synchronously
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/packages/ember-routing/lib/system/route.js b/packages/ember-routing/lib/system/route.js | |
index 1a2ddf7..98b9e0d 100644 | |
--- a/packages/ember-routing/lib/system/route.js | |
+++ b/packages/ember-routing/lib/system/route.js | |
@@ -29,6 +29,7 @@ import { | |
normalizeControllerQueryParams, | |
calculateCacheKey | |
} from 'ember-routing/utils'; | |
+import RSVP from 'rsvp'; | |
var slice = Array.prototype.slice; | |
@@ -1472,9 +1473,9 @@ var Route = EmberObject.extend(ActionHandler, Evented, { | |
if (!name && sawParams) { | |
return copy(params); | |
} else if (!name) { | |
- if (transition.resolveIndex < 1) { return; } | |
+ if (transition.requestIndex < 2) { return; } | |
- var parentModel = transition.state.handlerInfos[transition.resolveIndex - 1].context; | |
+ var parentModel = transition.state.handlerInfos[transition.requestIndex - 2].modelPromise; | |
return parentModel; | |
} | |
@@ -1776,7 +1777,7 @@ var Route = EmberObject.extend(ActionHandler, Evented, { | |
} | |
} | |
- return route && route.currentModel; | |
+ return route && RSVP.Promise.resolve(route.currentModel); | |
}, | |
/** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/lib/router/handler-info.js b/lib/router/handler-info.js | |
index 9819324..ae389f5 100644 | |
--- a/lib/router/handler-info.js | |
+++ b/lib/router/handler-info.js | |
@@ -36,11 +36,26 @@ HandlerInfo.prototype = { | |
return this.params || {}; | |
}, | |
+ request: function(payload) { | |
+ var beforeModel = bind(this, this.runBeforeModelHook, payload), | |
+ model = bind(this, this.getModel, payload), | |
+ afterModel = bind(this, this.runAfterModelHook, payload); | |
+ | |
+ this.beforeModelPromise = beforeModel(); | |
+ this.modelPromise = model(); | |
+ this.afterModelPromise(this.modelPromise); | |
+ }, | |
+ | |
resolve: function(shouldContinue, payload) { | |
var checkForAbort = bind(this, this.checkForAbort, shouldContinue), | |
- beforeModel = bind(this, this.runBeforeModelHook, payload), | |
- model = bind(this, this.getModel, payload), | |
- afterModel = bind(this, this.runAfterModelHook, payload), | |
+ beforeModel = bind(this, function() { | |
+ if (payload.trigger) { | |
+ payload.trigger(true, 'willResolveModel', payload, this.handler); | |
+ } | |
+ return this.beforeModelPromise; | |
+ }), | |
+ model = bind(this, function() { return this.modelPromise; }), | |
+ afterModel = bind(this, function() { return this.afterModelPromise; }), | |
becomeResolved = bind(this, this.becomeResolved, payload); | |
return Promise.resolve(undefined, this.promiseLabel("Start handler")) | |
@@ -55,20 +70,17 @@ HandlerInfo.prototype = { | |
}, | |
runBeforeModelHook: function(payload) { | |
- if (payload.trigger) { | |
- payload.trigger(true, 'willResolveModel', payload, this.handler); | |
- } | |
return this.runSharedModelHook(payload, 'beforeModel', []); | |
}, | |
- runAfterModelHook: function(payload, resolvedModel) { | |
+ runAfterModelHook: function(payload, modelPromise) { | |
// Stash the resolved model on the payload. | |
// This makes it possible for users to swap out | |
// the resolved model in afterModel. | |
var name = this.name; | |
- this.stashResolvedModel(payload, resolvedModel); | |
+ this.stashResolvedModel(payload, modelPromise); | |
- return this.runSharedModelHook(payload, 'afterModel', [resolvedModel]) | |
+ return this.runSharedModelHook(payload, 'afterModel', [modelPromise]) | |
.then(function() { | |
// Ignore the fulfilled value returned from afterModel. | |
// Return the value stashed in resolvedModels, which | |
diff --git a/lib/router/transition-state.js b/lib/router/transition-state.js | |
index 6aef122..27357ad 100644 | |
--- a/lib/router/transition-state.js | |
+++ b/lib/router/transition-state.js | |
@@ -34,6 +34,7 @@ TransitionState.prototype = { | |
}); | |
payload = payload || {}; | |
+ payload.requestIndex = 0; | |
payload.resolveIndex = 0; | |
var currentState = this; | |
@@ -41,6 +42,7 @@ TransitionState.prototype = { | |
// The prelude RSVP.resolve() asyncs us into the promise land. | |
return Promise.resolve(null, this.promiseLabel("Start transition")) | |
+ .then(requestOneHandlerInfo, null, this.promiseLabel('Request handler')) | |
.then(resolveOneHandlerInfo, null, this.promiseLabel('Resolve handler'))['catch'](handleError, this.promiseLabel('Handle error')); | |
function innerShouldContinue() { | |
@@ -88,6 +90,18 @@ TransitionState.prototype = { | |
return innerShouldContinue().then(resolveOneHandlerInfo, null, currentState.promiseLabel('Resolve handler')); | |
} | |
+ function requestOneHandlerInfo() { | |
+ if (payload.requestIndex === currentState.handlerInfos.length) { | |
+ // Exit condition | |
+ return Promise.resolve(null, currentState.promiseLabel('Finished requests')); | |
+ } | |
+ | |
+ var handlerInfo = currentState.handlerInfos[payload.requestIndex++]; | |
+ handlerInfo.request(payload); | |
+ | |
+ return requestOneHandlerInfo(); | |
+ } | |
+ | |
function resolveOneHandlerInfo() { | |
if (payload.resolveIndex === currentState.handlerInfos.length) { | |
// This is is the only possible | |
diff --git a/lib/router/transition.js b/lib/router/transition.js | |
index 53724c7..2cd5cc4 100644 | |
--- a/lib/router/transition.js | |
+++ b/lib/router/transition.js | |
@@ -80,6 +80,7 @@ Transition.prototype = { | |
intent: null, | |
params: null, | |
pivotHandler: null, | |
+ requestIndex: 0, | |
resolveIndex: 0, | |
handlerInfos: null, | |
resolvedModels: null, | |
@@ -120,7 +121,7 @@ Transition.prototype = { | |
hook and shared with a later hook. Properties set on `data` will | |
be copied to new transitions generated by calling `retry` on this | |
transition. | |
- | |
+ | |
@property data | |
@type {Object} | |
@public |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment