fix(router): navigation should not preserve query params and fragment by default
BREAKING CHANGE Previously both imperative (router.navigate) and declarative (routerLink) navigations would preserve the current query params and fragment. This behavior turned out to be confusing. This commit changes it. Now, neither is preserved by default. To preserve them, you need to do the following: router.naviage("newUrl", {preserveQueryParams: true, preserveFragment: true}) <a routerLink="newUrl" preserveQueryParams preserveFragment></a>
This commit is contained in:
parent
73a69895d8
commit
23ee29b6a2
|
@ -42,13 +42,11 @@ function validateCommands(n: NormalizedNavigationCommands): void {
|
|||
function tree(
|
||||
oldSegment: UrlSegment, newSegment: UrlSegment, urlTree: UrlTree, queryParams: Params,
|
||||
fragment: string): UrlTree {
|
||||
const q = queryParams ? stringify(queryParams) : urlTree.queryParams;
|
||||
const f = fragment ? fragment : urlTree.fragment;
|
||||
|
||||
if (urlTree.root === oldSegment) {
|
||||
return new UrlTree(newSegment, q, f);
|
||||
return new UrlTree(newSegment, stringify(queryParams), fragment);
|
||||
} else {
|
||||
return new UrlTree(replaceSegment(urlTree.root, oldSegment, newSegment), q, f);
|
||||
return new UrlTree(
|
||||
replaceSegment(urlTree.root, oldSegment, newSegment), stringify(queryParams), fragment);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,9 +51,15 @@ import {UrlTree} from '../url_tree';
|
|||
* <a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" fragment="education">link to user
|
||||
component</a>
|
||||
* ```
|
||||
*
|
||||
* RouterLink will use these to generate this link: `/user/bob#education?debug=true`.
|
||||
*
|
||||
* You can also tell the directive to preserve the current query params and fragment:
|
||||
*
|
||||
* ```
|
||||
* <a [routerLink]="['/user/bob']" preserveQueryParams preserveFragment>link to user
|
||||
component</a>
|
||||
* ```
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: ':not(a)[routerLink]'})
|
||||
|
@ -61,6 +67,8 @@ export class RouterLink {
|
|||
private commands: any[] = [];
|
||||
@Input() queryParams: {[k: string]: any};
|
||||
@Input() fragment: string;
|
||||
@Input() preserveQueryParams: boolean;
|
||||
@Input() preserveFragment: boolean;
|
||||
|
||||
constructor(
|
||||
private router: Router, private route: ActivatedRoute,
|
||||
|
@ -85,9 +93,13 @@ export class RouterLink {
|
|||
}
|
||||
|
||||
get urlTree(): UrlTree {
|
||||
return this.router.createUrlTree(
|
||||
this.commands,
|
||||
{relativeTo: this.route, queryParams: this.queryParams, fragment: this.fragment});
|
||||
return this.router.createUrlTree(this.commands, {
|
||||
relativeTo: this.route,
|
||||
queryParams: this.queryParams,
|
||||
fragment: this.fragment,
|
||||
preserveQueryParams: toBool(this.preserveQueryParams),
|
||||
preserveFragment: toBool(this.preserveFragment)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,6 +113,9 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy {
|
|||
private commands: any[] = [];
|
||||
@Input() queryParams: {[k: string]: any};
|
||||
@Input() fragment: string;
|
||||
@Input() routerLinkOptions: {preserveQueryParams: boolean, preserveFragment: boolean};
|
||||
@Input() preserveQueryParams: boolean;
|
||||
@Input() preserveFragment: boolean;
|
||||
private subscription: Subscription;
|
||||
|
||||
// the url displayed on the anchor element.
|
||||
|
@ -148,12 +163,21 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy {
|
|||
}
|
||||
|
||||
private updateTargetUrlAndHref(): void {
|
||||
this.urlTree = this.router.createUrlTree(
|
||||
this.commands,
|
||||
{relativeTo: this.route, queryParams: this.queryParams, fragment: this.fragment});
|
||||
this.urlTree = this.router.createUrlTree(this.commands, {
|
||||
relativeTo: this.route,
|
||||
queryParams: this.queryParams,
|
||||
fragment: this.fragment,
|
||||
preserveQueryParams: toBool(this.preserveQueryParams),
|
||||
preserveFragment: toBool(this.preserveFragment)
|
||||
});
|
||||
|
||||
if (this.urlTree) {
|
||||
this.href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.urlTree));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toBool(s?: any): boolean {
|
||||
if (s === '') return true;
|
||||
return !!s;
|
||||
}
|
|
@ -45,6 +45,8 @@ export interface NavigationExtras {
|
|||
relativeTo?: ActivatedRoute;
|
||||
queryParams?: Params;
|
||||
fragment?: string;
|
||||
preserveQueryParams?: boolean;
|
||||
preserveFragment?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -227,10 +229,13 @@ export class Router {
|
|||
* router.createUrlTree(['../../team/44/user/22'], {relativeTo: route});
|
||||
* ```
|
||||
*/
|
||||
createUrlTree(commands: any[], {relativeTo, queryParams, fragment}: NavigationExtras = {}):
|
||||
UrlTree {
|
||||
createUrlTree(
|
||||
commands: any[], {relativeTo, queryParams, fragment, preserveQueryParams,
|
||||
preserveFragment}: NavigationExtras = {}): UrlTree {
|
||||
const a = relativeTo ? relativeTo : this.routerState.root;
|
||||
return createUrlTree(a, this.currentUrlTree, commands, queryParams, fragment);
|
||||
const q = preserveQueryParams ? this.currentUrlTree.queryParams : queryParams;
|
||||
const f = preserveFragment ? this.currentUrlTree.fragment : fragment;
|
||||
return createUrlTree(a, this.currentUrlTree, commands, q, f);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -183,7 +183,9 @@ export class DefaultUrlSerializer implements UrlSerializer {
|
|||
serialize(tree: UrlTree): string {
|
||||
const segment = `/${serializeSegment(tree.root, true)}`;
|
||||
const query = serializeQueryParams(tree.queryParams);
|
||||
const fragment = tree.fragment !== null ? `#${encodeURIComponent(tree.fragment)}` : '';
|
||||
const fragment = tree.fragment !== null && tree.fragment !== undefined ?
|
||||
`#${encodeURIComponent(tree.fragment)}` :
|
||||
'';
|
||||
return `${segment}${query}${fragment}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,23 +182,11 @@ describe('createUrlTree', () => {
|
|||
expect(t.queryParams).toEqual({a: '1'});
|
||||
});
|
||||
|
||||
it('should reuse old query params when given undefined', () => {
|
||||
const p = serializer.parse('/?a=1');
|
||||
const t = createRoot(p, [], undefined);
|
||||
expect(t.queryParams).toEqual({a: '1'});
|
||||
});
|
||||
|
||||
it('should set fragment', () => {
|
||||
const p = serializer.parse('/');
|
||||
const t = createRoot(p, [], {}, 'fragment');
|
||||
expect(t.fragment).toEqual('fragment');
|
||||
});
|
||||
|
||||
it('should reused old fragment when given undefined', () => {
|
||||
const p = serializer.parse('/#fragment');
|
||||
const t = createRoot(p, [], undefined, undefined);
|
||||
expect(t.fragment).toEqual('fragment');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -594,10 +594,9 @@ describe('Integration', () => {
|
|||
expect(fixture.debugElement.nativeElement).toHaveText('team 33 [ simple, right: ]');
|
||||
})));
|
||||
|
||||
it('should update hrefs when query params change',
|
||||
it('should not preserve query params and fragment by default',
|
||||
fakeAsync(
|
||||
inject([Router, TestComponentBuilder], (router: Router, tcb: TestComponentBuilder) => {
|
||||
|
||||
@Component({
|
||||
selector: 'someRoot',
|
||||
template: `<router-outlet></router-outlet><a routerLink="/home">Link</a>`,
|
||||
|
@ -612,6 +611,29 @@ describe('Integration', () => {
|
|||
|
||||
const native = fixture.debugElement.nativeElement.querySelector('a');
|
||||
|
||||
router.navigateByUrl('/home?q=123#fragment');
|
||||
advance(fixture);
|
||||
expect(native.getAttribute('href')).toEqual('/home');
|
||||
})));
|
||||
|
||||
it('should update hrefs when query params or fragment change',
|
||||
fakeAsync(inject([Router, TestComponentBuilder], (router: Router, tcb: TestComponentBuilder) => {
|
||||
|
||||
@Component({
|
||||
selector: 'someRoot',
|
||||
template:
|
||||
`<router-outlet></router-outlet><a routerLink="/home" preserveQueryParams preserveFragment>Link</a>`,
|
||||
directives: ROUTER_DIRECTIVES
|
||||
})
|
||||
class RootCmpWithLink {
|
||||
}
|
||||
|
||||
const fixture = createRoot(tcb, router, RootCmpWithLink);
|
||||
|
||||
router.resetConfig([{path: 'home', component: SimpleCmp}]);
|
||||
|
||||
const native = fixture.debugElement.nativeElement.querySelector('a');
|
||||
|
||||
router.navigateByUrl('/home?q=123');
|
||||
advance(fixture);
|
||||
expect(native.getAttribute('href')).toEqual('/home?q=123');
|
||||
|
@ -619,6 +641,10 @@ describe('Integration', () => {
|
|||
router.navigateByUrl('/home?q=456');
|
||||
advance(fixture);
|
||||
expect(native.getAttribute('href')).toEqual('/home?q=456');
|
||||
|
||||
router.navigateByUrl('/home?q=456#1');
|
||||
advance(fixture);
|
||||
expect(native.getAttribute('href')).toEqual('/home?q=456#1');
|
||||
})));
|
||||
|
||||
it('should support using links on non-a tags',
|
||||
|
|
Loading…
Reference in New Issue