Angular 4.x Router Link Directives

RouterLink 指令简介

RouterLink 指令可让你连接到应用程序的特定部分。若连接是静态的,咱们能够按照如下的方式,来使用该指令:html

<a routerLink="/user/bob">link to user component</a>

若是你须要使用动态值生成连接地址,你能够传递一个路径片断 (segments) 的数组,而后再传递每一个段的参数。例如使用 ['/team', teamId, 'user', userName, {details: true}] 数组,意味着咱们想要生成一个连接到 /team/11/user/bob;details=truereact

多个静态段 (segments) 可以被合并为一个,例如 ['/team/11/user', userName, {details: true}]typescript

第一个路径片断能够以 /./../ 开头:数组

  • 若是以 / 开头,路由将从根路由开始查找浏览器

  • 若是以 ./ 开头或没有使用 / ,则路由将从当前激活路由的子路由开始查找框架

  • 若是以 ../ 开头,路由往上一级查找dom

你可使用如下方式设置查询参数和片断 (fragment):函数

<a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" fragment="education">
   link to user component
</a>

RouterLink 指令将基于以上设定的输入参数,生成以下连接:/user/bob#education?debug=true 。此外咱们能够经过 queryParamsHandling 属性来声明如何处理查询参数,可用的选项是:this

  • merge - 合并已有的 queryParams 到当前的 queryParamsurl

  • 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> 标签中有两个重要的属性:

  • href - 规定连接指向的页面的 URL 地址。若是不使用 href 属性,则不可使用以下属性:download, media, rel, target 以及 type 属性。

  • target - 规定连接的页面在浏览器窗口中的打开方式,它的参数值主要有:

    • _blank - 在新的浏览器窗口中载入目标文档。

    • _parent - 这个目标使得文档载入父窗口或者包含来超连接引用的框架的框架集。若是这个引用是在窗口或者在顶级框架中,那么它与目标 _self 等效。

    • _self - 这个目标的值对全部没有指定目标的 <a> 标签是默认目标,它使得目标文档载入并显示在相同的框架或者窗口中做为源文档。这个目标是多余且没必要要的,除非和文档标题 <base> 标签中的 target 属性一块儿使用。

    • _top - 这个目标使得文档载入包含这个超连接的窗口,用 _top 目标将会清除全部被包含的框架并将文档载入整个浏览器窗口。

事件绑定

// 监听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),
      replaceUrl: attrBoolValue(this.replaceUrl),
    };
    this.router.navigateByUrl(this.urlTree, extras);
    return false;
}

MouseEvent 表示用户与指针设备 (如鼠标) 交互时发生的事件,常见的事件包括:click、dblclick、mouseup 与 mousedown 事件。其中 MouseEvent 对象中包含一个 button 属性,用于表示用户按下的鼠标按键,可能的属性值以下:

  • 0 - 主按键被按下,一般指鼠标左键。

  • 1 - 辅助按键被按下,一般指鼠标滚轮。

  • 2 - 次按键被按下,一般指鼠标右键。

  • 3 - 第四个按钮被按下,一般指浏览器后退按钮。

  • 4 - 第五个按钮被按下,一般指浏览器的前进按钮。

对于配置为左手使用的鼠标,按键操做将正好相反。此种状况下,从右至左读取值。在上面示例代码中,咱们还访问了 MouseEvent 对象的 ctrlKeymetaKey 属性,此外除了这两个属性外 MouseEvent 对象中还包含 altKeyshiftKey 属性。这些属性的相关说明以下:

  • 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 route: ActivatedRoute,
      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, {
      relativeTo: this.route,
      queryParams: this.queryParams,
      fragment: this.fragment,
      preserveQueryParams: attrBoolValue(this.preserve),
      queryParamsHandling: this.queryParamsHandling,
      preserveFragment: attrBoolValue(this.preserveFragment),
    });
}

// 更新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));
}