Angular 教程:為英雄之旅添加路由支持-里程碑 1:起步

2022-07-04 13:57 更新

里程碑 1:起步

開(kāi)始本應(yīng)用的一個(gè)簡(jiǎn)版,它在兩個(gè)空路由之間導(dǎo)航。


創(chuàng)建一個(gè)范例應(yīng)用

  1. 創(chuàng)建一個(gè)新的 Angular 項(xiàng)目 angular-router-tour-of-heroes。
  2. ng new angular-router-tour-of-heroes

    當(dāng)系統(tǒng)提示 ?Would you like to add Angular routing?? 時(shí),選擇 ?N?。

    當(dāng)系統(tǒng)提示 ?Which stylesheet format would you like to use?? 時(shí),選擇 ?CSS?。

    片刻之后,一個(gè)新項(xiàng)目 ?angular-router-tour-of-heroes? 已準(zhǔn)備就緒。

  3. 從你的終端,導(dǎo)航到 ?angular-router-tour-of-heroes? 目錄。
  4. 運(yùn)行 ?ng serve? 來(lái)驗(yàn)證新應(yīng)用是否正常運(yùn)行。
  5. ng serve
  6. 打開(kāi)瀏覽器訪(fǎng)問(wèn) ?http://localhost:4200?。
  7. 你會(huì)發(fā)現(xiàn)本應(yīng)用正運(yùn)行在瀏覽器中。

定義路由

路由器必須用“路由定義”的列表進(jìn)行配置。

每個(gè)定義都被翻譯成了一個(gè)Route對(duì)象。該對(duì)象有一個(gè) ?path ?字段,表示該路由中的 URL 路徑部分,和一個(gè) ?component ?字段,表示與該路由相關(guān)聯(lián)的組件。

當(dāng)瀏覽器的 URL 變化時(shí)或在代碼中告訴路由器導(dǎo)航到一個(gè)路徑時(shí),路由器就會(huì)翻出它用來(lái)保存這些路由定義的注冊(cè)表。

第一個(gè)路由執(zhí)行以下操作:

  • 當(dāng)瀏覽器地址欄的 URL 變化時(shí),如果它匹配上了路徑部分 ?/crisis-center?,路由器就會(huì)激活一個(gè) ?CrisisListComponent ?的實(shí)例,并顯示它的視圖。
  • 當(dāng)應(yīng)用程序請(qǐng)求導(dǎo)航到路徑 ?/crisis-center? 時(shí),路由器激活一個(gè) ?CrisisListComponent ?的實(shí)例,顯示它的視圖,并將該路徑更新到瀏覽器地址欄和歷史。

第一個(gè)配置定義了由兩個(gè)路由構(gòu)成的數(shù)組,它們用最短路徑指向了 ?CrisisListComponent ?和 ?HeroListComponent?。

生成 ?CrisisList ?和 ?HeroList ?組件,以便路由器能夠渲染它們。

ng generate component crisis-list
ng generate component hero-list

把每個(gè)組件的內(nèi)容都替換成下列范例 HTML。

  • src/app/crisis-list/crisis-list.component.html
  • <h2>CRISIS CENTER</h2>
    <p>Get your crisis here</p>
  • src/app/hero-list/hero-list.component.html
  • <h2>HEROES</h2>
    <p>Get your heroes here</p>

注冊(cè) Router 和 Routes

為了使用 ?Router?,你必須注冊(cè)來(lái)自 ?@angular/router? 包中的 ?RouterModule?。定義一個(gè)路由數(shù)組 ?appRoutes?,并把它傳給 ?RouterModule.forRoot()? 方法。?RouterModule.forRoot()? 方法會(huì)返回一個(gè)模塊,其中包含配置好的 ?Router ?服務(wù)提供者,以及路由庫(kù)所需的其它提供者。一旦啟動(dòng)了應(yīng)用,?Router ?就會(huì)根據(jù)當(dāng)前的瀏覽器 URL 進(jìn)行首次導(dǎo)航。

注意:
?RouterModule.forRoot()? 方法是用于注冊(cè)全應(yīng)用級(jí)提供者的編碼模式。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { RouterModule, Routes } from '@angular/router';

import { AppComponent } from './app.component';
import { CrisisListComponent } from './crisis-list/crisis-list.component';
import { HeroListComponent } from './hero-list/hero-list.component';

const appRoutes: Routes = [
  { path: 'crisis-center', component: CrisisListComponent },
  { path: 'heroes', component: HeroListComponent },
];

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(
      appRoutes,
      { enableTracing: true } // <-- debugging purposes only
    )
  ],
  declarations: [
    AppComponent,
    HeroListComponent,
    CrisisListComponent,
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }
對(duì)于最小化的路由配置,把配置好的 ?RouterModule ?添加到 ?AppModule ?中就足夠了。但是,隨著應(yīng)用的成長(zhǎng),你將需要將路由配置重構(gòu)到單獨(dú)的文件中,并創(chuàng)建路由模塊,路由模塊是一種特殊的、專(zhuān)做路由的服務(wù)模塊。

