Skip to content

Instantly share code, notes, and snippets.

@nickiaconis
Last active September 29, 2015 22:22
Show Gist options
  • Save nickiaconis/9ab92bdc7bcd0f590635 to your computer and use it in GitHub Desktop.
Save nickiaconis/9ab92bdc7bcd0f590635 to your computer and use it in GitHub Desktop.
Makes Ember's Model Hooks Run Synchronously
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);
},
/**
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