revert: fix(router): ensure URL is updated after second redirect with UrlUpdateStrategy="eager" (#27523)

This commit is contained in:
Matias Niemelä 2018-12-25 21:37:36 -08:00
parent 13eb57a59f
commit eea2b0f288
2 changed files with 50 additions and 110 deletions

View File

@ -291,7 +291,6 @@ function defaultRouterHook(snapshot: RouterStateSnapshot, runExtras: {
export class Router { export class Router {
private currentUrlTree: UrlTree; private currentUrlTree: UrlTree;
private rawUrlTree: UrlTree; private rawUrlTree: UrlTree;
private browserUrlTree: UrlTree;
private readonly transitions: BehaviorSubject<NavigationTransition>; private readonly transitions: BehaviorSubject<NavigationTransition>;
private navigations: Observable<NavigationTransition>; private navigations: Observable<NavigationTransition>;
private lastSuccessfulNavigation: Navigation|null = null; private lastSuccessfulNavigation: Navigation|null = null;
@ -401,7 +400,6 @@ export class Router {
this.resetConfig(config); this.resetConfig(config);
this.currentUrlTree = createEmptyUrlTree(); this.currentUrlTree = createEmptyUrlTree();
this.rawUrlTree = this.currentUrlTree; this.rawUrlTree = this.currentUrlTree;
this.browserUrlTree = this.parseUrl(this.location.path());
this.configLoader = new RouterConfigLoader(loader, compiler, onLoadStart, onLoadEnd); this.configLoader = new RouterConfigLoader(loader, compiler, onLoadStart, onLoadEnd);
this.routerState = createEmptyState(this.currentUrlTree, this.rootComponentType); this.routerState = createEmptyState(this.currentUrlTree, this.rootComponentType);
@ -463,7 +461,7 @@ export class Router {
return of (t).pipe( return of (t).pipe(
switchMap(t => { switchMap(t => {
const urlTransition = const urlTransition =
!this.navigated || t.extractedUrl.toString() !== this.browserUrlTree.toString(); !this.navigated || t.extractedUrl.toString() !== this.currentUrlTree.toString();
const processCurrentUrl = const processCurrentUrl =
(this.onSameUrlNavigation === 'reload' ? true : urlTransition) && (this.onSameUrlNavigation === 'reload' ? true : urlTransition) &&
this.urlHandlingStrategy.shouldProcessUrl(t.rawUrl); this.urlHandlingStrategy.shouldProcessUrl(t.rawUrl);
@ -504,12 +502,8 @@ export class Router {
this.paramsInheritanceStrategy, this.relativeLinkResolution), this.paramsInheritanceStrategy, this.relativeLinkResolution),
// Update URL if in `eager` update mode // Update URL if in `eager` update mode
tap(t => { tap(t => this.urlUpdateStrategy === 'eager' && !t.extras.skipLocationChange &&
if (this.urlUpdateStrategy === 'eager' && !t.extras.skipLocationChange) { this.setBrowserUrl(t.urlAfterRedirects, !!t.extras.replaceUrl, t.id)),
this.setBrowserUrl(t.urlAfterRedirects, !!t.extras.replaceUrl, t.id);
this.browserUrlTree = t.urlAfterRedirects;
}
}),
// Fire RoutesRecognized // Fire RoutesRecognized
tap(t => { tap(t => {
@ -671,7 +665,6 @@ export class Router {
if (this.urlUpdateStrategy === 'deferred' && !t.extras.skipLocationChange) { if (this.urlUpdateStrategy === 'deferred' && !t.extras.skipLocationChange) {
this.setBrowserUrl(this.rawUrlTree, !!t.extras.replaceUrl, t.id, t.extras.state); this.setBrowserUrl(this.rawUrlTree, !!t.extras.replaceUrl, t.id, t.extras.state);
this.browserUrlTree = t.urlAfterRedirects;
} }
}), }),

View File

@ -13,7 +13,7 @@ import {ComponentFixture, TestBed, fakeAsync, inject, tick} from '@angular/core/
import {By} from '@angular/platform-browser/src/dom/debug/by'; import {By} from '@angular/platform-browser/src/dom/debug/by';
import {expect} from '@angular/platform-browser/testing/src/matchers'; import {expect} from '@angular/platform-browser/testing/src/matchers';
import {fixmeIvy} from '@angular/private/testing'; import {fixmeIvy} from '@angular/private/testing';
import {ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, CanActivate, CanDeactivate, ChildActivationEnd, ChildActivationStart, DefaultUrlSerializer, DetachedRouteHandle, Event, GuardsCheckEnd, GuardsCheckStart, Navigation, 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 {ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, CanActivate, CanDeactivate, ChildActivationEnd, ChildActivationStart, DetachedRouteHandle, Event, GuardsCheckEnd, GuardsCheckStart, Navigation, 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, Subscription, of } from 'rxjs'; import {Observable, Observer, Subscription, of } from 'rxjs';
import {filter, first, map, tap} from 'rxjs/operators'; import {filter, first, map, tap} from 'rxjs/operators';
@ -575,110 +575,64 @@ describe('Integration', () => {
expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]'); expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]');
}))); })));
describe('"eager" urlUpdateStrategy', () => { it('should eagerly update the URL with urlUpdateStrategy="eagar"',
beforeEach(() => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const serializer = new DefaultUrlSerializer(); const fixture = TestBed.createComponent(RootCmp);
TestBed.configureTestingModule({ advance(fixture);
providers: [{
provide: 'authGuardFail',
useValue: (a: any, b: any) => {
return new Promise(res => { setTimeout(() => res(serializer.parse('/login')), 1); });
}
}]
});
}); router.resetConfig([{path: 'team/:id', component: TeamCmp}]);
router.navigateByUrl('/team/22');
advance(fixture);
expect(location.path()).toEqual('/team/22');
it('should eagerly update the URL with urlUpdateStrategy="eagar"', expect(fixture.nativeElement).toHaveText('team 22 [ , right: ]');
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = TestBed.createComponent(RootCmp);
advance(fixture);
router.resetConfig([{path: 'team/:id', component: TeamCmp}]);
router.navigateByUrl('/team/22');
advance(fixture);
expect(location.path()).toEqual('/team/22');
router.urlUpdateStrategy = 'eager';
(router as any).hooks.beforePreactivation = () => {
expect(location.path()).toEqual('/team/33');
expect(fixture.nativeElement).toHaveText('team 22 [ , right: ]'); expect(fixture.nativeElement).toHaveText('team 22 [ , right: ]');
return of (null);
};
router.navigateByUrl('/team/33');
router.urlUpdateStrategy = 'eager'; advance(fixture);
(router as any).hooks.beforePreactivation = () => { expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]');
expect(location.path()).toEqual('/team/33'); })));
expect(fixture.nativeElement).toHaveText('team 22 [ , right: ]');
return of (null);
};
router.navigateByUrl('/team/33');
advance(fixture); it('should eagerly update URL after redirects are applied with urlUpdateStrategy="eagar"',
expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]'); fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
}))); const fixture = TestBed.createComponent(RootCmp);
it('should eagerly update the URL with urlUpdateStrategy="eagar"', advance(fixture);
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = TestBed.createComponent(RootCmp);
advance(fixture);
router.urlUpdateStrategy = 'eager'; router.resetConfig([{path: 'team/:id', component: TeamCmp}]);
router.resetConfig([ router.navigateByUrl('/team/22');
{path: 'team/:id', component: SimpleCmp, canActivate: ['authGuardFail']}, advance(fixture);
{path: 'login', component: AbsoluteSimpleLinkCmp} expect(location.path()).toEqual('/team/22');
]);
router.navigateByUrl('/team/22'); expect(fixture.nativeElement).toHaveText('team 22 [ , right: ]');
advance(fixture);
expect(location.path()).toEqual('/team/22');
// Redirects to /login router.urlUpdateStrategy = 'eager';
advance(fixture, 1);
expect(location.path()).toEqual('/login');
// Perform the same logic again, and it should produce the same result let urlAtNavStart = '';
router.navigateByUrl('/team/22'); let urlAtRoutesRecognized = '';
advance(fixture); router.events.subscribe(e => {
expect(location.path()).toEqual('/team/22'); if (e instanceof NavigationStart) {
urlAtNavStart = location.path();
}
if (e instanceof RoutesRecognized) {
urlAtRoutesRecognized = location.path();
}
});
// Redirects to /login router.navigateByUrl('/team/33');
advance(fixture, 1);
expect(location.path()).toEqual('/login');
})));
it('should eagerly update URL after redirects are applied with urlUpdateStrategy="eagar"', advance(fixture);
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { expect(urlAtNavStart).toBe('/team/22');
const fixture = TestBed.createComponent(RootCmp); expect(urlAtRoutesRecognized).toBe('/team/33');
advance(fixture); expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]');
})));
router.resetConfig([{path: 'team/:id', component: TeamCmp}]);
router.navigateByUrl('/team/22');
advance(fixture);
expect(location.path()).toEqual('/team/22');
expect(fixture.nativeElement).toHaveText('team 22 [ , right: ]');
router.urlUpdateStrategy = 'eager';
let urlAtNavStart = '';
let urlAtRoutesRecognized = '';
router.events.subscribe(e => {
if (e instanceof NavigationStart) {
urlAtNavStart = location.path();
}
if (e instanceof RoutesRecognized) {
urlAtRoutesRecognized = location.path();
}
});
router.navigateByUrl('/team/33');
advance(fixture);
expect(urlAtNavStart).toBe('/team/22');
expect(urlAtRoutesRecognized).toBe('/team/33');
expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]');
})));
});
it('should navigate back and forward', it('should navigate back and forward',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
@ -4713,10 +4667,6 @@ class DummyLinkCmp {
} }
} }
@Component({selector: 'link-cmp', template: `<a [routerLink]="['/simple']">link</a>`})
class AbsoluteSimpleLinkCmp {
}
@Component({selector: 'link-cmp', template: `<a [routerLink]="['../simple']">link</a>`}) @Component({selector: 'link-cmp', template: `<a [routerLink]="['../simple']">link</a>`})
class RelativeLinkCmp { class RelativeLinkCmp {
} }
@ -4897,8 +4847,8 @@ class ThrowingCmp {
function advance(fixture: ComponentFixture<any>, millis?: number): void { function advance(fixture: ComponentFixture<any>): void {
tick(millis); tick();
fixture.detectChanges(); fixture.detectChanges();
} }
@ -4926,7 +4876,6 @@ class LazyComponent {
StringLinkCmp, StringLinkCmp,
DummyLinkCmp, DummyLinkCmp,
AbsoluteLinkCmp, AbsoluteLinkCmp,
AbsoluteSimpleLinkCmp,
RelativeLinkCmp, RelativeLinkCmp,
DummyLinkWithParentCmp, DummyLinkWithParentCmp,
LinkWithQueryParamsAndFragment, LinkWithQueryParamsAndFragment,
@ -4955,7 +4904,6 @@ class LazyComponent {
StringLinkCmp, StringLinkCmp,
DummyLinkCmp, DummyLinkCmp,
AbsoluteLinkCmp, AbsoluteLinkCmp,
AbsoluteSimpleLinkCmp,
RelativeLinkCmp, RelativeLinkCmp,
DummyLinkWithParentCmp, DummyLinkWithParentCmp,
LinkWithQueryParamsAndFragment, LinkWithQueryParamsAndFragment,
@ -4986,7 +4934,6 @@ class LazyComponent {
StringLinkCmp, StringLinkCmp,
DummyLinkCmp, DummyLinkCmp,
AbsoluteLinkCmp, AbsoluteLinkCmp,
AbsoluteSimpleLinkCmp,
RelativeLinkCmp, RelativeLinkCmp,
DummyLinkWithParentCmp, DummyLinkWithParentCmp,
LinkWithQueryParamsAndFragment, LinkWithQueryParamsAndFragment,