feat: add RouterLink

This commit is contained in:
vsavkin 2016-05-24 14:33:34 -07:00
parent c9b4bcf689
commit 5b371736b2
4 changed files with 117 additions and 4 deletions

View File

@ -4,4 +4,6 @@ export { RouterState, ActivatedRoute } from './src/router_state';
export { RouterOutletMap } from './src/router_outlet_map'; export { RouterOutletMap } from './src/router_outlet_map';
import { RouterOutlet } from './src/directives/router_outlet'; import { RouterOutlet } from './src/directives/router_outlet';
export const ROUTER_DIRECTIVES = [RouterOutlet]; import { RouterLink } from './src/directives/router_link';
export const ROUTER_DIRECTIVES = [RouterOutlet, RouterLink];

View File

@ -0,0 +1,73 @@
import {
Directive,
HostListener,
HostBinding,
Input
} from '@angular/core';
import {Router} from '../router';
/**
* The RouterLink directive lets you link to specific parts of your app.
*
* Consider the following route configuration:
* ```
* [{ name: 'user', path: '/user', component: UserCmp }]
* ```
*
* When linking to this `User` route, you can write:
*
* ```
* <a [routerLink]="['/user']">link to user component</a>
* ```
*
* RouterLink expects the value to be an array of path segments, followed by the params
* for that level of routing. For instance `['/team', {teamId: 1}, 'user', {userId: 2}]`
* means that we want to generate a link to `/team;teamId=1/user;userId=2`.
*
* The first segment name can be prepended with `/`, `./`, or `../`.
* If the segment begins with `/`, the router will look up the route from the root of the app.
* If the segment begins with `./`, or doesn't begin with a slash, the router will
* instead look in the current component's children for the route.
* And if the segment begins with `../`, the router will go up one level.
*/
@Directive({selector: '[routerLink]'})
export class RouterLink {
@Input() target: string;
private commands: any[]|null = null;
private absoluteUrl: string|null = null;
// the url displayed on the anchor element.
@HostBinding() href: string;
constructor(private router: Router) {}
@Input()
set routerLink(data: any[] | string) {
if (Array.isArray(data)) {
this.commands = data;
this.absoluteUrl = null;
} else {
this.commands = null;
this.absoluteUrl = data;
}
this.updateTargetUrlAndHref();
}
@HostListener("click")
onClick(): boolean {
// If no target, or if target is _self, prevent default browser behavior
if (!(typeof this.target === "string") || this.target == '_self') {
this.router.navigateByUrl(this.absoluteUrl);
return false;
}
return true;
}
private updateTargetUrlAndHref(): void {
if (this.absoluteUrl) {
this.href = this.absoluteUrl;
}
}
}

View File

@ -67,6 +67,11 @@ export class Router {
this.config = config; this.config = config;
} }
/**
* @internal
*/
dispose(): void { this.locationSubscription.unsubscribe(); }
private setUpLocationChangeListener(): void { private setUpLocationChangeListener(): void {
this.locationSubscription = <any>this.location.subscribe((change) => { this.locationSubscription = <any>this.location.subscribe((change) => {
this.navigate(this.urlSerializer.parse(change['url']), change['pop']) this.navigate(this.urlSerializer.parse(change['url']), change['pop'])

View File

@ -153,20 +153,53 @@ describe("Integration", () => {
expect(fixture.debugElement.nativeElement).toHaveText(''); expect(fixture.debugElement.nativeElement).toHaveText('');
}))); })));
describe("router links", () => {
it("should support string router links",
fakeAsync(inject([Router, TestComponentBuilder], (router, tcb) => {
router.resetConfig([
{ name: 'team', path: 'team/:id', component: TeamCmp, children: [
{ name: 'link', path: 'link', component: StringLinkCmp },
{ name: 'simple', path: 'simple', component: SimpleCmp }
] }
]);
const fixture = tcb.createFakeAsync(RootCmp);
advance(fixture);
router.navigateByUrl('/team/22/link');
advance(fixture);
expect(fixture.debugElement.nativeElement).toHaveText('team 22 { link, right: }');
const native = fixture.debugElement.nativeElement.querySelector("a");
expect(native.getAttribute("href")).toEqual("/team/33/simple");
native.click();
advance(fixture);
expect(fixture.debugElement.nativeElement).toHaveText('team 33 { simple, right: }');
})));
});
}); });
@Component({
selector: 'link-cmp',
template: `<a routerLink="/team/33/simple">link</a>`,
directives: ROUTER_DIRECTIVES
})
class StringLinkCmp {}
@Component({ @Component({
selector: 'simple-cmp', selector: 'simple-cmp',
template: `simple`, template: `simple`,
directives: [ROUTER_DIRECTIVES] directives: ROUTER_DIRECTIVES
}) })
class SimpleCmp {} class SimpleCmp {
}
@Component({ @Component({
selector: 'team-cmp', selector: 'team-cmp',
template: `team {{id | async}} { <router-outlet></router-outlet>, right: <router-outlet name="right"></router-outlet> }`, template: `team {{id | async}} { <router-outlet></router-outlet>, right: <router-outlet name="right"></router-outlet> }`,
directives: [ROUTER_DIRECTIVES] directives: ROUTER_DIRECTIVES
}) })
class TeamCmp { class TeamCmp {
id: Observable<string>; id: Observable<string>;