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));
}
相關文章
相關標籤/搜索