在單頁面應用中,需要在定義好的不同檢視中(元件)來回切換,而不用去伺服器中獲取新頁面,要實現從一個檢視到另外一個檢視的導航,就需要使用到Angular中的Router。【相關教學推薦:《》】
需要路由跳轉的場景:
假如頁面上有兩個跳轉連結,我們希望點選這些連結的時候可以分別跳轉到
商品列表頁面
和個人中心頁面
。我們可以先建立兩個元件分別是 GoodsListComponent 和PersonalCenterComponent。具體流程如下所示:
路由建立
1、新建檔案app-routing.module.ts,將要跳轉的檢視設定放到裡邊
import { NgModule } from '@angular/core'; // 引入路由模組 RouterModule和 Routes import { Routes, RouterModule } from '@angular/router'; // 引入在不同URL下,需要展示的元件 import { GoodsListComponent } from './goods-list/goods-list.component'; import { PersonalCenterComponent } from './personal-center/personal-center.component'; // 設定的路由陣列 const routes: Routes = [ { path: 'goodsList', // 定義路由名稱 component: GoodsListComponent, // 指定顯示的那個元件 }, { path: 'personalCenter', // 定義路由名稱 component: PersonalCenterComponent, // 指定顯示的那個元件 } ]; @NgModule({ // forRoot() 方法會建立一個 NgModule,其中包含所有指令、給定的路由以及 Router 服務本身。 imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}
在
Routes 陣列
中定義你的路由(這個陣列中的每個路由都是一個包含兩個屬性的 JavaScript 物件。第一個屬性 path 定義了該路由的 URL 路徑。第二個屬性 component 定義了相對應的路徑下需要顯示的元件)
2、在app.module.ts中匯入AppRoutingModule
import { AppRoutingModule } from './app-routing.module';
3、在app.component.html中把這些路由新增進來,以便控制導航的展示
<div class="route-change-container"> <a routerLink="/goodsList">切換到商品列表元件</a> <!--routerLink將你定義的路由連線到模板檔案中--> <a routerLink="/personalCenter">切換到個人中心元件</a> </div> <router-outlet></router-outlet> <!--此指令通過路由來動態載入元件-->
- 件。
- 接下來,在模板中新增
<router-outlet>
標籤。該元素會通知 Angular,你可以用所選路由的元件更新應用的檢視。用法和元件一樣,充當預留位置,用來標記路由器應該把元件顯示到那個位置上。
路由中兩個重要API的介紹
用於,它所包含路由的資訊比如: 路由引數,靜態資料...具體提供的方法可參照ActivatedRoute官網
import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; // ① 先引入ActivatedRoute @Component({ selector: 'app-goods-list', templateUrl: './goods-list.component.html', styleUrls: ['./goods-list.component.scss'] }) export class GoodsListComponent implements OnInit { constructor( private route: ActivatedRoute, // ② 依賴注入這個服務 ) {} // ③ 在初始化的生命週期中去或者url的路由資訊 public ngOnInit(): void { // 第一種獲取引數的方式 const params = this.route.snapshot.params; // 第二種獲取引數的方式 this.route.params.subscribe(params => { console.log(params); }); } }
上述兩種獲取引數方法的區別和使用:
route.snapshot:
- 需要直接存取引數,主要獲取
初始值
,不用訂閱的情況下用snapshot;route.params.subscribe():
- 引數每次變化都要獲取到或者需要連續導航多次的時候的用params;
是一個的模組,具體提供的方法可參照Router官網,開發中常用到的方法如下,具體講解在路由傳參中會詳細介紹:
// 使用前,需要在元件中先引入Router這個服務類 import { Router } from '@angular/router';
navigate() 該方法支援的引數型別和routerLink指令一樣,所以他們的作用也是一樣的;
navigateByUrl() 絕對路由;
每次導航前都會呼叫events方法;(暫時僅瞭解)
路由傳參以及獲取
1. params (是/:id 動態路由)
使用場景: 比如當我們點選商品列表連結時,希望把使用者資訊,或者商品種類資訊通過url,傳到商品列表的元件中去。
// 需要設定路由 const routes: Routes = [ { path: 'goodsList/:id', // 定義路由名稱 component: GoodsListComponent, // 指定顯示的那個元件 }, ];
2. queryParams(是?id=xx 形式)
使用場景: 當我們希望通過url傳遞多個引數的時候,可以選擇用這種方式進行路由傳參
1個引數可以優先用動態路由,2個以上還是用query更加方便點
1. routerLink
單一引數:
<a [routerLink]="['/goodsList', id]" routerLinkActive="active-class">切換到商品列表元件</a> // 其中/goodsListl是我設定的路由path,id是需要傳遞的引數 // 多個引數的時候,也可以用這種形式,只不過看起來不夠直觀,所以不推薦多個引數的時候也用這種方法
多個引數:
<a [routerLink]="['/personalCenter']" [queryParams]="{name: 'zs', age: 16}">切換到個人中心元件</a> // 引數的格式可以自行組織成各種object
routerLinkActive
跟蹤元素上鍊路路由當前是否處於活動狀態
。並允許你指定一個或者多個css類,以便在連結路由處於活動狀態時新增該元素。
2. router.navigate
基於所提供的命令陣列和起點路由進行導航。 如果沒有指定起點路由,則從根路由開始進行絕對導航
單一引數:
public goToGoodsListPage(): void { // 第一種方式 this._router.navigate([`/goodsList/${this.id}`]); // 第二種方式 與第一種方式達到相同的效果 this._router.navigate(['/goodsList', this.id]); } // html中呼叫這個方法,等同於使用a標籤的routerLink屬性 <button (click)="goToGoodsListPage()">切換到商品列表元件</button>
多個引數:
public goToPersonalCenterPage(): void { this._router.navigate(['/personalCenter'], {queryParams:{name: 'zs', age: 16}}); } // html中呼叫 <button (click)="goToPersonalCenterPage()">切換到個人中心元件</button>
3. router.navigateByUrl
基於所提供的 URL 進行導航,必須使用
絕對路徑
。對於單一引數和多個引數的使用方法與router.navigate一致。
// 傳的第一個引數是陣列的形式,而navigate的第一個引數是陣列的方式 // 他的傳參目前是拼接在url上邊的 public goToPersonalCenterPage(): void { this._router.navigateByUrl(`/personalCenter?name=zs&age=16`); }
1. 接收params型別的引數
import { ActivatedRoute } from '@angular/router'; constructor( private _route: ActivatedRoute, ) {} public ngOnInit(): void { // 第一種只獲取初始值方式 const param = this.route.snapshot.params; // 第二種動態獲取方式 this.route.params.subscribe(params => { console.log(params); }); }
2. 接收queryParams型別的引數
import { ActivatedRoute } from '@angular/router'; constructor( private _route: ActivatedRoute, ) {} public ngOnInit(): void { // 第一種只獲取初始值方式 const queryParam = this.route.snapshot.queryParams; // 第二種動態獲取方式 this.route.queryParams.subscribe(params => { console.log(params); }); }
路由重定向
應用場景:當在一個應用中,希望使用者預設跳轉到某個頁面時,就需要使用得到路由重定向。
重定向的組成:
{ path: '', redirectTo: '/personalCenter', pathMatch: 'full' }, // 重定向到 `personalCenter`
是一個用來的字串。可選項有 prefix(預設值)和 full。
prefix: 以指定的路徑開頭 (就是path中的值與使用者輸入的的開頭路徑是一樣的,比如path是」abc「,那麼使用者輸入/abc, /abc/a都是可以的,但是輸入 /abcd這樣就不可以); 如果path是空,那麼所有路由都會匹配到這個頁面上
full: 與指定路徑完全一樣(這個就要求與path要求的路徑完全一樣,比如說path是‘abc’,那麼使用者輸入/abc , /abc/是可以的 /abcd,/abc/d是不可以的); 如果path是空,那麼只有localhost:4200後邊什麼都不加的情況下才匹配到這個頁面上。
路由順序
Router在匹配路由的時候,使用的是」「的策略,所以應當將放前邊,然後可以放置的空路徑路由,路由是最後一個(他可以匹配所以路由,當其他路由都沒有匹配對的時候,Router才會選擇他)
const routes: Routes = [ // 靜態路由 { path: 'goodsList/:id', component: GoodsListComponent, }, { path: 'personalCenter', component: PersonalCenterComponent, }, // 預設空路徑路由 { path: '', redirectTo: '/personalCenter', pathMatch: 'full' }, // 萬用字元路由 { path: '**', component: PageNotFoundComponent }, ];
路由巢狀
當應用變的複雜的時候,可能需要建立一些根元件之外的相對路由,這些路由成為子路由,這意味著在專案中需要新增第二個 ,因為它是 AppComponent 之外的另一個 。
場景: 我們需要在個人中心的頁面中,跳轉到他的兩個子頁面中,分別是和,具體程式碼實現如下所示:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { GoodsListComponent } from './goods-list/goods-list.component'; import { PersonalCenterComponent } from './personal-center/personal-center.component'; import { PersonalSettingComponent } from './personal-setting/personal-setting.component'; import { PersonalDetailComponent } from './personal-detail/personal-detail.component'; const routes: Routes = [ { path: 'goodsList/:id', component: GoodsListComponent, }, { path: 'personalCenter', component: PersonalCenterComponent, children: [ // 主要是這裡新增了一個children陣列,用來存放子路由 { path: 'personalDetail', component: PersonalDetailComponent, }, { path: 'personalSetting', component: PersonalSettingComponent, }, ] }, { path: '', redirectTo: '/personalCenter', pathMatch: 'full' }, ]; // 個人中心的html中,需要新增<router-outlet>從而來指定子頁面展示的位置 <div class="personal-center-container"> 這是個人中心頁面 <a routerLink="./personalDetail">切換個人詳情頁面</a> <a routerLink="./personalSetting">切換到個人設定頁面</a> </div> <router-outlet></router-outlet>
子路由和其它路由一樣,同時需要 path 和 component。唯一的區別是你要把子路由放在父路由的
children
陣列中。
路由懶載入
你可以設定路由定義來實現惰性載入模組,這意味著 Angular只會在才載入這些模組,而不是在應用就載入全部。
1、先給之前的兩個頁面元件增加一個module檔案,然後routes中使用loadChildren代替component進行設定
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ { path: 'goodsList/:id', loadChildren: () => import('./goods-list/goods-list.module') .then(m => m.GoodListModule) }, { path: 'personalCenter', loadChildren: () => import('./personal-center/personal-center.module') .then(m => m.PersonalCenterModule) }, { path: '', redirectTo: '/personalCenter', pathMatch: 'full' }, ]; @NgModule({ // forRoot() 方法會建立一個 NgModule,其中包含所有指令、給定的路由以及 Router 服務本身。 imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}
2、在之前的兩個頁面元件中新增路由模組檔案,新增一個指向該元件的路由。
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { GoodsListComponent } from './goods-list.component'; const routes: Routes = [ { path: '', component: GoodsListComponent }, ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) export class GoodsListRoutingModule { }
路由守衛(僅瞭解)
使用路由守衛來防止使用者未經授權就導航到應用的某些部分,想了解更多請移步 路由守衛官網介紹
export class TestGuard implements CanActivate { canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { //判斷是否可以進入某個路由的邏輯 } }
{ path: 'test-path', component: TestComponent, canActivate: [TestGuard], // 使用路由守衛guard }
路由事件(僅瞭解)
Router 在每次導航過程中都會通過 Router.events 屬性發出導航事件。這些事件的範圍貫穿從導航開始和結束之間的多個時間點。想了解更多請移步
路由事件官網詳細介紹
https://angular.cn/guide/router-reference#router-events
響應式表單與模板驅動表單的區別
響應式 | 模板驅動 | |
---|---|---|
建立表單模型 | 顯示的,在元件類中建立 | 隱式的,有指令建立 |
資料模型 | 結構化和不可變的(可觀察物件) | 非結構化和可變的(資料雙向繫結) |
資料流 | 同步 | 非同步 |
表單驗證 | 函數 | 指令 |
常用表單公共基礎類
響應式表單和模板驅動表單都建立在下列基礎類之上。
FormControl 範例用於追蹤單個表單控制元件的值和驗證狀態。
FormGroup 用於追蹤一個表單控制元件組的值和狀態。
FormArray 用於追蹤表單控制元件陣列的值和狀態。(瞭解)
模板驅動表單
模板中的來建立和操作底層的物件模型。主要用於新增簡單的表單,使用起來較簡單,但是擴充套件性方面不如響應式表單。當控制元件值更新的時候相關的屬性值會被修改為新值。
<!-- #form是模板參照變數,他就代表是form整個元素,我們可以在當前模板的任何地方使用模板參照變數。--> <!-- 這些變數提供了從模組中直接存取元素的能力 --> <!-- name名稱一定要加上 因為表單中的所有元件和控制元件都應該有名稱--> <form (ngSubmit)="onSubmit(myform.value);" #myform="ngForm" novalidate> <label for="name">姓名: </label> <input type="text" name="name" [(ngModel)]="name"> <label for="password">密碼: </label> <input type="password" name="password" [(ngModel)]="password"> <button>提交</button> </form>
<!--假如我們希望給上述的表單做個校驗,密碼不能為空,名字的長度不能少於2個字元--> <form (ngSubmit)="onSubmit(myform);" #myform="ngForm"> <label for="name">姓名: </label> <input type="text" name="name" [(ngModel)]="name" minlength="2"> <label for="password">密碼: </label> <input type="password" name="password" [(ngModel)]="password" required> <button>提交</button> <!-- 主要通過hasError方法來實現--> <div *ngIf="myform.form.hasError('minlength', 'name')">姓名的長度不能少於2位</div> <div *ngIf="myform.form.hasError('required', 'password')">密碼不能為空</div> </form>
響應式表單(重點)
提供對直接、顯式的存取。它們與模板驅動表單相比,更加健壯,它們的可延伸性、可複用性和可測試性都更高。而且控制元件更新的,因為FormControl 的範例總會返回一個新值,而不會更新現有的資料模型,如果表單是你的應用程式的關鍵部分,或者可延伸性很強,那就使用響應式表單
1、在模組檔案中引入響應式表單模組
import { ReactiveFormsModule } from '@angular/forms';
2、在元件中引入表單組
import { FormGroup, FormControl } from '@angular/forms';
3、建立一個FormGroup範例,並把這個FormGroup模型關聯到檢視
this.profileForm = new FormGroup({ name: new FormControl(''), password: new FormControl(''), });
<form [formGroup]="profileForm" (ngSubmit)="submitForm(profileForm.value)"> <label for="name">First Name: </label> <input id="name" type="text" formControlName="name"> <label for="password">Last Name: </label> <input id="password" type="text" formControlName="password"> <button>提交</button> </form>
表單組可以同時接受和其它表單組範例作為其。這可以讓複雜的表單模型更容易維護,並在邏輯上把它們分組到一起。
1、建立一個巢狀的表單組。
this.profileForm = new FormGroup({ name: new FormControl('', [Validators.required]), password: new FormControl(''), // 在表單中巢狀一個address的表單組 address: new FormGroup({ street: new FormControl(''), city: new FormControl(''), }) });
2、在模板中對這個巢狀表單分組。
<form [formGroup]="profileForm" (ngSubmit)="submitForm(profileForm.value)"> <label for="name">First Name: </label> <input id="name" type="text" formControlName="name"> <label for="password">Last Name: </label> <input id="password" type="text" formControlName="password"> // 使用formGroupName來宣告新的表單組 <div formGroupName="address"> <h3>Address</h3> <label for="street">Street: </label> <input id="street" type="text" formControlName="street"> <label for="city">City: </label> <input id="city" type="text" formControlName="city"> </div> <button>提交</button> </form>
在學習表單驗證之前,我們先來了解一下angular針對表單所提供的一些,這些欄位可幫助使用者決定在那些階段去做表單驗證。
用來判斷使用者
是否獲取過焦點
,如果獲取過焦點則touched=true; untouched=false,如果從來沒有獲取過焦點,touched=false; untouched=true。
用來判斷使用者是
否操作過欄位
,dirty表示使用者操作過表單元素,pristine表示未操作過表單元素。
判斷表單元素
是否有效
,當表單元素上有多個驗證條件事,只有所有的驗證條件都滿足驗證要求valid才是true,只要有一個驗證條件不滿足,那麼invalid這個屬性值就是true。
Angular 會自動把很多作為 對映到控制元件所在的上。你可以使用這些類來 給表單新增樣式。
場景: 比如我們希望當控制元件在不滿足驗證條件的情況下,控制元件邊框顯示成紅色~ 實現方式如下:
.ng-touched:not(form){ &.ng-invalid:not(form){ // 這是scss的一種寫法 border: 1px solid red } }
1、在表單元件中匯入一個驗證器函數,Angular具體提供了那些內建驗證器請參照表單驗證器官網
import { Validators } from '@angular/forms'; // angular提供的內建驗證器主要有: min max required email minLength maxLength pattern...
2、把這個驗證器新增到表單中的相應欄位。
場景:需要做驗證的內容分別是,① 名稱不能為空; ② 密碼不能為空,且最小長度不能少於4; ③住址中的街道不能為空
this.profileForm = new FormGroup({ name: new FormControl('', [Validators.required]), password: new FormControl('', [Validators.required, Validators.minLength(4)]), address: new FormGroup({ street: new FormControl('', [Validators.required]), city: new FormControl(''), }) });
3、在模板中錯誤資訊的提醒邏輯
<form [formGroup]="profileForm" (ngSubmit)="submitForm(profileForm.value)"> <!-- 姓名--> <label for="name">姓名: </label> <input id="name" type="text" formControlName="name"> <div [hidden]="profileForm?.get('name').valid || profileForm?.get('name')?.untouched"> <p [hidden]="!profileForm.hasError('required', 'name')">名字是必填項</p> </div> <!-- 密碼--> <label for="password">密碼: </label> <input id="password" type="text" formControlName="password"> <!-- 先驗證密碼當密碼是合法的或者使用者從未操作多的時候,不顯示錯誤資訊 --> <!-- 然後在根據hasError來判斷具體的錯誤提醒--> <div [hidden]="profileForm?.get('password').valid || profileForm?.get('password')?.untouched"> <p [hidden]="!profileForm.hasError('minlength', 'password')">密碼的位數不能少於四位</p> <p [hidden]="!profileForm.hasError('required', 'password')">密碼不能為空</p> </div> <!-- 地址表單組--> <div formGroupName="address"> <h3>Address</h3> <label for="street">Street: </label> <input id="street" type="text" formControlName="street"> <div [hidden]="profileForm?.get('address')?.get('street')?.valid || profileForm?.get('address')?.get('street')?.untouched"> <div [hidden]="!profileForm.hasError('required', ['address', 'street'])">街道不能為空</div> </div> <label for="city">City: </label> <input id="city" type="text" formControlName="city"> </div> <button>提交</button> </form>
場景: 使用者希望在填寫手機號的時候,可以驗證手機號的格式是否輸入正確,便可以通過自定義函數去做這件事
public ngOnInit(): void { this.profileForm = new FormGroup({ phone: new FormControl('', [this.mobileValidator]) }); } public mobileValidator = (control: FormControl): any => { const value = control.value; // 手規號碼正則 const mobileReg = /^0?(13[0-9]|15[012356789]|17[013678]|18[0-9]|14[57])[0-9]{8}$/; const result = mobileReg.test(value); // 有效值返回null, 無效時返回驗證的錯誤物件,驗證物件是名為mobileFormat的驗證祕鑰屬性。值一般可以是任意值 // mobileFormat 用於在模板中獲取錯誤資訊的時候會被再次使用 return result ? null : {mobileFormat: {info: '手機格式不正確'}}; } // 在模板中的使用 <form [formGroup]="profileForm" (ngSubmit)="submitForm(profileForm.value)"> <input id="phone" type="text" formControlName="phone"> <div>{{profileForm?.get('phone').errors | json}}</div> // 可以將在自定義函數中定義的報錯資訊列印出來 <div [hidden]="profileForm?.get('phone').valid || profileForm?.get('phone')?.untouched"> <p [hidden]="!profileForm.hasError('mobileFormat', 'phone')">手機格式不正確</p> </div> <button>提交</button> </form>
- 使用
setValue()
方法來為單個控制元件設定新值。 setValue() 方法會嚴格遵循表單組的結構,並整體性
替換控制元件的值。- 使用
patchValue()
方法可以用物件中所定義的任何屬性為表單模型進行替換。
FormBuilder 服務提供了一些來生成表單控制元件。FormBuilder 在幕後也使用同樣的方式來建立和返回這些範例,只是用起來更簡單。
服務有三個方法:control()、group() 和 array()。這些方法都是工廠方法,用於在元件類中分別生成 FormControl、FormGroup 和 FormArray。
1.在元件中引入FormBuilder類,並注入服務
import { FormBuilder } from '@angular/forms'; constructor(private fb: FormBuilder) { }
2.生成表單內容
this.profileForm = this.fb.group({ name: [''], password: [''] }); // 等同於 this.profileForm = new FormGroup({ name: new FormControl(''), password: new FormControl(''), });
const heroForm = new FormGroup({ 'name': new FormControl(), 'alterEgo': new FormControl(), 'power': new FormControl() }, { validators: identityRevealedValidator }); export const identityRevealedValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { const name = control.get('name'); const alterEgo = control.get('alterEgo'); return name && alterEgo && name.value === alterEgo.value ? { identityRevealed: true } : null; };
更多程式設計相關知識,請存取:!!
以上就是Angular進階學習之深入瞭解路由和表單的詳細內容,更多請關注TW511.COM其它相關文章!