Skip to content

Commit

Permalink
feat(router): add activate and deactivate events to RouterOutlet
Browse files Browse the repository at this point in the history
  • Loading branch information
vsavkin committed Jul 8, 2016
1 parent a77db44 commit 245b091
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 2 deletions.
22 changes: 20 additions & 2 deletions modules/@angular/router/src/directives/router_outlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,34 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Attribute, ComponentFactory, ComponentFactoryResolver, ComponentRef, Directive, NoComponentFactoryError, ReflectiveInjector, ResolvedReflectiveProvider, ViewContainerRef} from '@angular/core';
import {Attribute, ComponentFactory, ComponentFactoryResolver, ComponentRef, Directive, EventEmitter, NoComponentFactoryError, Output, ReflectiveInjector, ResolvedReflectiveProvider, ViewContainerRef} from '@angular/core';

import {RouterOutletMap} from '../router_outlet_map';
import {ActivatedRoute} from '../router_state';
import {PRIMARY_OUTLET} from '../shared';


/**
* A router outlet is a placeholder that Angular dynamically fills based on the application's route.
*
* ## Use
* ## Example
*
* ```
* <router-outlet></router-outlet>
* <router-outlet name="left"></router-outlet>
* <router-outlet name="right"></router-outlet>
* ```
*
* A router outlet will emit an activate event any time a new component is being instantiated,
* and a deactivate event when it is being destroyed.
*
* ## Example
*
* ```
* <router-outlet (activate)="onActivate($event)"
* (deactivate)="onDeactivate($event)"></router-outlet>
* ```
*
* @stable
*/
@Directive({selector: 'router-outlet'})
Expand All @@ -31,6 +42,9 @@ export class RouterOutlet {
private _activatedRoute: ActivatedRoute;
public outletMap: RouterOutletMap;

@Output('activate') activateEvents = new EventEmitter<any>();
@Output('deactivate') deactivateEvents = new EventEmitter<any>();

constructor(
parentOutletMap: RouterOutletMap, private location: ViewContainerRef,
private resolver: ComponentFactoryResolver, @Attribute('name') name: string) {
Expand All @@ -49,8 +63,10 @@ export class RouterOutlet {

deactivate(): void {
if (this.activated) {
const c = this.component;
this.activated.destroy();
this.activated = null;
this.deactivateEvents.emit(c);
}
}

Expand Down Expand Up @@ -86,5 +102,7 @@ export class RouterOutlet {
const inj = ReflectiveInjector.fromResolvedProviders(providers, this.location.parentInjector);
this.activated = this.location.createComponent(factory, this.location.length, inj, []);
this.activated.changeDetectorRef.detectChanges();

this.activateEvents.emit(this.activated.instance);
}
}
42 changes: 42 additions & 0 deletions modules/@angular/router/test/router.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,48 @@ describe('Integration', () => {
.toHaveText('primary {simple} right {user victor}');
})));

it('should emit an event when an outlet gets activated',
fakeAsync(inject(
[Router, TestComponentBuilder, Location],
(router: Router, tcb: TestComponentBuilder, location: Location) => {
@Component({
selector: 'container',
template:
`<router-outlet (activate)="recordActivate($event)" (deactivate)="recordDeactivate($event)"></router-outlet>`
})
class Container {
activations: any[] = [];
deactivations: any[] = [];

recordActivate(component: any): void { this.activations.push(component); }

recordDeactivate(component: any): void { this.deactivations.push(component); }
}

const fixture = createRoot(tcb, router, Container);
const cmp = fixture.debugElement.componentInstance;

router.resetConfig(
[{path: 'blank', component: BlankCmp}, {path: 'simple', component: SimpleCmp}]);

cmp.activations = [];
cmp.deactivations = [];

router.navigateByUrl('/blank');
advance(fixture);

expect(cmp.activations.length).toEqual(1);
expect(cmp.activations[0] instanceof BlankCmp).toBe(true);

router.navigateByUrl('/simple');
advance(fixture);

expect(cmp.activations.length).toEqual(2);
expect(cmp.activations[1] instanceof SimpleCmp).toBe(true);
expect(cmp.deactivations.length).toEqual(2);
expect(cmp.deactivations[1] instanceof BlankCmp).toBe(true);
})));

describe('data', () => {
class ResolveSix implements Resolve<TeamCmp> {
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): number { return 6; }
Expand Down
2 changes: 2 additions & 0 deletions tools/public_api_guard/router/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,10 @@ export declare class RouterModule {

/** @stable */
export declare class RouterOutlet {
activateEvents: EventEmitter<any>;
activatedRoute: ActivatedRoute;
component: Object;
deactivateEvents: EventEmitter<any>;
isActivated: boolean;
outletMap: RouterOutletMap;
constructor(parentOutletMap: RouterOutletMap, location: ViewContainerRef, resolver: ComponentFactoryResolver, name: string);
Expand Down

0 comments on commit 245b091

Please sign in to comment.