fix(router): make an outlet to unregister itself when it is removed from the dom
This commit is contained in:
parent
8dc82a0080
commit
3e377f520e
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Attribute, ComponentFactory, ComponentFactoryResolver, ComponentRef, Directive, EventEmitter, Injector, NoComponentFactoryError, Output, ReflectiveInjector, ResolvedReflectiveProvider, ViewContainerRef} from '@angular/core';
|
import {Attribute, ComponentFactory, ComponentFactoryResolver, ComponentRef, Directive, EventEmitter, Injector, NoComponentFactoryError, OnDestroy, Output, ReflectiveInjector, ResolvedReflectiveProvider, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
import {RouterOutletMap} from '../router_outlet_map';
|
import {RouterOutletMap} from '../router_outlet_map';
|
||||||
import {ActivatedRoute} from '../router_state';
|
import {ActivatedRoute} from '../router_state';
|
||||||
|
@ -38,7 +38,7 @@ import {PRIMARY_OUTLET} from '../shared';
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Directive({selector: 'router-outlet'})
|
@Directive({selector: 'router-outlet'})
|
||||||
export class RouterOutlet {
|
export class RouterOutlet implements OnDestroy {
|
||||||
private activated: ComponentRef<any>;
|
private activated: ComponentRef<any>;
|
||||||
private _activatedRoute: ActivatedRoute;
|
private _activatedRoute: ActivatedRoute;
|
||||||
public outletMap: RouterOutletMap;
|
public outletMap: RouterOutletMap;
|
||||||
|
@ -47,11 +47,13 @@ export class RouterOutlet {
|
||||||
@Output('deactivate') deactivateEvents = new EventEmitter<any>();
|
@Output('deactivate') deactivateEvents = new EventEmitter<any>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
parentOutletMap: RouterOutletMap, private location: ViewContainerRef,
|
private parentOutletMap: RouterOutletMap, private location: ViewContainerRef,
|
||||||
private resolver: ComponentFactoryResolver, @Attribute('name') name: string) {
|
private resolver: ComponentFactoryResolver, @Attribute('name') private name: string) {
|
||||||
parentOutletMap.registerOutlet(name ? name : PRIMARY_OUTLET, this);
|
parentOutletMap.registerOutlet(name ? name : PRIMARY_OUTLET, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void { this.parentOutletMap.removeOutlet(this.name ? this.name : PRIMARY_OUTLET); }
|
||||||
|
|
||||||
get isActivated(): boolean { return !!this.activated; }
|
get isActivated(): boolean { return !!this.activated; }
|
||||||
get component(): Object {
|
get component(): Object {
|
||||||
if (!this.activated) throw new Error('Outlet is not activated');
|
if (!this.activated) throw new Error('Outlet is not activated');
|
||||||
|
|
|
@ -15,4 +15,6 @@ export class RouterOutletMap {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_outlets: {[name: string]: RouterOutlet} = {};
|
_outlets: {[name: string]: RouterOutlet} = {};
|
||||||
registerOutlet(name: string, outlet: RouterOutlet): void { this._outlets[name] = outlet; }
|
registerOutlet(name: string, outlet: RouterOutlet): void { this._outlets[name] = outlet; }
|
||||||
|
|
||||||
|
removeOutlet(name: string): void { this._outlets[name] = undefined; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,38 @@ describe('Integration', () => {
|
||||||
expect(location.path()).toEqual('/child/simple');
|
expect(location.path()).toEqual('/child/simple');
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
it('should work when an outlet is in an ngIf (and is removed)',
|
||||||
|
fakeAsync(inject(
|
||||||
|
[Router, TestComponentBuilder, Location],
|
||||||
|
(router: Router, tcb: TestComponentBuilder, location: Location) => {
|
||||||
|
@Component({
|
||||||
|
selector: 'someRoot',
|
||||||
|
template: `<div *ngIf="cond"><router-outlet></router-outlet></div>`,
|
||||||
|
entryComponents: [BlankCmp, SimpleCmp]
|
||||||
|
})
|
||||||
|
class RootCmpWithLink {
|
||||||
|
cond: boolean = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixture = createRoot(tcb, router, RootCmpWithLink);
|
||||||
|
|
||||||
|
router.resetConfig(
|
||||||
|
[{path: 'simple', component: SimpleCmp}, {path: 'blank', component: BlankCmp}]);
|
||||||
|
|
||||||
|
router.navigateByUrl('/simple');
|
||||||
|
advance(fixture);
|
||||||
|
expect(location.path()).toEqual('/simple');
|
||||||
|
|
||||||
|
const instance = fixture.componentInstance;
|
||||||
|
instance.cond = false;
|
||||||
|
advance(fixture);
|
||||||
|
|
||||||
|
let recordedError: any = null;
|
||||||
|
router.navigateByUrl('/blank').catch(e => recordedError = e);
|
||||||
|
advance(fixture);
|
||||||
|
expect(recordedError.message).toEqual('Cannot find primary outlet to load \'BlankCmp\'');
|
||||||
|
})));
|
||||||
|
|
||||||
it('should update location when navigating',
|
it('should update location when navigating',
|
||||||
fakeAsync(inject(
|
fakeAsync(inject(
|
||||||
[Router, TestComponentBuilder, Location],
|
[Router, TestComponentBuilder, Location],
|
||||||
|
|
|
@ -228,7 +228,7 @@ export declare class RouterModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class RouterOutlet {
|
export declare class RouterOutlet implements OnDestroy {
|
||||||
activateEvents: EventEmitter<any>;
|
activateEvents: EventEmitter<any>;
|
||||||
activatedRoute: ActivatedRoute;
|
activatedRoute: ActivatedRoute;
|
||||||
component: Object;
|
component: Object;
|
||||||
|
@ -238,11 +238,13 @@ export declare class RouterOutlet {
|
||||||
constructor(parentOutletMap: RouterOutletMap, location: ViewContainerRef, resolver: ComponentFactoryResolver, name: string);
|
constructor(parentOutletMap: RouterOutletMap, location: ViewContainerRef, resolver: ComponentFactoryResolver, name: string);
|
||||||
activate(activatedRoute: ActivatedRoute, loadedResolver: ComponentFactoryResolver, loadedInjector: Injector, providers: ResolvedReflectiveProvider[], outletMap: RouterOutletMap): void;
|
activate(activatedRoute: ActivatedRoute, loadedResolver: ComponentFactoryResolver, loadedInjector: Injector, providers: ResolvedReflectiveProvider[], outletMap: RouterOutletMap): void;
|
||||||
deactivate(): void;
|
deactivate(): void;
|
||||||
|
ngOnDestroy(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class RouterOutletMap {
|
export declare class RouterOutletMap {
|
||||||
registerOutlet(name: string, outlet: RouterOutlet): void;
|
registerOutlet(name: string, outlet: RouterOutlet): void;
|
||||||
|
removeOutlet(name: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
|
|
Loading…
Reference in New Issue