RouterLink 指令简介
RouterLink 指令可以让你链接到应用程序的特定部分。若链接是静态的,我们可以按照以下的方式,来使用该指令:
<a routerLink="/user/bob">link to user component</a>
如果你需要使用动态值生成链接地址,你可以传递一个路径片段 (segments) 的数组,然后再传递每个段的参数。例如使用 ['/team',teamId,'user',userName,{details: true}]
数组,意味着我们想要生成一个链接到 /team/11/user/bob;details=true
。
多个静态段 (segments) 能够被合并为一个,例如 ['/team/11/user',{details: true}]
。
第一个路径片段可以以 /
,./
或 ../
开头:
如果以
/
开头,路由将从根路由开始查找如果以
./
开头或没有使用/
,则路由将从当前激活路由的子路由开始查找如果以
../
开头,路由往上一级查找
你可以使用以下方式设置查询参数和片段 (fragment):
<a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" fragment="education"> link to user component </a>
RouterLink 指令将基于以上设定的输入参数,生成如下链接:/user/bob#education?debug=true
。此外我们可以通过 queryParamsHandling
属性来声明如何处理查询参数,可用的选项是:
merge - 合并已有的
queryParams
到当前的queryParams
中preserve - 保存当前的
queryParams
default ('') - 仅使用查询参数
具体使用示例如下:
<a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" queryParamsHandling="merge"> link to user component </a>
RouterLink 指令详解
RouterLink 指令定义
@Directive({selector: ':not(a)[routerLink]'})
RouterLink 指令输入属性
// 设置URL相关的查询参数 @Input() queryParams: {[k: string]: any}; // 设置URL上的hash fragment @Input() fragment: string; // 设置查询参数处理方式:merge、preserve 、default @Input() queryParamsHandling: QueryParamsHandling; // 设置是否保留fragment @Input() preserveFragment: boolean; // 设置页面导航时,是否把新的状态添加到历史记录中 @Input() skipLocationChange: boolean; // 设置页面导航的同时,是否替换历史记录中的当前状态 @Input() replaceUrl: boolean; // 设置commands参数信息,如:['/user/bob'] @Input() set routerLink(commands: any[]|string) { if (commands != null) { this.commands = Array.isArray(commands) ? commands : [commands]; } else { this.commands = []; } }
RouterLink 指令绑定
事件绑定
// 监听RouterLink指令宿主元素的click事件,进行页面切换 @HostListener('click') onClick(): boolean { const extras = { skipLocationChange: attrBoolValue(this.skipLocationChange),replaceUrl: attrBoolValue(this.replaceUrl),}; this.router.navigateByUrl(this.urlTree,extras); return true; } // 转化设置的属性值为bool值 function attrBoolValue(s: any): boolean { return s === '' || !!s; }
RouterLink 类的构造函数
export class RouterLink { constructor( private router: Router,private route: ActivatedRoute,@Attribute('tabindex') tabIndex: string,renderer: Renderer,el: ElementRef) { if (tabIndex == null) { renderer.setElementAttribute(el.nativeElement,'tabindex','0'); } } }
@Attribute()
@Attribute('attributeName') 装饰器:用于获取指令宿主元素上 attributeName
属性名对应的属性值。
tabindex
tabindex 属性规定元素的 tab 键控制次序 (当 tab 键用于导航时)。
以下元素支持 tabindex 属性:<a>,<area>,<button>,<input>,<object>,<select> 以及 <textarea>
。
tabindex 语法:
<element tabindex="number"> <!-- number:规定元素的 tab 键控制次序 (1是第一个)-->
RouterLink 类的属性
// 用于保存commands参数信息 private commands: any[] = []; // 标识是否保存查询参数,4.0.0版本后用queryParamsHandling代替 private preserve: boolean;
RouterLink 类的方法
// 获取routerLink上配置信息对应的UrlTree get urlTree(): UrlTree { return this.router.createUrlTree(this.commands,{ relativeTo: this.route,queryParams: this.queryParams,fragment: this.fragment,preserveQueryParams: attrBoolValue(this.preserve),queryParamsHandling: this.queryParamsHandling,preserveFragment: attrBoolValue(this.preserveFragment),}); } // angular\packages\router\src\router.ts // 创建UrlTree createUrlTree( commands: any[],{relativeTo,queryParams,fragment,preserveQueryParams,queryParamsHandling,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 f = preserveFragment ? this.currentUrlTree.fragment : fragment; let q: Params|null = null; // 根据queryParamsHandling属性值,处理查询参数 if (queryParamsHandling) { switch (queryParamsHandling) { case 'merge': q = {...this.currentUrlTree.queryParams,...queryParams}; break; case 'preserve': q = this.currentUrlTree.queryParams; break; default: q = queryParams || null; } } else { q = preserveQueryParams ? this.currentUrlTree.queryParams : queryParams || null; } return createUrlTree(a,this.currentUrlTree,commands,q !,f !); }
RouterLinkWithHref 指令详解
RouterLinkWithHref 指令定义
@Directive({selector: 'a[routerLink]'})
RouterLinkWithHref 指令输入属性
// 设置a标签target的值 @Input() target: string; // 设置URL相关的查询参数 @Input() queryParams: {[k: string]: any}; // 设置URL上的hash fragment @Input() fragment: string; // 设置查询参数处理方式:merge、preserve 、default @Input() queryParamsHandling: QueryParamsHandling; // 设置是否保留fragment @Input() preserveFragment: boolean; // 设置页面导航时,是否把新的状态添加到历史记录中 @Input() skipLocationChange: boolean; // 设置页面导航的同时,是否替换历史记录中的当前状态 @Input() replaceUrl: boolean; // 设置commands信息,如:['/user/bob'] @Input() set routerLink(commands: any[]|string) { if (commands != null) { this.commands = Array.isArray(commands) ? commands : [commands]; } else { this.commands = []; } }
RouterLinkWithHref 指令绑定
属性绑定
@HostBinding('attr.target') @Input() target: string; @HostBinding() href: string;
<a>
标签定义超链接,用于从一个页面链接到另外一个页面。<a>
标签中有两个重要的属性:
事件绑定
// 监听RouterLink指令宿主元素的click事件,进行页面切换 @HostListener('click',['$event.button','$event.ctrlKey','$event.MetaKey']) onClick(button: number,ctrlKey: boolean,MetaKey: boolean): boolean { if (button !== 0 || ctrlKey || MetaKey) { return true; } if (typeof this.target === 'string' && this.target != '_self') { return true; } const extras = { skipLocationChange: attrBoolValue(this.skipLocationChange),}; this.router.navigateByUrl(this.urlTree,extras); return false; }
MouseEvent 表示用户与指针设备 (如鼠标) 交互时发生的事件,常见的事件包括:click、dblclick、mouseup 与 mousedown 事件。其中 MouseEvent 对象中包含一个 button
属性,用于表示用户按下的鼠标按键,可能的属性值如下:
0 - 主按键被按下,通常指鼠标左键。
1 - 辅助按键被按下,通常指鼠标滚轮。
2 - 次按键被按下,通常指鼠标右键。
3 - 第四个按钮被按下,通常指浏览器后退按钮。
4 - 第五个按钮被按下,通常指浏览器的前进按钮。
对于配置为左手使用的鼠标,按键操作将正好相反。此种情况下,从右至左读取值。在上面示例代码中,我们还访问了 MouseEvent
对象的 ctrlKey
和 MetaKey
属性,此外除了这两个属性外 MouseEvent
对象中还包含 altKey
和 shiftKey
属性。这些属性的相关说明如下:
MouseEvent.ctrlKey - 当鼠标事件触发时,如果
control
键被按下,则返回 true。MouseEvent.MetaKey - 当鼠标事件触发时,如果 (Window -
⊞
,Mac -⌘ Command
) 键被按下,则返回 true。MouseEvent.altKey - 当鼠标事件触发的时候,如果 (Window -
alt
,Mac -Option
或⌥
) 键被按下,返回true。MouseEvent.shiftKey - 当鼠标事件触发时,如果
shift
键被按下,则返回 true。
若按下 ctrlKey
,再点击 <a>
标签,则会使用当前的 URL 地址,新建一个新的 tab 页。若按下 MetaKey
,再点击 <a>
标签,则会重新刷新当前页。因此在 onClick()
方法中,才会执行相应的判断。
RouterLinkWithHref 指令生命周期
ngOnChanges()
// 输入属性发生变化时,更新a标签href属性 ngOnChanges(changes: {}): any { this.updateTargetUrlAndHref(); }
ngOnDestroy()
// 指令销毁时,取消路由事件的订阅 ngOnDestroy(): any { this.subscription.unsubscribe(); }
RouterLinkWithHref 类的构造函数
export class RouterLinkWithHref implements OnChanges,OnDestroy { constructor( private router: Router,private locationStrategy: LocationStrategy) { // 订阅路由事件,当页面切换成功后更新a标签的href属性 this.subscription = router.events.subscribe(s => { if (s instanceof NavigationEnd) { this.updateTargetUrlAndHref(); } }); } }
RouterLinkWithHref 类的属性
// 用于保存commands参数信息 private commands: any[] = []; // 用于保存取消订阅路由事件订阅的Subscription对象 private subscription: Subscription; // 标识是否保存查询参数,4.0.0版本后用queryParamsHandling代替 private preserve: boolean;
RouterLinkWithHref 类的方法
// 获取routerLink上配置信息对应的UrlTree get urlTree(): UrlTree { return this.router.createUrlTree(this.commands,}); } // 更新a标签href属性值 private updateTargetUrlAndHref(): void { this.href = this.locationStrategy .prepareExternalUrl(this.router.serializeUrl(this.urlTree)); }
RouterLinkActive 指令简介
RouterLinkActive 指令允许你在链接的路由变为活动状态时向元素添加 CSS 类。请看一下以下示例:
<a routerLink="/user/bob" routerLinkActive="active-link">Bob</a>
当 URL 地址是 /user
或 /user/bob
时,active-link
类将会被添加到 <a>
标签上。如果 URL 发生变化,则 active-link
类将自动从 <a>
标签上移除。你也可以一次性添加多个类,具体如下:
<a routerLink="/user/bob" routerLinkActive="class1 class2">Bob</a> <a routerLink="/user/bob" [routerLinkActive]="['class1','class2']">Bob</a>
在应用 routerLinkActive
指令时,你也可以通过 routerLinkActiveOptions
参数,来配置 URL 的匹配方式,具体如下:
<a routerLink="/user/bob" routerLinkActive="active-link" [routerLinkActiveOptions]="{exact: true}">Bob</a>
当配置了 {exact: true}
参数,仅当 URL 地址完全匹配时,active-link
类才会被添加到 <a>
标签上。此外你可以将 RouterLinkActive
实例分配给模板变量,并直接检查指令的 isActive
状态:
<a routerLink="/user/bob" routerLinkActive #rla="routerLinkActive"> Bob {{ rla.isActive ? '(already open)' : ''}} </a>
最后,你也可以将 RouterLinkActive 指令应用于 RouterLink 的父级元素。具体示例如下:
<div routerLinkActive="active-link" [routerLinkActiveOptions]="{exact: true}"> <a routerLink="/user/jim">Jim</a> <a routerLink="/user/bob">Bob</a> </div>
在上面示例中,当 URL 的地址为 /user/jim
或 /user/bob
时,active-link
类会被添加到对应的 <div>
元素上。
RouterLinkActive 指令详解
RouterLinkActive 指令定义
@Directive({ selector: '[routerLinkActive]',exportAs: 'routerLinkActive',})
RouterLinkActive 指令输入属性
// 设置处于激活状态时,宿主元素上应用的class信息 @Input() set routerLinkActive(data: string[]|string) { const classes = Array.isArray(data) ? data : data.split(' '); this.classes = classes.filter(c => !!c); } // 设置URL地址的匹配方式 @Input() routerLinkActiveOptions: {exact: boolean} = {exact: false};
RouterLinkActive 指令生命周期
ngAfterContentInit()
// 订阅RouterLink或RouterLinkWithHref集合的changes对象,从而自动更新宿主元素的class信息 ngAfterContentInit(): void { this.links.changes.subscribe(_ => this.update()); this.linksWithHrefs.changes.subscribe(_ => this.update()); this.update(); }
ngOnChanges()
// 输入属性变化时,更新宿主元素的class信息 ngOnChanges(changes: SimpleChanges): void { this.update(); }
ngOnDestroy()
// 指令销毁时,取消路由事件的订阅 ngOnDestroy(): void { this.subscription.unsubscribe(); }
RouterLinkActive 类的构造函数
export class RouterLinkActive implements OnChanges,OnDestroy,AfterContentInit { constructor( private router: Router,private element: ElementRef,private renderer: Renderer,private cdr: ChangeDetectorRef) { // 订阅路由事件,当页面切换成功后更新宿主元素上的class信息 this.subscription = router.events.subscribe(s => { if (s instanceof NavigationEnd) { this.update(); } }); } }
RouterLinkActive 类的属性
// 获取RouterLink集合 @ContentChildren(RouterLink,{descendants: true}) links: QueryList<RouterLink>; // 获取RouterLinkWithHref集合 @ContentChildren(RouterLinkWithHref,{descendants: true}) linksWithHrefs: QueryList<RouterLinkWithHref>; // 激活状态的样式列表 private classes: string[] = []; // 用于保存取消订阅路由事件订阅的Subscription对象 private subscription: Subscription; // 标识是否处于激活状态 private active: boolean = false;
RouterLinkActive 类的方法
// 获取激活状态 get isActive(): boolean { return this.active; } // 更新宿主元素的class信息 private update(): void { if (!this.links || !this.linksWithHrefs || !this.router.navigated) return; const hasActiveLinks = this.hasActiveLinks(); // react only when status has changed to prevent unnecessary dom updates if (this.active !== hasActiveLinks) { this.classes.forEach( c => this.renderer.setElementClass(this.element.nativeElement,c,hasActiveLinks)); Promise.resolve(hasActiveLinks).then(active => this.active = active); } } // 判断是否是激活的链接 private isLinkActive(router: Router): (link: (RouterLink|RouterLinkWithHref)) => boolean { return (link: RouterLink | RouterLinkWithHref) => router.isActive(link.urlTree,this.routerLinkActiveOptions.exact); } // 判断RouterLink或RouterLinkWithHref集合中是否含有激活的链接 private hasActiveLinks(): boolean { return this.links.some(this.isLinkActive(this.router)) || this.linksWithHrefs.some(this.isLinkActive(this.router)); }