feat(router): update RouterLink to support query params and fragment

This commit is contained in:
vsavkin 2016-06-06 15:44:12 -07:00
parent b0e7c14545
commit 33b518ad21
3 changed files with 59 additions and 9 deletions

View File

@ -2,7 +2,8 @@ import {
Directive, Directive,
HostListener, HostListener,
HostBinding, HostBinding,
Input Input,
OnChanges
} from '@angular/core'; } from '@angular/core';
import {Router} from '../router'; import {Router} from '../router';
import {ActivatedRoute} from '../router_state'; import {ActivatedRoute} from '../router_state';
@ -33,9 +34,11 @@ import {ActivatedRoute} from '../router_state';
* And if the segment begins with `../`, the router will go up one level. * And if the segment begins with `../`, the router will go up one level.
*/ */
@Directive({selector: '[routerLink]'}) @Directive({selector: '[routerLink]'})
export class RouterLink { export class RouterLink implements OnChanges {
@Input() target: string; @Input() target: string;
private commands: any[] = []; private commands: any[] = [];
@Input() queryParams: {[k:string]:any};
@Input() fragment: string;
// the url displayed on the anchor element. // the url displayed on the anchor element.
@HostBinding() href: string; @HostBinding() href: string;
@ -48,25 +51,36 @@ export class RouterLink {
@Input() @Input()
set routerLink(data: any[] | string) { set routerLink(data: any[] | string) {
if (Array.isArray(data)) { if (Array.isArray(data)) {
this.commands = data; this.commands = <any>data;
} else { } else {
this.commands = [data]; this.commands = [data];
} }
}
ngOnChanges(changes:{}):any {
this.updateTargetUrlAndHref(); this.updateTargetUrlAndHref();
} }
@HostListener("click") @HostListener("click")
onClick(): boolean { onClick(): boolean {
// If no target, or if target is _self, prevent default browser behavior // If no target, or if target is _self, prevent default browser behavior
if (!(typeof this.target === "string") || this.target == '_self') { if (!(typeof this.target === "string") || this.target == '_self') {
this.router.navigate(this.commands, {relativeTo: this.route}); this.router.navigate(this.commands, {
relativeTo: this.route,
queryParameters: this.queryParams,
fragment: this.fragment
});
return false; return false;
} }
return true; return true;
} }
private updateTargetUrlAndHref(): void { private updateTargetUrlAndHref(): void {
const tree = this.router.createUrlTree(this.commands, {relativeTo: this.route}); const tree = this.router.createUrlTree(this.commands, {
relativeTo: this.route,
queryParameters: this.queryParams,
fragment: this.fragment
});
if (tree) { if (tree) {
this.href = this.router.serializeUrl(tree); this.href = this.router.serializeUrl(tree);
} }

View File

@ -193,7 +193,7 @@ export class Router {
navigate(commands: any[], extras: NavigationExtras = {}): Promise<boolean> { navigate(commands: any[], extras: NavigationExtras = {}): Promise<boolean> {
return this.scheduleNavigation(this.createUrlTree(commands, extras), false); return this.scheduleNavigation(this.createUrlTree(commands, extras), false);
} }
/** /**
* Serializes a {@link UrlTree} into a string. * Serializes a {@link UrlTree} into a string.
*/ */

View File

@ -36,8 +36,11 @@ describe("Integration", () => {
{provide: Location, useClass: SpyLocation}, {provide: Location, useClass: SpyLocation},
{ {
provide: Router, provide: Router,
useFactory: (resolver, urlSerializer, outletMap, location, injector) => useFactory: (resolver, urlSerializer, outletMap, location, injector) => {
new Router(RootCmp, resolver, urlSerializer, outletMap, location, injector, config), const r = new Router(RootCmp, resolver, urlSerializer, outletMap, location, injector, config);
r.initialNavigation();
return r;
},
deps: [ComponentResolver, UrlSerializer, RouterOutletMap, Location, Injector] deps: [ComponentResolver, UrlSerializer, RouterOutletMap, Location, Injector]
}, },
{provide: ActivatedRoute, useFactory: (r) => r.routerState.root, deps: [Router]}, {provide: ActivatedRoute, useFactory: (r) => r.routerState.root, deps: [Router]},
@ -389,6 +392,32 @@ describe("Integration", () => {
advance(fixture); advance(fixture);
expect(fixture.debugElement.nativeElement).toHaveText('link'); expect(fixture.debugElement.nativeElement).toHaveText('link');
}))); })));
it("should support query params and fragments",
fakeAsync(inject([Router, Location, TestComponentBuilder], (router, location, tcb) => {
router.resetConfig([
{ path: 'team/:id', component: TeamCmp, children: [
{ path: 'link', component: LinkWithQueryParamsAndFragment },
{ path: 'simple', component: SimpleCmp }
] }
]);
const fixture = tcb.createFakeAsync(RootCmp);
advance(fixture);
router.navigateByUrl('/team/22/link');
advance(fixture);
const native = fixture.debugElement.nativeElement.querySelector("a");
expect(native.getAttribute("href")).toEqual("/team/22/simple?q=1#f");
native.click();
advance(fixture);
expect(fixture.debugElement.nativeElement)
.toHaveText('team 22 { simple, right: }');
expect(location.path()).toEqual('/team/22/simple?q=1#f');
})));
}); });
describe("guards", () => { describe("guards", () => {
@ -603,6 +632,13 @@ class AbsoluteLinkCmp {}
}) })
class RelativeLinkCmp {} class RelativeLinkCmp {}
@Component({
selector: 'link-cmp',
template: `<a [routerLink]="['../simple']" [queryParams]="{q: '1'}" fragment="f">link</a>`,
directives: ROUTER_DIRECTIVES
})
class LinkWithQueryParamsAndFragment {}
@Component({ @Component({
selector: 'simple-cmp', selector: 'simple-cmp',
template: `simple`, template: `simple`,