mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
The current way of computing a route's params and data recomputes inherited data from the inheritance root every time. When the inheritance strategy is "emptyOnly", this isn't necessarily the root of the tree, but some point along the way (it stops once it reaches an ancestor route with a component). Instead, this commit updates parameter inheritance to only inherit data directly from the parent route (again, instead of recomputing all inherited data back to the inheritance root). The only requirement for making this work is that the parent route data has already calculated and updated its own inherited data. This was really already a requirement -- parents need to be processed before children. In addition, the update to the inheritance algorithm in this commit requires more of an understanding that a resolver running higher up in the tree has to propagate inherited data downwards. The previous algorithm hid this knowledge because resolvers would recompute inherited data from the root when run. However, routes that did not have resolvers rerun or never had resolvers at all would not get the updated resolved data. fixes #51934 PR Close #52167
138 lines
4.8 KiB
TypeScript
138 lines
4.8 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import {Component} from '@angular/core';
|
|
import {TestBed} from '@angular/core/testing';
|
|
import {provideRouter, Router} from '@angular/router';
|
|
import {RouterTestingHarness} from '@angular/router/testing';
|
|
import {EMPTY, interval, NEVER, of} from 'rxjs';
|
|
|
|
describe('resolveData operator', () => {
|
|
it('should take only the first emitted value of every resolver', async () => {
|
|
TestBed.configureTestingModule({
|
|
providers: [provideRouter([{path: '**', children: [], resolve: {e1: () => interval()}}])]
|
|
});
|
|
await RouterTestingHarness.create('/');
|
|
expect(TestBed.inject(Router).routerState.root.firstChild?.snapshot.data).toEqual({e1: 0});
|
|
});
|
|
|
|
it('should cancel navigation if a resolver does not complete', async () => {
|
|
TestBed.configureTestingModule(
|
|
{providers: [provideRouter([{path: '**', children: [], resolve: {e1: () => EMPTY}}])]});
|
|
await RouterTestingHarness.create('/a');
|
|
expect(TestBed.inject(Router).url).toEqual('/');
|
|
});
|
|
|
|
it('should cancel navigation if 1 of 2 resolvers does not emit', async () => {
|
|
TestBed.configureTestingModule({
|
|
providers:
|
|
[provideRouter([{path: '**', children: [], resolve: {e0: () => of(0), e1: () => EMPTY}}])]
|
|
});
|
|
await RouterTestingHarness.create('/a');
|
|
expect(TestBed.inject(Router).url).toEqual('/');
|
|
});
|
|
|
|
it('should complete instantly if at least one resolver doesn\'t emit', async () => {
|
|
TestBed.configureTestingModule({
|
|
providers:
|
|
[provideRouter([{path: '**', children: [], resolve: {e0: () => EMPTY, e1: () => NEVER}}])]
|
|
});
|
|
await RouterTestingHarness.create('/a');
|
|
expect(TestBed.inject(Router).url).toEqual('/');
|
|
});
|
|
|
|
it('should update children inherited data when resolvers run', async () => {
|
|
let value = 0;
|
|
TestBed.configureTestingModule({
|
|
providers: [provideRouter([{
|
|
path: 'a',
|
|
children: [{path: 'b', children: []}],
|
|
resolve: {d0: () => ++value},
|
|
runGuardsAndResolvers: 'always',
|
|
}])]
|
|
});
|
|
const harness = await RouterTestingHarness.create('/a/b');
|
|
expect(TestBed.inject(Router).routerState.root.firstChild?.snapshot.data).toEqual({d0: 1});
|
|
expect(TestBed.inject(Router).routerState.root.firstChild?.firstChild?.snapshot.data).toEqual({
|
|
d0: 1
|
|
});
|
|
|
|
await harness.navigateByUrl('/a/b#new');
|
|
expect(TestBed.inject(Router).routerState.root.firstChild?.snapshot.data).toEqual({d0: 2});
|
|
expect(TestBed.inject(Router).routerState.root.firstChild?.firstChild?.snapshot.data).toEqual({
|
|
d0: 2
|
|
});
|
|
});
|
|
|
|
it('should have correct data when parent resolver runs but data is not inherited', async () => {
|
|
@Component({template: ''})
|
|
class Empty {
|
|
}
|
|
|
|
TestBed.configureTestingModule({
|
|
providers: [provideRouter([{
|
|
path: 'a',
|
|
component: Empty,
|
|
data: {parent: 'parent'},
|
|
resolve: {other: () => 'other'},
|
|
children: [{
|
|
path: 'b',
|
|
data: {child: 'child'},
|
|
component: Empty,
|
|
}]
|
|
}])]
|
|
});
|
|
await RouterTestingHarness.create('/a/b');
|
|
const rootSnapshot = TestBed.inject(Router).routerState.root.firstChild!.snapshot;
|
|
expect(rootSnapshot.data).toEqual({parent: 'parent', other: 'other'});
|
|
expect(rootSnapshot.firstChild!.data).toEqual({child: 'child'});
|
|
});
|
|
|
|
it('should have static title when there is a resolver', async () => {
|
|
@Component({template: ''})
|
|
class Empty {
|
|
}
|
|
|
|
TestBed.configureTestingModule({
|
|
providers: [provideRouter([{
|
|
path: 'a',
|
|
title: 'a title',
|
|
component: Empty,
|
|
resolve: {other: () => 'other'},
|
|
children: [{
|
|
path: 'b',
|
|
title: 'b title',
|
|
component: Empty,
|
|
resolve: {otherb: () => 'other b'},
|
|
}]
|
|
}])]
|
|
});
|
|
await RouterTestingHarness.create('/a/b');
|
|
const rootSnapshot = TestBed.inject(Router).routerState.root.firstChild!.snapshot;
|
|
expect(rootSnapshot.title).toBe('a title');
|
|
expect(rootSnapshot.firstChild!.title).toBe('b title');
|
|
});
|
|
|
|
it('should inherit resolved data from parent of parent route', async () => {
|
|
@Component({template: ''})
|
|
class Empty {
|
|
}
|
|
|
|
TestBed.configureTestingModule({
|
|
providers: [provideRouter([{
|
|
path: 'a',
|
|
resolve: {aResolve: () => 'a'},
|
|
children:
|
|
[{path: 'b', resolve: {bResolve: () => 'b'}, children: [{path: 'c', component: Empty}]}]
|
|
}])]
|
|
});
|
|
await RouterTestingHarness.create('/a/b/c');
|
|
const rootSnapshot = TestBed.inject(Router).routerState.root.firstChild!.snapshot;
|
|
expect(rootSnapshot.firstChild!.firstChild!.data).toEqual({bResolve: 'b', aResolve: 'a'});
|
|
});
|
|
});
|