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 (#41381)

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

PR Close #41381
  • Loading branch information
atscott authored and zarend committed Apr 7, 2021
1 parent f72e218 commit 646f4a1
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 646f4a1

Please sign in to comment.