feat(router): deprecate preserveQueryParams,add queryParamsHandling (#14095)

PR Close #14095
This commit is contained in:
evan Liu 2017-01-25 17:33:13 +08:00 committed by Miško Hevery
parent 2ffa1a71aa
commit c87c3bec93
5 changed files with 131 additions and 11 deletions

View File

@ -320,6 +320,13 @@ export type LoadChildrenCallback = () =>
*/ */
export type LoadChildren = string | LoadChildrenCallback; export type LoadChildren = string | LoadChildrenCallback;
/**
* @whatItDoes The type of `queryParamsHandling`.
* See {@link RouterLink} for more details.
* @stable
*/
export type QueryParamsHandling = 'merge' | 'preserve' | '';
/** /**
* See {@link Routes} for more details. * See {@link Routes} for more details.
* @stable * @stable

View File

@ -7,9 +7,10 @@
*/ */
import {LocationStrategy} from '@angular/common'; import {LocationStrategy} from '@angular/common';
import {Attribute, Directive, ElementRef, HostBinding, HostListener, Input, OnChanges, OnDestroy, Renderer} from '@angular/core'; import {Attribute, Directive, ElementRef, HostBinding, HostListener, Input, OnChanges, OnDestroy, Renderer, isDevMode} from '@angular/core';
import {Subscription} from 'rxjs/Subscription'; import {Subscription} from 'rxjs/Subscription';
import {QueryParamsHandling} from '../config';
import {NavigationEnd, Router} from '../router'; import {NavigationEnd, Router} from '../router';
import {ActivatedRoute} from '../router_state'; import {ActivatedRoute} from '../router_state';
import {UrlTree} from '../url_tree'; import {UrlTree} from '../url_tree';
@ -59,12 +60,26 @@ import {UrlTree} from '../url_tree';
* *
* You can also tell the directive to preserve the current query params and fragment: * You can also tell the directive to preserve the current query params and fragment:
* *
* deprecated, use `queryParamsHandling` instead
*
* ``` * ```
* <a [routerLink]="['/user/bob']" preserveQueryParams preserveFragment> * <a [routerLink]="['/user/bob']" preserveQueryParams preserveFragment>
* link to user component * link to user component
* </a> * </a>
* ``` * ```
* *
* You can tell the directive to how to handle queryParams, available options are:
* - 'merge' merge the queryParams into the current queryParams
* - 'preserve' prserve the current queryParams
* - default / '' use the queryParams only
* same options for {@link NavigationExtras.queryParamsHandling}
*
* ```
* <a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" queryParamsHandling="merge">
* link to user component
* </a>
* ```
*
* The router link directive always treats the provided input as a delta to the current url. * The router link directive always treats the provided input as a delta to the current url.
* *
* For instance, if the current url is `/user/(box//aux:team)`. * For instance, if the current url is `/user/(box//aux:team)`.
@ -82,11 +97,12 @@ import {UrlTree} from '../url_tree';
export class RouterLink { export class RouterLink {
@Input() queryParams: {[k: string]: any}; @Input() queryParams: {[k: string]: any};
@Input() fragment: string; @Input() fragment: string;
@Input() preserveQueryParams: boolean; @Input() queryParamsHandling: QueryParamsHandling;
@Input() preserveFragment: boolean; @Input() preserveFragment: boolean;
@Input() skipLocationChange: boolean; @Input() skipLocationChange: boolean;
@Input() replaceUrl: boolean; @Input() replaceUrl: boolean;
private commands: any[] = []; private commands: any[] = [];
private preserve: boolean;
constructor( constructor(
private router: Router, private route: ActivatedRoute, private router: Router, private route: ActivatedRoute,
@ -105,6 +121,14 @@ export class RouterLink {
} }
} }
@Input()
set preserveQueryParams(value: boolean) {
if (isDevMode() && <any>console && <any>console.warn) {
console.warn('preserveQueryParams is deprecated!, use queryParamsHandling instead.');
}
this.preserve = value;
}
@HostListener('click') @HostListener('click')
onClick(): boolean { onClick(): boolean {
const extras = { const extras = {
@ -120,7 +144,8 @@ export class RouterLink {
relativeTo: this.route, relativeTo: this.route,
queryParams: this.queryParams, queryParams: this.queryParams,
fragment: this.fragment, fragment: this.fragment,
preserveQueryParams: attrBoolValue(this.preserveQueryParams), preserveQueryParams: attrBoolValue(this.preserve),
queryParamsHandling: this.queryParamsHandling,
preserveFragment: attrBoolValue(this.preserveFragment), preserveFragment: attrBoolValue(this.preserveFragment),
}); });
} }
@ -140,12 +165,13 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy {
@HostBinding('attr.target') @Input() target: string; @HostBinding('attr.target') @Input() target: string;
@Input() queryParams: {[k: string]: any}; @Input() queryParams: {[k: string]: any};
@Input() fragment: string; @Input() fragment: string;
@Input() preserveQueryParams: boolean; @Input() queryParamsHandling: QueryParamsHandling;
@Input() preserveFragment: boolean; @Input() preserveFragment: boolean;
@Input() skipLocationChange: boolean; @Input() skipLocationChange: boolean;
@Input() replaceUrl: boolean; @Input() replaceUrl: boolean;
private commands: any[] = []; private commands: any[] = [];
private subscription: Subscription; private subscription: Subscription;
private preserve: boolean;
// the url displayed on the anchor element. // the url displayed on the anchor element.
@HostBinding() href: string; @HostBinding() href: string;
@ -169,6 +195,14 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy {
} }
} }
@Input()
set preserveQueryParams(value: boolean) {
if (isDevMode() && <any>console && <any>console.warn) {
console.warn('preserveQueryParams is deprecated, use queryParamsHandling instead.');
}
this.preserve = value;
}
ngOnChanges(changes: {}): any { this.updateTargetUrlAndHref(); } ngOnChanges(changes: {}): any { this.updateTargetUrlAndHref(); }
ngOnDestroy(): any { this.subscription.unsubscribe(); } ngOnDestroy(): any { this.subscription.unsubscribe(); }
@ -199,7 +233,8 @@ export class RouterLinkWithHref implements OnChanges, OnDestroy {
relativeTo: this.route, relativeTo: this.route,
queryParams: this.queryParams, queryParams: this.queryParams,
fragment: this.fragment, fragment: this.fragment,
preserveQueryParams: attrBoolValue(this.preserveQueryParams), preserveQueryParams: attrBoolValue(this.preserve),
queryParamsHandling: this.queryParamsHandling,
preserveFragment: attrBoolValue(this.preserveFragment), preserveFragment: attrBoolValue(this.preserveFragment),
}); });
} }

