feat(router): add support for basic events
This commit is contained in:
parent
2717bcc3af
commit
88920bfee1
|
@ -1,4 +1,4 @@
|
|||
export { Router } from './router';
|
||||
export { Router, Event, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from './router';
|
||||
export { UrlSerializer, DefaultUrlSerializer } from './url_serializer';
|
||||
export { RouterState, ActivatedRoute, RouterStateSnapshot, ActivatedRouteSnapshot } from './router_state';
|
||||
export { UrlTree, UrlSegment} from './url_tree';
|
||||
|
|
|
@ -26,6 +26,12 @@ import {forkJoin} from 'rxjs/observable/forkJoin';
|
|||
|
||||
export interface NavigationExtras { relativeTo?: ActivatedRoute; queryParameters?: Params; fragment?: string; }
|
||||
|
||||
export class NavigationStart { constructor(public id:number, public url:UrlTree) {} }
|
||||
export class NavigationEnd { constructor(public id:number, public url:UrlTree) {} }
|
||||
export class NavigationCancel { constructor(public id:number, public url:UrlTree) {} }
|
||||
export class NavigationError { constructor(public id:number, public url:UrlTree, public error:any) {} }
|
||||
export type Event = NavigationStart | NavigationEnd | NavigationCancel | NavigationError;
|
||||
|
||||
/**
|
||||
* The `Router` is responsible for mapping URLs to components.
|
||||
*/
|
||||
|
@ -34,12 +40,14 @@ export class Router {
|
|||
private currentRouterState: RouterState;
|
||||
private config: RouterConfig;
|
||||
private locationSubscription: Subscription;
|
||||
private routerEvents: Subject<Event>;
|
||||
private navigationId: number = 0;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(private rootComponentType:Type, private resolver: ComponentResolver, private urlSerializer: UrlSerializer, private outletMap: RouterOutletMap, private location: Location, private injector: Injector) {
|
||||
this.routerEvents = new Subject<Event>();
|
||||
this.currentUrlTree = createEmptyUrlTree();
|
||||
this.currentRouterState = createEmptyState(rootComponentType);
|
||||
this.setUpLocationChangeListener();
|
||||
|
@ -60,6 +68,10 @@ export class Router {
|
|||
return this.currentUrlTree;
|
||||
}
|
||||
|
||||
get events(): Observable<Event> {
|
||||
return this.routerEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate based on the provided url. This navigation is always absolute.
|
||||
*
|
||||
|
@ -160,6 +172,7 @@ export class Router {
|
|||
|
||||
private scheduleNavigation(url: UrlTree, pop: boolean):Promise<boolean> {
|
||||
const id = ++ this.navigationId;
|
||||
this.routerEvents.next(new NavigationStart(id, url));
|
||||
return Promise.resolve().then((_) => this.runNavigate(url, false, id));
|
||||
}
|
||||
|
||||
|
@ -171,6 +184,7 @@ export class Router {
|
|||
|
||||
private runNavigate(url: UrlTree, pop: boolean, id: number):Promise<boolean> {
|
||||
if (id !== this.navigationId) {
|
||||
this.routerEvents.next(new NavigationCancel(id, url));
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
|
@ -190,7 +204,8 @@ export class Router {
|
|||
|
||||
}).forEach((shouldActivate) => {
|
||||
if (!shouldActivate || id !== this.navigationId) {
|
||||
return;
|
||||
this.routerEvents.next(new NavigationCancel(id, url));
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
new ActivateRoutes(state, this.currentRouterState).activate(this.outletMap);
|
||||
|
@ -200,7 +215,14 @@ export class Router {
|
|||
if (!pop) {
|
||||
this.location.go(this.urlSerializer.serialize(url));
|
||||
}
|
||||
}).then(() => resolvePromise(true), e => rejectPromise(e));
|
||||
}).then(() => {
|
||||
this.routerEvents.next(new NavigationEnd(id, url));
|
||||
resolvePromise(true);
|
||||
|
||||
}, e => {
|
||||
this.routerEvents.next(new NavigationError(id, url, e));
|
||||
rejectPromise(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing'
|
|||
import { ComponentResolver } from '@angular/core';
|
||||
import { SpyLocation } from '@angular/common/testing';
|
||||
import { UrlSerializer, DefaultUrlSerializer, RouterOutletMap, Router, ActivatedRoute, ROUTER_DIRECTIVES, Params,
|
||||
RouterStateSnapshot, ActivatedRouteSnapshot, CanActivate, CanDeactivate } from '../src/index';
|
||||
RouterStateSnapshot, ActivatedRouteSnapshot, CanActivate, CanDeactivate, Event, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '../src/index';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
|
@ -228,6 +228,9 @@ describe("Integration", () => {
|
|||
{ path: '/user/:name', component: UserCmp }
|
||||
]);
|
||||
|
||||
const recordedEvents = [];
|
||||
router.events.forEach(e => recordedEvents.push(e));
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
router.navigateByUrl('/user/init');
|
||||
advance(fixture);
|
||||
|
@ -244,6 +247,17 @@ describe("Integration", () => {
|
|||
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('user fedor');
|
||||
expect(user.recordedParams).toEqual([{name: 'init'}, {name: 'fedor'}]);
|
||||
|
||||
expectEvents(router, recordedEvents.slice(1), [
|
||||
[NavigationStart, '/user/init'],
|
||||
[NavigationEnd, '/user/init'],
|
||||
|
||||
[NavigationStart, '/user/victor'],
|
||||
[NavigationStart, '/user/fedor'],
|
||||
|
||||
[NavigationCancel, '/user/victor'],
|
||||
[NavigationEnd, '/user/fedor']
|
||||
]);
|
||||
})));
|
||||
|
||||
it("should handle failed navigations gracefully",
|
||||
|
@ -252,6 +266,9 @@ describe("Integration", () => {
|
|||
{ path: '/user/:name', component: UserCmp }
|
||||
]);
|
||||
|
||||
const recordedEvents = [];
|
||||
router.events.forEach(e => recordedEvents.push(e));
|
||||
|
||||
const fixture = tcb.createFakeAsync(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
|
@ -264,6 +281,13 @@ describe("Integration", () => {
|
|||
advance(fixture);
|
||||
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('user fedor');
|
||||
expectEvents(router, recordedEvents.slice(1), [
|
||||
[NavigationStart, '/invalid'],
|
||||
[NavigationError, '/invalid'],
|
||||
|
||||
[NavigationStart, '/user/fedor'],
|
||||
[NavigationEnd, '/user/fedor']
|
||||
]);
|
||||
})));
|
||||
|
||||
describe("router links", () => {
|
||||
|
@ -482,6 +506,13 @@ describe("Integration", () => {
|
|||
});
|
||||
});
|
||||
|
||||
function expectEvents(router: Router, events:Event[], pairs: any[]) {
|
||||
for (let i = 0; i < events.length; ++i) {
|
||||
expect((<any>events[i].constructor).name).toBe(pairs[i][0].name);
|
||||
expect(router.serializeUrl((<any>events[i]).url)).toBe(pairs[i][1]);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'link-cmp',
|
||||
template: `<a routerLink="/team/33/simple">link</a>`,
|
||||
|
|
Loading…
Reference in New Issue