把 ?RouterModule.forRoot()? 注冊(cè)到 ?AppModule ?的 ?imports ?數(shù)組中,能讓該 ?Router ?服務(wù)在應(yīng)用的任何地方都能使用。

添加路由出口

根組件 ?AppComponent ?是本應(yīng)用的殼。它在頂部有一個(gè)標(biāo)題、一個(gè)帶兩個(gè)鏈接的導(dǎo)航條,在底部有一個(gè)路由器出口,路由器會(huì)在它所指定的位置上渲染各個(gè)組件。


路由出口扮演一個(gè)占位符的角色,表示路由組件將會(huì)渲染到哪里。

該組件所對(duì)應(yīng)的模板是這樣的:

<h1>Angular Router</h1>
<nav>
  <a routerLink="/crisis-center" routerLinkActive="active" ariaCurrentWhenActive="page">Crisis Center</a>
  <a routerLink="/heroes" routerLinkActive="active" ariaCurrentWhenActive="page">Heroes</a>
</nav>
<router-outlet></router-outlet>

定義通配符路由

你以前在應(yīng)用中創(chuàng)建過(guò)兩個(gè)路由,一個(gè)是 ?/crisis-center?,另一個(gè)是 ?/heroes?。所有其它 URL 都會(huì)導(dǎo)致路由器拋出錯(cuò)誤,并讓?xiě)?yīng)用崩潰。

可以添加一個(gè)通配符路由來(lái)攔截所有無(wú)效的 URL,并優(yōu)雅的處理它們。 通配符路由的 ?path ?是兩個(gè)星號(hào)(?**?),它會(huì)匹配任何 URL。 而當(dāng)路由器匹配不上以前定義的那些路由時(shí),它就會(huì)選擇這個(gè)通配符路由。 通配符路由可以導(dǎo)航到自定義的“404 Not Found”組件,也可以重定向到一個(gè)現(xiàn)有路由。

路由器會(huì)使用先到先得的策略來(lái)選擇路由。由于通配符路由是最不具體的那個(gè),因此務(wù)必確保它是路由配置中的最后一個(gè)路由。

要測(cè)試本特性,請(qǐng)往 ?HeroListComponent ?的模板中添加一個(gè)帶 ?RouterLink ?的按鈕,并且把它的鏈接設(shè)置為一個(gè)不存在的路由 ?"/sidekicks"?。

<h2>HEROES</h2>
<p>Get your heroes here</p>

<button type="button" routerLink="/sidekicks">Go to sidekicks</button>

當(dāng)用戶(hù)點(diǎn)擊該按鈕時(shí),應(yīng)用就會(huì)失敗,因?yàn)槟闵形炊x過(guò) ?"/sidekicks"? 路由。

不要添加 ?"/sidekicks"? 路由,而是定義一個(gè)“通配符”路由,讓它導(dǎo)航到 ?PageNotFoundComponent ?組件。

{ path: '**', component: PageNotFoundComponent }

創(chuàng)建 ?PageNotFoundComponent?,以便在用戶(hù)訪(fǎng)問(wèn)無(wú)效網(wǎng)址時(shí)顯示它。

ng generate component page-not-found
<h2>Page not found</h2>

現(xiàn)在,當(dāng)用戶(hù)訪(fǎng)問(wèn) ?/sidekicks? 或任何無(wú)效的 URL 時(shí),瀏覽器就會(huì)顯示“Page not found”。瀏覽器的地址欄仍指向無(wú)效的 URL。

設(shè)置跳轉(zhuǎn)

應(yīng)用啟動(dòng)時(shí),瀏覽器地址欄中的初始 URL 默認(rèn)是這樣的:

localhost:4200

它不能匹配上任何硬編碼進(jìn)來(lái)的路由,于是就會(huì)走到通配符路由中去,并且顯示 ?PageNotFoundComponent?。

這個(gè)應(yīng)用需要一個(gè)有效的默認(rèn)路由,在這里應(yīng)該用英雄列表作為默認(rèn)頁(yè)。當(dāng)用戶(hù)點(diǎn)擊"Heroes"鏈接或把 ?localhost:4200/heroes? 粘貼到地址欄時(shí),它應(yīng)該導(dǎo)航到列表頁(yè)。

添加一個(gè) ?redirect ?路由,把最初的相對(duì) URL(?''?)轉(zhuǎn)換成所需的默認(rèn)路徑(?/heroes?)。

在通配符路由上方添加一個(gè)默認(rèn)路由。在下方的代碼片段中,它出現(xiàn)在通配符路由的緊上方,展示了這個(gè)里程碑的完整 ?appRoutes?。

