-
Notifications
You must be signed in to change notification settings - Fork 159
Commit
This has been broken forever AFAIK, and is surprising to a lot of people. In fact, even the ember guides recommend [using `this.transitionTo` for redirecting](https://guides.emberjs.com/v2.9.0/routing/redirection/) This is in fact broken. If you use `transitionTo` as a redirect strategy, then you get an extra history entry if you hit the route as the first route in your app. This breaks the back button, as the back button takes you to the route with the redirect, then immediately redirects back to the page you're on. Maybe you can go back if you hammer back button really quickly, but you have to hammer it loads super quick. Not a good UX. `replaceWith` works if you use it to redirect from a route that's your initial transition. However if you use it to redirect and you hit that route from some way _after_ the initial app load, then at the point that the replaceWith is called, you are still on the same URL. For example, if you are on `/` and you click a link to `/foo`, which in it's model hook redirects to `/bar` using `replaceWith`, at the time replaceWith is called, your current url is `/`. This means `/` entry gets removed from your history entirely. Clicking back will take you back to whatever page you were on before `/`, which often isn't event your app, maybe it's google or something. This breaks the back button again. This commit should do the correct thing in all cases, allowing replaceWith and transitionTo outside of redirects as specified by the developer but only allowing transitionTo or replaceWith in redirects in a way that doesn't break the back button.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ import { trigger, slice, log, promiseLabel } from './utils'; | |
@param {Object} error | ||
@private | ||
*/ | ||
function Transition(router, intent, state, error) { | ||
function Transition(router, intent, state, error, previousTransition) { | ||
var transition = this; | ||
this.state = state || router.state; | ||
this.intent = intent; | ||
|
@@ -40,6 +40,18 @@ function Transition(router, intent, state, error) { | |
return; | ||
} | ||
|
||
// if you're doing multiple redirects, need the new transition to know if it | ||
// is actually part of the first transition or not. Any further redirects | ||
// in the initial transition also need to know if they are part of the | ||
// initial transition | ||
this.isCausedByAbortingTransition = !!previousTransition; | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
alanwguo
|
||
this.isCausedByInitialTransition = ( | ||
previousTransition && ( | ||
previousTransition.isCausedByInitialTransition || | ||
previousTransition.sequence === 0 | ||
) | ||
); | ||
|
||
if (state) { | ||
this.params = state.params; | ||
this.queryParams = state.queryParams; | ||
|
@@ -58,16 +70,9 @@ function Transition(router, intent, state, error) { | |
this.pivotHandler = handlerInfo.handler; | ||
} | ||
|
||
this.sequence = Transition.currentSequence++; | ||
this.promise = state.resolve(checkForAbort, this)['catch'](function(result) { | ||
if (result.wasAborted || transition.isAborted) { | ||
return Promise.reject(logAbort(transition)); | ||
} else { | ||
transition.trigger('error', result.error, transition, result.handlerWithError); | ||
transition.abort(); | ||
return Promise.reject(result.error); | ||
} | ||
}, promiseLabel('Handle Abort')); | ||
this.sequence = router.currentSequence++; | ||
this.promise = state.resolve(checkForAbort, this)['catch']( | ||
catchHandlerForTransition(transition), promiseLabel('Handle Abort')); | ||
} else { | ||
this.promise = Promise.resolve(this.state); | ||
this.params = {}; | ||
|
@@ -80,7 +85,18 @@ function Transition(router, intent, state, error) { | |
} | ||
} | ||
|
||
Transition.currentSequence = 0; | ||
function catchHandlerForTransition(transition) { | ||
return function(result) { | ||
if (result.wasAborted || transition.isAborted) { | ||
return Promise.reject(logAbort(transition)); | ||
} else { | ||
transition.trigger('error', result.error, transition, result.handlerWithError); | ||
transition.abort(); | ||
return Promise.reject(result.error); | ||
} | ||
}; | ||
} | ||
|
||
|
||
Transition.prototype = { | ||
targetName: null, | ||
|
@alexspeller, I have an issue here:
I have a case where I do a replaceWith to the same route with different queryParams.
The route has the queryParams configuration
{ replace: true }
.The queryParamsDidChange method calls
this.refresh()
during the replaceWith transition which aborts the previous transition. Becausethis.isCausedByAbortingTransition
is true, however, we no longer respect the originalreplaceWith
and now do anupdateUrl
.