fix(router): mount correct component if router outlet was not instantiated and if using a route reuse strategy (#25313) (#25314)
This unsets 'attachRef' on outlet context if no route is to be reused in route activation. Closes #25313 PR Close #25314
This commit is contained in:
@ -8,13 +8,13 @@
|
||||
|
||||
import {CommonModule, Location} from '@angular/common';
|
||||
import {SpyLocation} from '@angular/common/testing';
|
||||
import {ChangeDetectionStrategy, Component, Injectable, NgModule, NgModuleFactoryLoader, NgModuleRef, NgZone, ɵConsole as Console, ɵNoopNgZone as NoopNgZone} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, Component, Injectable, NgModule, NgModuleFactoryLoader, NgModuleRef, NgZone, OnDestroy, ɵConsole as Console, ɵNoopNgZone as NoopNgZone} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, fakeAsync, inject, tick} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, CanActivate, CanDeactivate, ChildActivationEnd, ChildActivationStart, DetachedRouteHandle, Event, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, PRIMARY_OUTLET, ParamMap, Params, PreloadAllModules, PreloadingStrategy, Resolve, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterEvent, RouterModule, RouterPreloader, RouterStateSnapshot, RoutesRecognized, RunGuardsAndResolvers, UrlHandlingStrategy, UrlSegmentGroup, UrlSerializer, UrlTree} from '@angular/router';
|
||||
import {Observable, Observer, of } from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
import {Observable, Observer, Subscription, of } from 'rxjs';
|
||||
import {filter, map} from 'rxjs/operators';
|
||||
|
||||
import {forEach} from '../src/utils/collection';
|
||||
import {RouterTestingModule, SpyNgModuleFactoryLoader} from '../testing';
|
||||
@ -4022,6 +4022,82 @@ describe('Integration', () => {
|
||||
const simpleCmp2 = fixture.debugElement.children[1].componentInstance;
|
||||
expect(simpleCmp1).not.toBe(simpleCmp2);
|
||||
})));
|
||||
|
||||
it('should not mount the component of the previously reused route when the outlet was not instantiated at the time of route activation',
|
||||
fakeAsync(() => {
|
||||
@Component({
|
||||
selector: 'root-cmp',
|
||||
template:
|
||||
'<div *ngIf="isToolpanelShowing"><router-outlet name="toolpanel"></router-outlet></div>'
|
||||
})
|
||||
class RootCmpWithCondOutlet implements OnDestroy {
|
||||
private subscription: Subscription;
|
||||
public isToolpanelShowing: boolean = false;
|
||||
|
||||
constructor(router: Router) {
|
||||
this.subscription =
|
||||
router.events.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe(
|
||||
() => this.isToolpanelShowing =
|
||||
!!router.parseUrl(router.url).root.children['toolpanel']);
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void { this.subscription.unsubscribe(); }
|
||||
}
|
||||
|
||||
@Component({selector: 'tool-1-cmp', template: 'Tool 1 showing'})
|
||||
class Tool1Component {
|
||||
}
|
||||
|
||||
@Component({selector: 'tool-2-cmp', template: 'Tool 2 showing'})
|
||||
class Tool2Component {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [RootCmpWithCondOutlet, Tool1Component, Tool2Component],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterTestingModule.withRoutes([
|
||||
{path: 'a', outlet: 'toolpanel', component: Tool1Component},
|
||||
{path: 'b', outlet: 'toolpanel', component: Tool2Component},
|
||||
]),
|
||||
],
|
||||
})
|
||||
class TestModule {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({imports: [TestModule]});
|
||||
|
||||
const router: Router = TestBed.get(Router);
|
||||
router.routeReuseStrategy = new AttachDetachReuseStrategy();
|
||||
|
||||
const fixture = createRoot(router, RootCmpWithCondOutlet);
|
||||
|
||||
// Activate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: 'a'}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).toContainComponent(Tool1Component, '(a)');
|
||||
|
||||
// Deactivate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: null}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).not.toContainComponent(Tool1Component, '(b)');
|
||||
|
||||
// Activate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: 'a'}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).toContainComponent(Tool1Component, '(c)');
|
||||
|
||||
// Deactivate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: null}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).not.toContainComponent(Tool1Component, '(d)');
|
||||
|
||||
// Activate 'tool-2'
|
||||
router.navigate([{outlets: {toolpanel: 'b'}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).toContainComponent(Tool2Component, '(e)');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user