const appRoutes: Routes = [
  { path: 'crisis-center', component: CrisisListComponent },
  { path: 'heroes',        component: HeroListComponent },
  { path: '',   redirectTo: '/heroes', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];

瀏覽器的地址欄會(huì)顯示 ?.../heroes?,好像你直接在那里導(dǎo)航一樣。

重定向路由需要一個(gè) ?pathMatch ?屬性,來(lái)告訴路由器如何用 URL 去匹配路由的路徑。在本應(yīng)用中,路由器應(yīng)該只有在完整的 URL等于 ?''? 時(shí)才選擇 ?HeroListComponent ?組件,因此要把 ?pathMatch ?設(shè)置為 ?'full'?。

聚焦 PATHMATCH
從技術(shù)角度看,?pathMatch = 'full'? 會(huì)導(dǎo)致 URL 中剩下的、未匹配的部分必須等于 ?''?。在這個(gè)例子中,跳轉(zhuǎn)路由在一個(gè)頂層路由中,因此剩下的URL 和完整的URL 是一樣的。
?pathMatch ?的另一個(gè)可能的值是 ?'prefix'?,它會(huì)告訴路由器:當(dāng)剩下的URL 以這個(gè)跳轉(zhuǎn)路由中的 ?prefix ?值開(kāi)頭時(shí),就會(huì)匹配上這個(gè)跳轉(zhuǎn)路由。但這不適用于此示例應(yīng)用,因?yàn)槿绻?nbsp;?pathMatch ?值是 ?'prefix'?,那么每個(gè) URL 都會(huì)匹配 ?''?。
嘗試把它設(shè)置為 ?'prefix'?,并點(diǎn)擊 ?Go to sidekicks? 按鈕。這是因?yàn)樗且粋€(gè)無(wú)效 URL,本應(yīng)顯示“Page not found”頁(yè)。但是,你仍然在“英雄列表”頁(yè)中。在地址欄中輸入一個(gè)無(wú)效的 URL,你又被路由到了 ?/heroes?。每一個(gè) URL,無(wú)論有效與否,都會(huì)匹配上這個(gè)路由定義。
默認(rèn)路由應(yīng)該只有在整個(gè) URL 等于 ?''? 時(shí)才重定向到 ?HeroListComponent?,別忘了把重定向路由設(shè)置為 ?pathMatch = 'full'?。

里程碑 1 小結(jié)

當(dāng)用戶(hù)單擊某個(gè)鏈接時(shí),該示例應(yīng)用可以在兩個(gè)視圖之間切換。

里程碑 1 涵蓋了以下幾點(diǎn)的做法:

  • 加載路由庫(kù)
  • 往殼組件的模板中添加一個(gè)導(dǎo)航條,導(dǎo)航條中有一些 A 標(biāo)簽、?routerLink ?指令和 ?routerLinkActive ?指令
  • 往殼組件的模板中添加一個(gè) ?router-outlet? 指令,視圖將會(huì)被顯示在那里
  • 用 ?RouterModule.forRoot()? 配置路由器模塊
  • 設(shè)置路由器,使其合成 HTML5 模式的瀏覽器 URL
  • 使用通配符路由來(lái)處理無(wú)效路由
  • 當(dāng)應(yīng)用在空路徑下啟動(dòng)時(shí),導(dǎo)航到默認(rèn)路由

這個(gè)初學(xué)者應(yīng)用的結(jié)構(gòu)是這樣的:


下面是本里程碑中的文件列表。

  • app.component.html
  • <h1>Angular Router</h1>
    <nav>
      <a routerLink="/crisis-center" routerLinkActive="active" ariaCurrentWhenActive="page">Crisis Center</a>
      <a routerLink="/heroes" routerLinkActive="active" ariaCurrentWhenActive="page">Heroes</a>
    </nav>
    <router-outlet></router-outlet>
  • app.module.ts
  • import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { FormsModule } from '@angular/forms';
    import { RouterModule, Routes } from '@angular/router';
    
    import { AppComponent } from './app.component';
    import { CrisisListComponent } from './crisis-list/crisis-list.component';
    import { HeroListComponent } from './hero-list/hero-list.component';
    import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
    
    const appRoutes: Routes = [
      { path: 'crisis-center', component: CrisisListComponent },
      { path: 'heroes', component: HeroListComponent },
    
      { path: '',   redirectTo: '/heroes', pathMatch: 'full' },
      { path: '**', component: PageNotFoundComponent }
    ];
    
    @NgModule({
      imports: [
        BrowserModule,
        FormsModule,
        RouterModule.forRoot(
          appRoutes,
          { enableTracing: true } // <-- debugging purposes only
        )
      ],
      declarations: [
        AppComponent,
        HeroListComponent,
        CrisisListComponent,
        PageNotFoundComponent
      ],
      bootstrap: [ AppComponent ]
    })
    export class AppModule { }
  • hero-list/hero-list.component.html
  • <h2>HEROES</h2>
    <p>Get your heroes here</p>
    
    <button type="button" routerLink="/sidekicks">Go to sidekicks</button>
  • crisis-list/crisis-list.component.html
  • <h2>CRISIS CENTER</h2>
    <p>Get your crisis here</p>
  • page-not-found/page-not-found.component.html
  • <h2>Page not found</h2>
  • index.html
  • <html lang="en">
      <head>
        <!-- Set the base href -->
        <base href="/">
        <title>Angular Router</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
      </head>
    
      <body>
        <app-root></app-root>
      </body>
    
    </html>
以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)