Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/hooks/lazyLoad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ const lazyLoadHook: TransitionHookFn = (transition: Transition) => {

// No matching state found, so let .sync() choose the best non-state match/otherwise
router.urlService.sync();

// If the successful lazy load replaced the target state (e.g., a `.**` placeholder)
// in the registry, abort the current transition; otherwise it continues towards the
// deregistered placeholder and supersedes the transition started by sync().
if (!router.stateRegistry.get(transition.to().name)) {
return false;
}
}

const promises = transition
Expand Down
54 changes: 53 additions & 1 deletion test/lazyLoadSpec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UIRouter, TransitionService, StateService } from '../src/index';
import { UIRouter, TransitionService, StateService, RejectType, Rejection } from '../src/index';

import { StateRegistry } from '../src/state/stateRegistry';
import { UrlRouter } from '../src/url/urlRouter';
Expand Down Expand Up @@ -298,6 +298,58 @@ describe('future state', function() {
});
});

describe('triggered by a URL sync that the lazy loaded states do not match', () => {
let lazyLoadCount: number;

beforeEach(() => {
lazyLoadCount = 0;
$state.defaultErrorHandler(() => {});
$registry.register({ name: 'notFound' });
$registry.register({
name: 'A.**',
url: '/a',
lazyLoad: () => {
lazyLoadCount++;
return Promise.resolve({ states: [{ name: 'A', url: '/a/:id' }] });
},
});
});

// Test for https://github.com/ui-router/core/issues/850
it('should activate the otherwise() rule instead of the deregistered placeholder', (done) => {
router.urlService.rules.otherwise({ state: 'notFound' });

router.urlService.url('/a/no/such/page');
router.urlService.sync();

$transitions.onSuccess({}, () => {
expect(lazyLoadCount).toBe(1);
expect($state.current.name).toBe('notFound');
expect($registry.get('A.**')).toBeFalsy();
done();
});
});

it('should abort the original transition to the deregistered placeholder', (done) => {
router.urlService.rules.otherwise({ state: 'notFound' });
const errors: { targetName: string; error: Rejection }[] = [];
$transitions.onError({}, (trans) => {
errors.push({ targetName: trans.targetState().name(), error: trans.error() });
});

// The url change is synced by the url listener
router.urlService.url('/a/no/such/page');

$transitions.onSuccess({}, () => {
expect(errors.length).toBe(1);
expect(errors[0].targetName).toBe('A.**');
expect(errors[0].error.type).toBe(RejectType.ABORTED);
expect($state.current.name).toBe('notFound');
done();
});
});
});

describe('that resolves to multiple states', () => {
let futureA, futureB;
const lazyA = { name: 'A', url: '/a/:id', params: { id: 'default' } };
Expand Down