Angular Router的组件路由介绍
這是SitePoint Angular 2+教程的第4部分,有關如何使用Angular CLI創建CRUD應用程序。 在本文中,我們將介紹Angular Router,并了解當瀏覽器URL更改時它如何更新我們的應用程序,反之亦然。 我們還將學習如何使用路由器更新應用程序,以解析來自后端API的數據。
在第一部分中,我們學習了如何啟動和運行Todo應用程序并將其部署到GitHub頁面。 這樣做很好,但是不幸的是,整個應用程序都擠在一個組件中。
在第二部分中,我們研究了模塊化程度更高的組件體系結構,并學習了如何將單個組件分解為較小的組件的結構化樹,這些樹更易于理解,重用和維護。
在第三部分中,我們更新了應用程序以使用RxJS和Angular的HTTP服務與REST API后端進行通信。
不用擔心 您無需遵循本教程的第一部分,第二部分或第三部分,就可以使第四部分有意義。 您可以簡單地獲取一份我們的倉庫的副本,從第三部分中檢出代碼,然后以此為起點。 下面將對此進行詳細說明。
啟動并運行
確保已安裝最新版本的Angular CLI。 如果沒有安裝,則可以使用以下命令進行安裝:
npm install -g @angular/cli@latest如果您需要刪除以前版本的Angular CLI,則可以執行以下操作:
npm uninstall -g @angular/cli angular-cli npm cache clean npm install -g @angular/cli@latest之后,您將需要第三部分的代碼副本。 可以在GitHub上找到 。 本系列中的每篇文章在存儲庫中都有一個相應的標記,因此您可以在應用程序的不同狀態之間來回切換。
我們在第三部分結尾并且在本文開始的代碼被標記為第三部分 。 本文結尾處的代碼被標記為part-4 。
您可以將標簽視為特定提交ID的別名。 您可以使用git checkout在它們之間切換。 您可以在此處內容 。
因此,要啟動并運行(安裝了最新版本的Angular CLI),我們可以這樣做:
git clone git@github.com:sitepoint-editors/angular-todo-app.git cd angular-todo-app git checkout part-3 npm install ng serve然后訪問http:// localhost:4200 / 。 如果一切順利,您應該會看到正在運行的Todo應用程序。
快速回顧
這是第3部分結尾處的應用程序體系結構:
在本文中,我們將:
- 了解為什么應用程序可能需要路由
- 了解什么是JavaScript路由器
- 了解什么是Angular Router,如何工作以及可以為您做什么
- 設置Angular Router并為我們的應用程序配置路由
- 創建一個解析器以從我們的REST API獲取待辦事項
- 使用新的解析器更新我們的應用程序以獲取待辦事項。
到本文結尾,您將了解:
- 什么時候以及為什么您的應用程序可能需要路由
- 服務器上的路由和瀏覽器中的路由之間的區別
- 什么是Angular Router以及它可以為您的應用程序做什么
- 如何設置角度路由器
- 如何為您的應用程序配置路由
- 如何告訴Angular Router在DOM中放置組件的位置
- 如何正常處理未知URL
- 什么是解析器及其用途
- 如何使用解析器使用Angular Router解析數據。
所以,讓我們開始吧!
為什么要布線?
在當前狀態下,我們的Web應用程序未考慮瀏覽器URL。
我們通過一個URL(例如http://localhost:4200訪問我們的應用程序,而我們的應用程序不知道其他任何URL(例如http://localhost:4200/todos 。
大多數Web應用程序需要支持不同的URL才能將用戶導航到應用程序中的不同頁面。 那就是路由器進來的地方。
在傳統網站中,路由是由服務器上的路由器處理的:
在現代JavaScript Web應用程序中,路由通常由瀏覽器中的JavaScript路由器處理。
什么是JavaScript路由器?
本質上,JavaScript路由器執行以下兩項操作:
JavaScript路由器使我們能夠開發單頁應用程序(SPA)。
SPA是一種Web應用程序,可提供類似于桌面應用程序的用戶體驗。 在SPA中,與后端的所有通信都在后臺進行。
當用戶從一個頁面導航到另一頁面時,即使URL更改了,頁面也將動態更新而無需重新加載。
有許多不同的JavaScript路由器實現。
其中一些是為特定的JavaScript框架(例如Angular , Ember , React , Vue.js和Aurelia等)專門編寫的。其他實現是出于通用目的而構建的,并不與特定的框架綁定。
什么是角路由器?
Angular Router是由Angular Core團隊編寫和維護的官方Angular路由庫。
這是一個JavaScript路由器實現,旨在與Angular一起使用,并打包為@angular/router 。
首先,Angular Router負責JavaScript路由器的職責:
- 當用戶導航到某個URL時,它將激活所有必需的Angular組件以組成一個頁面
- 它使用戶可以從一頁導航到另一頁而無需重新加載頁面
- 它會更新瀏覽器的歷史記錄,以便用戶在頁面之間來回導航時可以使用后退和前進按鈕。
此外,Angular Router還使我們能夠:
- 將一個URL重定向到另一個URL
- 在顯示頁面之前解析數據
- 激活或停用頁面時運行腳本
- 我們應用程序的延遲加載部分。
在本文中,我們將學習如何設置和配置Angular Router,如何重定向URL以及如何使用Angular Router通過后端API解析待辦事項。
在下一篇文章中,我們將向我們的應用程序添加身份驗證,并使用路由器確保只有在用戶登錄后才能訪問某些頁面。
角路由器如何工作
在深入研究代碼之前,了解Angular Router的工作方式及其引入的術語非常重要。
當用戶導航到頁面時,Angular Router依次執行以下步驟:
為了完成其任務,Angular Router引入了以下術語和概念:
- 路由器服務 :我們應用程序中的全局Angular路由器服務
- 路由器配置 :定義我們的應用程序可以位于的所有可能的路由器狀態
- 路由器狀態 :路由器在某個時間點的狀態,表示為激活的路由快照的樹
- 激活的路由快照 :提供對路由器狀態節點的URL,參數和數據的訪問
- guard :在加載,激活或停用路由時運行的腳本
- 解析器 :在激活請求的頁面之前獲取數據的腳本
- 路由器出口 :Angular Router可以在其中放置激活組件的DOM中的位置。
不用擔心該術語聽起來很壓倒性。 當我們在本系列中逐步解決這些術語時,您會習慣這些術語,并且您在Angular Router上獲得了更多的經驗。
使用Angular路由器的Angular應用程序只有一個路由器服務實例:這是一個單例。 無論何時何地在應用程序中注入Router服務,您都可以訪問相同的Angular Router服務實例。
要更深入地了解Angular路由過程,請確保檢查出Angular Router導航的7步路由過程 。
啟用路由
要在Angular應用程序中啟用路由,我們需要做三件事:
因此,讓我們從創建路由配置開始。
創建路由配置
要創建路由配置,我們需要我們希望應用程序支持的URL列表。
當前,我們的應用程序非常簡單,只有一個頁面顯示待辦事項列表:
- / :顯示待辦事項清單
它將待辦事項列表顯示為我們應用程序的主頁。
但是,當用戶在瀏覽器中為/書簽以查看待辦事項列表時,如果我們更改了主頁的內容(我們將在本系列的第5部分中進行此操作),則他們的書簽將不再顯示待辦事項列表。
因此,讓我們給待辦事項列出自己的URL并將我們的主頁重定向到該URL:
- / :重定向到/todos
- /todos :顯示待辦事項列表。
這為我們提供了兩個好處:
- 當用戶將待辦事項頁面添加為書簽時,即使我們更改了主頁內容,他們的瀏覽器也會將/todos而不是/書簽,這將繼續按預期工作
- 現在,我們可以通過將其重定向到我們喜歡的任何URL來輕松地更改主頁,如果您需要定期更改主頁內容,這將非常方便。
官方Angular樣式指南建議將Angular模塊的路由配置存儲在文件名中,該文件名以-routing.module.ts結尾,該文件將導出一個單獨的Angular模塊,其名稱以RoutingModule結尾。
我們當前的模塊稱為AppModule ,因此我們創建一個文件src/app/app-routing.module.ts并將路由配置導出為一個名為AppRoutingModule的Angular模塊:
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AppComponent } from './app.component';const routes: Routes = [{path: '',redirectTo: 'todos',pathMatch: 'full'},{path: 'todos',component: AppComponent} ];@NgModule({imports: [RouterModule.forRoot(routes)],exports: [RouterModule],providers: [] }) export class AppRoutingModule { }首先,我們從@angular/router導入RouterModule和Routes :
import { RouterModule, Routes } from '@angular/router';接下來,我們定義類型為Routes的可變routes ,并為其分配路由器配置:
const routes: Routes = [{path: '',redirectTo: 'todos',pathMatch: 'full'},{path: 'todos',component: AppComponent} ];Routes類型是可選的,可讓具有TypeScript支持的IDE或TypeScript編譯器在開發過程中方便地驗證您的路由配置。
路由器配置代表我們的應用程序可以處于的所有可能的路由器狀態。
它是路由樹,定義為JavaScript數組,其中每個路由可以具有以下屬性:
- path :字符串,匹配URL的路徑
- pathMatch :字符串,如何匹配URL
- component :類引用,激活此路由時要激活的組件
- redirectTo :字符串,激活此路由后重定向到的URL
- data :分配給路由的靜態數據
- resolve : 解決動態數據并在解析后與數據合并
- 兒童 :兒童路線。
我們的應用程序很簡單,僅包含兩個同級路由,但是較大的應用程序可能具有帶有子路由的路由器配置,例如:
const routes: Routes = [{path: '',redirectTo: 'todos',pathMatch: 'full'},{path: 'todos',children: [{path: '',component: 'TodosPageComponent'},{path: ':id',component: 'TodoPageComponent'}]} ];在這里, todos有兩個子路由,而:id是一個路由參數,使路由器能夠識別以下URL:
- / :主頁,重定向到/todos
- /todos :激活TodosPageComponent并顯示TodosPageComponent列表
- /todos/1 :激活TodoPageComponent并將:id參數的值設置為1
- /todos/2 :激活TodoPageComponent并將:id參數的值設置為2 。
注意定義重定向時我們如何指定pathMatch: 'full' 。
Angular Router具有兩種匹配策略:
- prefix :默認值,當URL 以 path的值開頭時匹配
- full :URL 等于 path的值時匹配。
我們可以創建以下路線:
// no pathMatch specified, so Angular Router applies // the default `prefix` pathMatch {path: '',redirectTo: 'todos' }在這種情況下,Angular Router應用默認的prefix路徑匹配策略,并且每個URL都重定向到todos因為每個URL都以path指定的空字符串'' 開頭 。
我們只希望將主頁重定向到todos ,因此我們添加pathMatch: 'full'以確保僅匹配等于空字符串''的URL:
{path: '',redirectTo: 'todos',pathMatch: 'full' }要了解有關不同的路由配置選項的更多信息,請查看有關路由和導航的正式Angular文檔。
最后,我們創建并導出一個Angular模塊AppRoutingModule :
@NgModule({imports: [RouterModule.forRoot(routes)],exports: [RouterModule],providers: [] }) export class AppRoutingModule { }有兩種創建路由模塊的方法:
當您的應用程序具有多個路由模塊時,需要RouterModule.forChild()方法。
請記住,路由器服務負責應用程序狀態和瀏覽器URL之間的同步。 實例化與同一瀏覽器URL交互的多個路由器服務會導致問題,因此,無論我們在應用程序中導入了多少個路由模塊,我們的應用程序中只有一個路由器服務實例非常重要。
當我們導入使用RouterModule.forRoot()創建的路由模塊時,Angular將實例化路由器服務。 當我們導入使用RouterModule.forChild()創建的路由模塊時,Angular將不會實例化路由器服務。
因此,對于其他路由模塊,我們只能使用一次RouterModule.forRoot() ,并多次使用RouterModule.forChild() 。
因為我們的應用程序只有一個路由模塊,所以我們使用RouterModule.forRoot() :
imports: [RouterModule.forRoot(routes)]另外,我們還在exports屬性中指定RouterModule :
exports: [RouterModule]這確保了我們沒有明確導入RouterModule再次AppModule時AppModule進口AppRoutingModule 。
現在我們有了AppRoutingModule ,我們需要將其導入到AppModule以啟用它。
導入路由配置
要將路由配置導入到我們的應用程序中,我們必須將AppRoutingModule導入到主AppModule 。
讓我們打開src/app/app.module.ts并將AppRoutingModule添加到AppModule的@NgModule元數據中的imports數組中:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http';import { AppComponent } from './app.component'; import { TodoListComponent } from './todo-list/todo-list.component'; import { TodoListFooterComponent } from './todo-list-footer/todo-list-footer.component'; import { TodoListHeaderComponent } from './todo-list-header/todo-list-header.component'; import { TodoDataService } from './todo-data.service'; import { TodoListItemComponent } from './todo-list-item/todo-list-item.component'; import { ApiService } from './api.service'; import { AppRoutingModule } from './app-routing.module';@NgModule({declarations: [AppComponent,TodoListComponent,TodoListFooterComponent,TodoListHeaderComponent,TodoListItemComponent],imports: [AppRoutingModule,BrowserModule,FormsModule,HttpModule],providers: [TodoDataService, ApiService],bootstrap: [AppComponent] }) export class AppModule { }由于AppRoutingModule有RoutingModule在其上市exports財產,角將導入RoutingModule自動當我們導入AppRoutingModule ,所以我們沒有明確導入RouterModule再次(雖然這樣做不會造成任何傷害)。
在嘗試在瀏覽器中進行更改之前,我們需要完成第三步也是最后一步。
添加路由器插座
盡管我們的應用程序現在具有路由配置,但是我們仍然需要告訴Angular Router它將實例化的組件放置在DOM中的位置。
當我們的應用程序被引導時,Angular實例化AppComponent因為AppComponent的bootstrap屬性中列出了AppModule :
@NgModule({// ...bootstrap: [AppComponent] }) export class AppModule { }為了告訴Angular Router可以在哪里放置組件,我們必須在AppComponent的HTML模板中添加<router-outlet></router-outlet>元素。
<router-outlet></router-outlet>元素告訴Angular Router可以在哪里實例化DOM中的組件。
如果您熟悉AngularJS 1.x路由器和UI-Router ,可以考慮<router-outlet></router-outlet>替代ng-view和ui-view的Angular。
如果沒有<router-outlet></router-outlet>元素,Angular Router將不知道組件的放置位置,并且僅AppComponent自己的HTML。
AppComponent當前顯示AppComponent列表。
但是,我們現在不希望AppComponent顯示AppComponent列表,而是希望AppComponent包含<router-outlet></router-outlet>并告訴Angular Router實例化AppComponent內部的另一個組件以顯示AppComponent列表。
為此,讓我們使用Angular CLI生成一個新組件TodosComponent :
$ ng generate component Todos讓我們還將所有HTML從src/app/app.component.html移到src/app/todos/todos.component.html :
<!-- src/app/todos/todos.component.html --> <section class="todoapp"><app-todo-list-header(add)="onAddTodo($event)"></app-todo-list-header><app-todo-list[todos]="todos"(toggleComplete)="onToggleTodoComplete($event)"(remove)="onRemoveTodo($event)"></app-todo-list><app-todo-list-footer[todos]="todos"></app-todo-list-footer> </section>讓我們還將所有邏輯從src/app/app.component.ts移到src/app/todos/todos.component.ts :
/* src/app/todos/todos.component.ts */ import { Component, OnInit } from '@angular/core'; import { TodoDataService } from '../todo-data.service'; import { Todo } from '../todo';@Component({selector: 'app-todos',templateUrl: './todos.component.html',styleUrls: ['./todos.component.css'],providers: [TodoDataService] }) export class TodosComponent implements OnInit {todos: Todo[] = [];constructor(private todoDataService: TodoDataService) {}public ngOnInit() {this.todoDataService.getAllTodos().subscribe((todos) => {this.todos = todos;});}onAddTodo(todo) {this.todoDataService.addTodo(todo).subscribe((newTodo) => {this.todos = this.todos.concat(newTodo);});}onToggleTodoComplete(todo) {this.todoDataService.toggleTodoComplete(todo).subscribe((updatedTodo) => {todo = updatedTodo;});}onRemoveTodo(todo) {this.todoDataService.deleteTodoById(todo.id).subscribe((_) => {this.todos = this.todos.filter((t) => t.id !== todo.id);});} }現在,我們可以更換AppComponent的模板中src/app/app.component.html有:
<router-outlet></router-outlet>我們也可以刪除所有過時代碼AppComponent的類src/app/app.component.ts :
import { Component } from '@angular/core';@Component({selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css'], }) export class AppComponent {}最后,我們更新src/app/app-routing.module.ts todos路由,以實例化TodosComponent而不是AppComponent :
const routes: Routes = [{path: '',redirectTo: 'todos',pathMatch: 'full'},{path: 'todos',component: TodosComponent} ];現在,當我們的應用程序啟動時,Angular實例化AppComponent并找到一個<router-outlet></router-outlet> ,Angular Router可以在其中實例化和激活組件。
讓我們嘗試在瀏覽器中進行更改。
通過運行以下命令啟動開發服務器和后端API:
$ ng serve $ npm run json-server然后將瀏覽器導航到http://localhost:4200 。
Angular Router會讀取路由器配置,并自動將瀏覽器重定向到http://localhost:4200/todos 。
如果檢查頁面上的元素,則會看到TodosComponent不在<router-outlet></router-outlet>內部呈現,而是在其旁邊呈現:
<app-root><!-- Angular Router finds router outlet --><router-outlet></router-outlet><!-- and places the component right next to it, NOT inside it --><app-todos></app-todos> </app-root>我們的應用程序現已啟用路由。 太棒了!
添加通配符路由
當您將瀏覽器導航到http://localhost:4200/unmatched-url并打開瀏覽器的開發人員工具時,您會注意到Angular Router將以下錯誤記錄到控制臺:
Error: Cannot match any routes. URL Segment: 'unmatched-url'為了優雅地處理不匹配的URL,我們需要做兩件事:
讓我們從使用Angular CLI生成PageNotFoundComponent開始:
$ ng generate component PageNotFound然后在src/app/page-not-found/page-not-found.component.html編輯其模板:
<p>We are sorry, the requested page could not be found.</p>接下來,我們使用**作為路徑添加通配符路由:
const routes: Routes = [{path: '',redirectTo: 'todos',pathMatch: 'full'},{path: 'todos',component: AppComponent},{path: '**',component: PageNotFoundComponent} ];**匹配任何URL,包括子路徑。
現在,如果將瀏覽器導航到http://localhost:4200/unmatched-url PageNotFoundComponent http://localhost:4200/unmatched-url , PageNotFoundComponent顯示PageNotFoundComponent 。
請注意,通配符路由必須是我們路由配置中的最后一條路由,才能按預期工作。
當Angular Router將請求URL與路由器配置匹配時,一旦找到第一個匹配項,它將立即停止處理。
因此,如果我們要將路線的順序更改為此:
const routes: Routes = [{path: '',redirectTo: 'todos',pathMatch: 'full'},{path: '**',component: PageNotFoundComponent},{path: 'todos',component: AppComponent} ];然后todos將永遠不會達到和PageNotFoundComponent將顯示,因為通配符路線將首先匹配。
我們已經做了很多工作,所以讓我們快速回顧一下到目前為止已經完成的工作:
- 我們設置了角路由器
- 我們為應用程序創建了路由配置
- 我們重構AppComponent到TodosComponent
- 我們在AppComponent的模板中添加了<router-outlet></router-outlet>
- 我們添加了通配符路由,以正常處理不匹配的網址。
接下來,我們將創建解析器以使用Angular Router從我們的后端API中獲取現有的待辦事項。
使用Angular路由器解析數據
在本系列的第3部分中,我們已經學習了如何使用Angular HTTP服務從后端API提取數據。
當前,當我們將瀏覽器導航到todos URL時,會發生以下情況:
如果在步驟5中加載待辦事項需要三秒鐘,則在步驟6中顯示實際待辦事項之前,將向用戶顯示一個空的待辦事項列表三秒鐘。
如果TodosComponent的模板中包含以下HTML:
<div *ngIf="!todos.length">You currently do not have any todos yet. </div>那么用戶會在顯示實際待辦事項之前看到此消息三秒鐘,這可能會完全誤導用戶并導致用戶在輸入實際數據之前先離開。
我們可以在TodosComponent中添加一個加載器,以在加載數據時顯示一個微調TodosComponent ,但是有時我們可能無法控制實際的組件,例如,當我們使用第三方組件時。
要解決此不良行為,我們需要進行以下操作:
在這里,直到來自我們的API后端的數據可用時, TodosComponent顯示TodosComponent 。
這正是解析器可以為我們做的。
為了讓Angular Router在激活TodosComponent之前解析TodosComponent ,我們必須做兩件事:
通過將解析器連接到todos路由,我們要求Angular Router在激活TodosComponent之前先解析數據。
因此,讓我們創建一個解析器以獲取我們的待辦事項。
創建TodosResolver
Angular CLI沒有用于生成解析器的命令,因此讓我們手動創建一個新文件src/todos.resolver.ts并添加以下代碼:
import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { Todo } from './todo'; import { TodoDataService } from './todo-data.service';@Injectable() export class TodosResolver implements Resolve<Observable<Todo[]>> {constructor(private todoDataService: TodoDataService) {}public resolve(route: ActivatedRouteSnapshot,state: RouterStateSnapshot): Observable<Todo[]> {return this.todoDataService.getAllTodos();} }我們將解析器定義為實現Resolve接口的類。
Resolve接口是可選的,但是讓我們的TypeScript IDE或編譯器通過要求我們實現resolve()方法來確保我們正確地實現了該類。
當Angular Router需要使用解析器解析數據時,它將調用解析器的resolve()方法并期望resolve()方法返回一個值,一個promise或一個observable。
如果resolve()方法返回一個Promise或可觀察到的Angular Router將在激活路由的組件之前等待該Promise或可觀察到的完成。
當調用resolve()方法時,Angular Router可以方便地傳遞已激活的路由快照和路由器狀態快照,以使我們可以訪問數據(例如,路由參數或查詢參數),我們可能需要解析數據。
TodosResolver的代碼非常簡潔,因為我們已經有一個TodoDataService來處理與API后端的所有通信。
我們注入TodoDataService在構造和使用其getAllTodos()方法中獲取所有待辦事項resolve()方法。
resolve方法返回類型為Todo[]的可觀察對象,因此Angular Router將在激活路由組件之前等待可觀察對象完成。
現在我們有了解析器,讓我們配置Angular Router來使用它。
通過路由器解析待辦事項
為了使Angular Router使用解析器,我們必須將其附加到路由配置中的路由上。
讓我們打開src/app-routing.module.ts并將我們的TodosResolver添加到todos路由中:
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { TodosComponent } from './todos/todos.component'; import { TodosResolver } from './todos.resolver';const routes: Routes = [{path: '',redirectTo: 'todos',pathMatch: 'full'},{path: 'todos',component: TodosComponent,resolve: {todos: TodosResolver}},{path: '**',component: PageNotFoundComponent} ];@NgModule({imports: [RouterModule.forRoot(routes)],exports: [RouterModule],providers: [TodosResolver] }) export class AppRoutingModule { }我們導入TodosResolver :
import { TodosResolver } from './todos.resolver';todos它添加為todos路線的解析器:
{path: 'todos',component: TodosComponent,resolve: {todos: TodosResolver} }這告訴Angular Router使用TodosResolver解析數據,并將解析器的返回值指定為路由數據中的todos 。
可以從ActivatedRoute或ActivatedRouteSnapshot訪問路線的數據,我們將在下一部分中看到它們。
您可以使用路線的data屬性將靜態數據直接添加到路線的數據中:
{path: 'todos',component: TodosComponent,data: {title: 'Example of static route data'} }您還可以使用在路由的resolve屬性中指定的解析器添加動態數據:
resolve: {path: 'todos',component: TodosComponent,resolve: {todos: TodosResolver} }您也可以同時做兩個:
resolve: {path: 'todos',component: TodosComponent,data: {title: 'Example of static route data'}resolve: {todos: TodosResolver} }一旦解析來自resolve屬性的解析器,它們的值就會與來自data屬性的靜態數據合并,并且所有數據都將用作路徑的數據。
Angular Router使用Angular依賴注入來訪問解析器,因此我們必須確保將TodosResolver注冊到Angular的依賴注入系統中,方法是將其添加到AppRoutingModule的@NgModule元數據中的providers屬性中:
@NgModule({imports: [RouterModule.forRoot(routes)],exports: [RouterModule],providers: [TodosResolver] }) export class AppRoutingModule { }當您將瀏覽器導航到http://localhost:4200 ,Angular Router現在:
如果打開開發人員工具的“網絡”標簽,您會看到待辦事項現在已從API提取了兩次。 一旦被角路由器,一旦由ngOnInit在處理TodosComponent 。
因此,Angular Router已經從API提取了TodosComponent ,但是TodosComponent仍然使用其自己的內部邏輯來加載待辦事項。
在下一節中,我們將更新TodosComponent以使用Angular Router解析的數據。
使用已解析的數據
讓我們打開app/src/todos/todos.component.ts 。
ngOnInit()處理程序當前直接從API獲取ngOnInit() :
public ngOnInit() {this.todoDataService.getAllTodos().subscribe((todos) => {this.todos = todos;}); }現在,Angular Router使用TodosResolver來獲取TodosResolver ,我們想從路由數據而非API中獲取TodosComponent中的TodosComponent 。
要訪問路線數據,我們必須從@angular/router導入ActivatedRoute :
import { ActivatedRoute } from '@angular/router';并使用Angular依賴注入來獲取已激活路由的句柄:
constructor(private todoDataService: TodoDataService,private route: ActivatedRoute ) { }最后,我們更新ngOnInit()處理函數,以從路線數據而非API獲取ngOnInit() :
public ngOnInit() {this.route.data.map((data) => data['todos']).subscribe((todos) => {this.todos = todos;}); }ActivatedRoute將路由數據公開為可觀察的,因此我們的代碼幾乎不變。
我們用this.route.data.map((data) => data['todos'])替換this.todoDataService.getAllTodos() ,其余所有代碼保持不變。
如果將瀏覽器導航到localhost:4200并打開“網絡”標簽,您將不再看到兩個HTTP請求從API提取待辦事項。
任務完成! 我們已成功將Angular Router集成到我們的應用程序中!
在總結之前,讓我們運行單元測試:
ng serve一個單元測試失敗:
Executed 11 of 11 (1 FAILED) TodosComponent should create FAILED'app-todo-list-header' is not a known element在測試TodosComponent ,測試平臺不知道TodoListHeaderComponent ,因此Angular抱怨它不知道app-todo-list-header元素。
要解決此錯誤,我們打開app/src/todos/todos.component.spec.ts并將NO_ERRORS_SCHEMA添加到TestBed選項中:
beforeEach(async(() => {TestBed.configureTestingModule({declarations: [TodosComponent],schemas: [NO_ERRORS_SCHEMA]}).compileComponents(); }));現在,業力顯示另一個錯誤:
Executed 11 of 11 (1 FAILED) TodosComponent should create FAILEDNo provider for ApiService!讓我們將必要的提供程序添加到測試平臺選項中:
beforeEach(async(() => {TestBed.configureTestingModule({declarations: [TodosComponent],schemas: [NO_ERRORS_SCHEMA],providers: [TodoDataService,{provide: ApiService,useClass: ApiMockService}],}).compileComponents(); }));這再次引發另一個錯誤:
Executed 11 of 11 (1 FAILED) TodosComponent should create FAILEDNo provider for ActivatedRoute!!讓我們在測試床選項中添加一個更多的ActivatedRoute提供程序:
beforeEach(async(() => {TestBed.configureTestingModule({declarations: [TodosComponent],schemas: [NO_ERRORS_SCHEMA],providers: [TodoDataService,{provide: ApiService,useClass: ApiMockService},{provide: ActivatedRoute,useValue: {data: Observable.of({todos: []})}}],}).compileComponents(); }));我們為ActivatedRoute的提供者分配了一個模擬對象,該對象包含一個可觀察的data屬性,以顯示todos的測試值。
現在,單元測試成功通過:
Executed 11 of 11 SUCCESS極好! 要將我們的應用程序部署到生產環境中,我們現在可以運行:
ng build --aot --environment prod我們將生成的dist目錄上載到我們的托管服務器。 那有多甜?
我們已經在本文中介紹了很多內容,所以讓我們回顧一下我們學到的東西。
摘要
在第一篇文章中 ,我們學習了如何:
- 使用Angular CLI初始化我們的Todo應用程序
- 創建一個Todo類來代表單個Todo
- 創建TodoDataService服務以創建,更新和刪除待辦事項
- 使用AppComponent組件顯示用戶界面
- 將我們的應用程序部署到GitHub頁面
在第二篇文章中 ,我們將AppComponent重構為將其大部分工作委托給:
- TodoListComponent以顯示TodoListComponent列表
- TodoListItemComponent以顯示單個待辦事項
- 一個TodoListHeaderComponent來創建一個新的待辦事項
- TodoListFooterComponent來顯示還剩下多少個TodoListFooterComponent 。
在第三篇文章中 ,我們學習了如何:
- 創建一個模擬REST API后端
- 將API URL存儲為環境變量
- 創建一個ApiService與REST API通信
- 更新TodoDataService以使用新的ApiService
- 更新AppComponent以處理異步API調用
- 創建一個ApiMockService以避免在運行單元測試時進行真正的HTTP調用。
在第四篇文章中,我們了解到:
- 為什么應用程序可能需要路由
- 什么是JavaScript路由器
- 什么是Angular Router,它如何工作以及它能為您做什么
- 如何為我們的應用程序設置Angular Router和配置路由
- 如何告訴Angular Router在DOM中放置組件的位置
- 如何正常處理未知URL
- 如何使用解析器讓Angular Router解析數據。
這篇文章中的所有代碼都可以在GitHub上找到 。
在第五部分中,我們將實現身份驗證,以防止未經授權訪問我們的應用程序。
因此,請繼續關注更多信息,并且與往常一樣,隨時在評論中留下您的想法和問題!
推薦課程
Angular和TypeScript在線課程 托德·格托 專家指導的針對個人和團隊的在線AngularJS,Angular和TypeScript培訓課程。 結帳時使用優惠券代碼'SITEPOINT'可獲得25%的折扣 。From: https://www.sitepoint.com/component-routing-angular-router/
總結
以上是生活随笔為你收集整理的Angular Router的组件路由介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: laravel5.4使用Laravel
- 下一篇: 中国电子科技集团公司家族谱及信息