From 8c2461df99a054ce66b5e8851f2a9ee8d3648b74 Mon Sep 17 00:00:00 2001 From: Chris Thielen Date: Wed, 4 Jan 2017 09:41:00 -0600 Subject: [PATCH] fix(lazyLoad): Use UrlService.match() to retry url sync after successful lazy load triggered by url Closes #19 --- src/hooks/lazyLoad.ts | 47 ++++++++++++++++++++----------------------- test/urlRouterSpec.ts | 23 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/hooks/lazyLoad.ts b/src/hooks/lazyLoad.ts index b5c7c2a6..51b1614d 100644 --- a/src/hooks/lazyLoad.ts +++ b/src/hooks/lazyLoad.ts @@ -5,6 +5,7 @@ import {TransitionHookFn} from "../transition/interface"; import {StateDeclaration, LazyLoadResult} from "../state/interface"; import {State} from "../state/stateObject"; import {services} from "../common/coreservices"; +import { StateRule } from "../url/interface"; /** * A [[TransitionHookFn]] that performs lazy loading @@ -33,36 +34,32 @@ import {services} from "../common/coreservices"; const lazyLoadHook: TransitionHookFn = (transition: Transition) => { const transitionSource = (trans: Transition) => trans.redirectedFrom() ? transitionSource(trans.redirectedFrom()) : trans.options().source; + let router = transition.router; function retryOriginalTransition() { - if (transitionSource(transition) === 'url') { - let $loc = transition.router.urlService, - $reg = transition.router.stateRegistry, - path = $loc.path(), - search = $loc.search(), - hash = $loc.hash(); - - let matchState = state => - [state, state.url && state.url.exec(path, search, hash)]; - - let matches = $reg.get() - .map(s => s.$$state()) - .map(matchState) - .filter(([state, params]) => !!params); - - if (matches.length) { - let [state, params] = matches[0]; - return transition.router.stateService.target(state, params, transition.options()); - } + if (transitionSource(transition) !== 'url') { + // The original transition was not triggered via url sync + // The lazy state should be loaded now, so re-try the original transition + let orig = transition.targetState(); + return router.stateService.target(orig.identifier(), orig.params(), orig.options()); + } - transition.router.urlRouter.sync(); - return; + // The original transition was triggered via url sync + // Run the URL rules and find the best match + let $url = router.urlService; + let result = $url.match($url.parts()); + let rule = result && result.rule; + + // If the best match is a state, redirect the transition (instead + // of calling sync() which supersedes the current transition) + if (rule && rule.type === "STATE") { + let state = (rule as StateRule).state; + let params = result.match; + return router.stateService.target(state, params, transition.options()); } - // The original transition was not triggered via url sync - // The lazy state should be loaded now, so re-try the original transition - let orig = transition.targetState(); - return transition.router.stateService.target(orig.identifier(), orig.params(), orig.options()); + // No matching state found, so let .sync() choose the best non-state match/otherwise + router.urlRouter.sync(); } let promises = transition.entering() diff --git a/test/urlRouterSpec.ts b/test/urlRouterSpec.ts index 269520de..9272477e 100644 --- a/test/urlRouterSpec.ts +++ b/test/urlRouterSpec.ts @@ -300,6 +300,29 @@ describe("UrlRouter", function () { expect(match.rule).toBe(CCC); }); }); + + describe('lazy loaded state url', () => { + it("should obey rule priority ordering", (done) => { + let registry = router.stateRegistry; + let loadedState; + const lazyLoad = () => { + loadedState = registry.register({ name: 'lazy', url: '/lazy' }); + return null; + }; + + registry.register({ name: 'lazy.**', url: '/lazy', lazyLoad: lazyLoad }); + registry.register({ name: 'param', url: '/:param', }); + + router.transitionService.onSuccess({}, trans => { + expect(trans.$to()).toBe(loadedState); + expect(trans.redirectedFrom().to().name).toBe('lazy.**'); + + done(); + }); + + router.urlService.url('/lazy'); + }) + }) }); describe('UrlRouter.deferIntercept', () => {