View File

@ -7,7 +7,7 @@
*/ */
import {Location} from '@angular/common'; import {Location} from '@angular/common';
import {Compiler, ComponentFactoryResolver, Injector, NgModuleFactoryLoader, ReflectiveInjector, Type} from '@angular/core'; import {Compiler, ComponentFactoryResolver, Injector, NgModuleFactoryLoader, ReflectiveInjector, Type, isDevMode} from '@angular/core';
import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject'; import {Subject} from 'rxjs/Subject';
@ -22,7 +22,7 @@ import {mergeMap} from 'rxjs/operator/mergeMap';
import {reduce} from 'rxjs/operator/reduce'; import {reduce} from 'rxjs/operator/reduce';
import {applyRedirects} from './apply_redirects'; import {applyRedirects} from './apply_redirects';
import {ResolveData, Routes, validateConfig} from './config'; import {QueryParamsHandling, ResolveData, Routes, validateConfig} from './config';
import {createRouterState} from './create_router_state'; import {createRouterState} from './create_router_state';
import {createUrlTree} from './create_url_tree'; import {createUrlTree} from './create_url_tree';
import {RouterOutlet} from './directives/router_outlet'; import {RouterOutlet} from './directives/router_outlet';
@ -102,12 +102,26 @@ export interface NavigationExtras {
/** /**
* Preserves the query parameters for the next navigation. * Preserves the query parameters for the next navigation.
* *
* deprecated, use `queryParamsHandling` instead
*
* ``` * ```
* // Preserve query params from /results?page=1 to /view?page=1 * // Preserve query params from /results?page=1 to /view?page=1
* this.router.navigate(['/view'], { preserveQueryParams: true }); * this.router.navigate(['/view'], { preserveQueryParams: true });
* ``` * ```
*
* @deprecated
*/ */
preserveQueryParams?: boolean; preserveQueryParams?: boolean;
/**
* config strategy to handle the query parameters for the next navigation.
*
* ```
* // from /results?page=1 to /view?page=1&page=2
* this.router.navigate(['/view'], { queryParams: { page: 2 }, queryParamsHandling: "merge" });
* ```
*/
queryParamsHandling?: QueryParamsHandling;
/** /**
* Preserves the fragment for the next navigation * Preserves the fragment for the next navigation
* *
@ -465,11 +479,28 @@ export class Router {
* ``` * ```
*/ */
createUrlTree( createUrlTree(
commands: any[], {relativeTo, queryParams, fragment, preserveQueryParams, commands: any[], {relativeTo, queryParams, fragment, preserveQueryParams, queryParamsHandling,
preserveFragment}: NavigationExtras = {}): UrlTree { preserveFragment}: NavigationExtras = {}): UrlTree {
if (isDevMode() && preserveQueryParams && <any>console && <any>console.warn) {
console.warn('preserveQueryParams is deprecated, use queryParamsHandling instead.');
}
const a = relativeTo || this.routerState.root; const a = relativeTo || this.routerState.root;
const q = preserveQueryParams ? this.currentUrlTree.queryParams : queryParams;
const f = preserveFragment ? this.currentUrlTree.fragment : fragment; const f = preserveFragment ? this.currentUrlTree.fragment : fragment;
let q: Params = null;
if (queryParamsHandling) {
switch (queryParamsHandling) {
case 'merge':
q = merge(this.currentUrlTree.queryParams, queryParams);
break;
case 'preserve':
q = this.currentUrlTree.queryParams;
break;
default:
q = queryParams;
}
} else {
q = preserveQueryParams ? this.currentUrlTree.queryParams : queryParams;
}
return createUrlTree(a, this.currentUrlTree, commands, q, f); return createUrlTree(a, this.currentUrlTree, commands, q, f);
} }

View File

@ -1100,6 +1100,50 @@ describe('Integration', () => {
expect(native.getAttribute('href')).toEqual('/home?q=456#1'); expect(native.getAttribute('href')).toEqual('/home?q=456#1');
})); }));
it('should correctly use the preserve strategy', fakeAsync(() => {
@Component({
selector: 'someRoot',
template:
`<router-outlet></router-outlet><a routerLink="/home" [queryParams]="{q: 456}" queryParamsHandling="preserve">Link</a>`
})
class RootCmpWithLink {
}
TestBed.configureTestingModule({declarations: [RootCmpWithLink]});
const router: Router = TestBed.get(Router);
const fixture = createRoot(router, RootCmpWithLink);
router.resetConfig([{path: 'home', component: SimpleCmp}]);
const native = fixture.nativeElement.querySelector('a');
router.navigateByUrl('/home?a=123');
advance(fixture);
expect(native.getAttribute('href')).toEqual('/home?a=123');
}));
it('should correctly use the merge strategy', fakeAsync(() => {
@Component({
selector: 'someRoot',
template:
`<router-outlet></router-outlet><a routerLink="/home" [queryParams]="{q: 456}" queryParamsHandling="merge">Link</a>`
})
class RootCmpWithLink {
}
TestBed.configureTestingModule({declarations: [RootCmpWithLink]});
const router: Router = TestBed.get(Router);
const fixture = createRoot(router, RootCmpWithLink);
router.resetConfig([{path: 'home', component: SimpleCmp}]);
const native = fixture.nativeElement.querySelector('a');
router.navigateByUrl('/home?a=123');
advance(fixture);
expect(native.getAttribute('href')).toEqual('/home?a=123&q=456');
}));
it('should support using links on non-a tags', fakeAsync(inject([Router], (router: Router) => { it('should support using links on non-a tags', fakeAsync(inject([Router], (router: Router) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);

View File

@ -127,8 +127,9 @@ export declare class NavigationError {
export interface NavigationExtras { export interface NavigationExtras {
fragment?: string; fragment?: string;
preserveFragment?: boolean; preserveFragment?: boolean;
preserveQueryParams?: boolean; /** @deprecated */ preserveQueryParams?: boolean;
queryParams?: Params; queryParams?: Params;
queryParamsHandling?: QueryParamsHandling;
relativeTo?: ActivatedRoute; relativeTo?: ActivatedRoute;
replaceUrl?: boolean; replaceUrl?: boolean;
skipLocationChange?: boolean; skipLocationChange?: boolean;
@ -209,7 +210,7 @@ export declare class Router {
url: string; url: string;
urlHandlingStrategy: UrlHandlingStrategy; urlHandlingStrategy: UrlHandlingStrategy;
constructor(rootComponentType: Type<any>, urlSerializer: UrlSerializer, outletMap: RouterOutletMap, location: Location, injector: Injector, loader: NgModuleFactoryLoader, compiler: Compiler, config: Routes); constructor(rootComponentType: Type<any>, urlSerializer: UrlSerializer, outletMap: RouterOutletMap, location: Location, injector: Injector, loader: NgModuleFactoryLoader, compiler: Compiler, config: Routes);
createUrlTree(commands: any[], {relativeTo, queryParams, fragment, preserveQueryParams, preserveFragment}?: NavigationExtras): UrlTree; createUrlTree(commands: any[], {relativeTo, queryParams, fragment, preserveQueryParams, queryParamsHandling, preserveFragment}?: NavigationExtras): UrlTree;
dispose(): void; dispose(): void;
initialNavigation(): void; initialNavigation(): void;
isActive(url: string | UrlTree, exact: boolean): boolean; isActive(url: string | UrlTree, exact: boolean): boolean;
@ -245,6 +246,7 @@ export declare class RouterLink {
queryParams: { queryParams: {
[k: string]: any; [k: string]: any;
}; };
queryParamsHandling: QueryParamsHandling;
replaceUrl: boolean; replaceUrl: boolean;
routerLink: any[] | string; routerLink: any[] | string;
skipLocationChange: boolean; skipLocationChange: boolean;
@ -277,6 +279,7 @@ export declare class RouterLinkWithHref implements OnChanges, OnDestroy {
queryParams: { queryParams: {
[k: string]: any; [k: string]: any;
}; };
queryParamsHandling: QueryParamsHandling;
replaceUrl: boolean; replaceUrl: boolean;
routerLink: any[] | string; routerLink: any[] | string;
skipLocationChange: boolean; skipLocationChange: boolean;