Skip to content

Commit

Permalink
fix(router): Remove information about attached component when deactiv…
Browse files Browse the repository at this point in the history
…ating route

When we deactivate a child route, we deactivate its outlet as well as
its children. We also need to clear the stored information about the
route and the associated component.
If we do not, the context will keep these references and can result in
reactivating an outlet that was deactivated by the previous navigation.

Fixes #41379
  • Loading branch information
atscott committed Mar 30, 2021
1 parent 5e46901 commit 4f94113
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 1 deletion.
5 changes: 5 additions & 0 deletions packages/router/src/operators/activate_routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ export class ActivateRoutes {
context.outlet.deactivate();
// Destroy the contexts for all the outlets that were in the component
context.children.onOutletDeactivated();
// Clear the information about the attached component on the context but keep the reference to
// the outlet.
context.attachRef = null;
context.resolver = null;
context.route = null;
}
}

Expand Down
42 changes: 41 additions & 1 deletion packages/router/test/regression_integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {CommonModule} from '@angular/common';
import {ChangeDetectionStrategy, Component, ContentChild, NgModule, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core';
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, NgModule, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core';
import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
import {Router} from '@angular/router';
import {RouterTestingModule} from '@angular/router/testing';
Expand Down Expand Up @@ -187,6 +187,46 @@ describe('Integration', () => {
expect(fixture.nativeElement.innerHTML).toContain('isActive: true');
}));
});

it('should not reactivate a deactivated outlet when destroyed and recreated - #41379',
fakeAsync(() => {
@Component({template: 'simple'})
class SimpleComponent {
}

@Component({template: ` <router-outlet *ngIf="outletVisible" name="aux"></router-outlet> `})
class AppComponent {
outletVisible = true;
}

TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes(
[{path: ':id', component: SimpleComponent, outlet: 'aux'}])],
declarations: [SimpleComponent, AppComponent],
});

const router = TestBed.inject(Router);
const fixture = createRoot(router, AppComponent);
const componentCdr = fixture.componentRef.injector.get<ChangeDetectorRef>(ChangeDetectorRef);

router.navigate([{outlets: {aux: ['1234']}}]);
advance(fixture);
expect(fixture.nativeElement.innerHTML).toContain('simple');

router.navigate([{outlets: {aux: null}}]);
advance(fixture);
expect(fixture.nativeElement.innerHTML).not.toContain('simple');

fixture.componentInstance.outletVisible = false;
componentCdr.detectChanges();
expect(fixture.nativeElement.innerHTML).not.toContain('simple');
expect(fixture.nativeElement.innerHTML).not.toContain('router-outlet');

fixture.componentInstance.outletVisible = true;
componentCdr.detectChanges();
expect(fixture.nativeElement.innerHTML).toContain('router-outlet');
expect(fixture.nativeElement.innerHTML).not.toContain('simple');
}));
});

function advance<T>(fixture: ComponentFixture<T>): void {
Expand Down

0 comments on commit 4f94113

Please sign in to comment.