成人怡红院-成人怡红院视频在线观看-成人影视大全-成人影院203nnxyz-美女毛片在线看-美女免费黄

站長資訊網
最全最豐富的資訊網站

深入了解Angular(新手入門指南)

本篇文章帶大家深入了解Angular,分享最全的Angular新手入門指南,希望對大家有所幫助!

深入了解Angular(新手入門指南)

前端(vue)入門到精通課程,老師在線輔導:聯系老師
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API調試工具:點擊使用

Angular概述

Angular 是谷歌開發的一款開源的 web 前端框架,基于 TypeScript ?!鞠嚓P教程推薦:《angular教程》】

和 react 與 vue 相比, Angular 更適合中大型企業級項目。

Angular程序架構

深入了解Angular(新手入門指南)

Angular優勢

  • 可伸縮性:基于RxJS 、immutable.js和其他推送模型,能適應海量數據需求
  • 跨平臺:漸進式應用(高性能、離線使用、免安裝),原生(Ionic),桌面端
  • 生產率:模版(通過簡單而強大的模版語法,快速創建UI視圖),CLI(快速進入構建環節、添加組件和測試,然后立即部署)
  • 測試:單元測試(支持Karma、Jasmine等工具進行單元測試),端到端測試(支持Protractor等工具進行端到端測試)

@angular/cli腳手架

ng new 新建項目

  • ——routing 配置路由
  • ——style=css|scss|less 配置css樣式

ng serve 啟動項目

  • ——port 4200 端口號,默認4200
  • ——open 自動打開瀏覽器

ng build 打包項目

  • ——aot 預編譯
  • ——prod 壓縮打包
  • ——base-href=/static/

ng generate 創建模塊/組件/服務

  • module ——routing 創建模塊
  • component 創建組件
  • service / 創建服務

文件加載順序

main.ts => app.module.ts => app.component.ts => index.html => app.component.html

項目目錄結構

|-- project 	|-- .editorconfig // 用于在不同編輯器中統一代碼風格 	|-- .gitignore // git中的忽略文件列表 	|-- README.md // markdown格式的說明文件 	|-- angular.json // angular的配置文件 	|-- browserslist // 用于配置瀏覽器兼容性的文件 	|-- karma.conf.js // 自動化測試框架Karma的配置文件 	|-- package-lock.json // 依賴包版本鎖定文件 	|-- package.json // npm的包定義文件 	|-- tsconfig.app.json // 用于app項目的ts配置文件 	|-- tsconfig.json // 整個工作區的ts配置文件 	|-- tsconfig.spec.json // 用于測試的ts配置文件 	|-- tslint.json // ts的代碼靜態掃描配置 	|-- e2e // 自動化集成測試目錄 	|-- src // 源代碼目錄   |-- src // 源代碼目錄 	|-- favicon.ico // 收藏圖標 	|-- index.html // 單頁應用到宿主HTML 	|-- main.ts // 入口 ts 文件 	|-- polyfills.ts // 用于不同瀏覽器的兼容腳本加載 	|-- styles.css // 整個項目的全局css 	|-- test.ts // 測試入口 	|-- app // 工程源碼目錄 	|-- assets // 資源目錄 	|-- environments // 環境配置 		|-- environments.prod.ts // 生產環境 		|-- environments.ts // 開發環境
登錄后復制

Angular模塊

在 app.module.ts 中定義 AppModule,這個根模塊會告訴 Angular 如何組裝應用。

深入了解Angular(新手入門指南)

@NgModule 裝飾器

@NgModule 接受一個元數據對象,告訴 Angular 如何編譯和啟動應用

設計意圖

  • 靜態的元數據(declarations)
  • 運行時的元數據(providers)
  • 組合與分組(imports 和 exports)

元數據

  • declarations 數組:模塊擁有的組件、指令或管道,注意每個組件/指令/管道只能在一個模塊中聲明
  • providers 數組: 模塊中需要使用的服務
  • imports 數組:導入本模塊需要的依賴模塊,注意是模塊
  • exports 數組: 暴露給其他模塊使用的組件、指令或管道等
  • bootstrap 數組:指定應用的主視圖(稱為根組件)通過引導根 AppModule 來啟動應用,即項目剛加載時選擇讀哪個組件
  • entryComponents 數組:一般用于動態組件

內置模塊

常用的有:核心模塊、通用模塊、表單模塊、網絡模塊等

深入了解Angular(新手入門指南)

自定義模塊

當項目比較小的時候可以不用自定義模塊

但是當項目非常龐大的時候,把所有的組件都掛載到根模塊里面就不太合適了

所以可以使用自定義模塊來組織項目,并且通過自定義模塊可以實現路由的懶加載

模塊的tips

導入其他模塊時,需要知道使用該模塊的目的

  • 如果是組件,那么需要在每一個需要的模塊中都進行導入
  • 如果是服務,那么一般來說在根模塊導入一次即可

需要在每個需要的模塊中進行導入的

  • CommonModule : 提供綁定、*ngIf 和 *ngFor 等基礎指令,基本上每個模塊都需要導入它
  • FormsModule / ReactiveFormsModule : 表單模塊需要在每個需要的模塊導入
  • 提供組件、指令或管道的模塊

只在根模塊導入一次的

  • HttpClientModule / BrowerAnimationsModule NoopAnimationsModule
  • 只提供服務的模塊

Angular組件

深入了解Angular(新手入門指南)

  • 組件是 Angular 的核心,是 Angular 應用中最基本的 UI 構造塊,控制屏幕上被稱為視圖的一小片區域
  • 組件必須從屬于某個 NgModule 才能被其他組件或應用使用
  • 組件在 @NgModule 元數據的 declarations 字段中引用

@Component 元數據

  • selector :選擇器,選擇相匹配的HTML里的指令模版
  • templateUrl :將選擇器中匹配的指令同級替換成值的模版
  • template :內嵌模版,直接可以在里面寫HTML模版
  • styleUrls :對應模版的樣式,為一個數組,可以引入多個css樣式控制組件
  • encapsulation:組件樣式封裝策略

@Component({   selector: 'app-xxx',   templateUrl: 'XXX',   styleUrls: ['XXX'],   encapsulation:ViewEncapsulation.Emulated  // 不寫則默認該值,表示該組件樣式只作用于組件本身,不影響全局樣式,在 head 中生成單獨的 style 標簽 })
登錄后復制

數據綁定

  • 數據綁定 {{data}}

  • 屬性綁定 [id]="id",其中[class.樣式類名]=“判斷表達式”是在應用單個class樣式時的常用技巧

  • 事件綁定 (keyup)="keyUpFn($event)"

  • 樣式綁定可以用 :host 這樣一個偽類選擇器,綁定的樣式作用于組件本身

  • 雙向數據綁定 [(ngModel)]

    // 注意引入:FormsModule import { FormsModule } from '@angular/forms';  <input type="text" [(ngModel)]="inputValue"/> {{inputValue}}  // 其實是一個語法糖 [ngModel]="username" (ngModelChange)="username = $event"
    登錄后復制

臟值檢測

臟值檢測:當數據改變時更新視圖(DOM)

如何進行檢測:檢測兩個狀態值(當前狀態和新狀態)

何時觸發臟值檢測:瀏覽器事件(click、mouseover、keyup等)、setTimeout()setInterval()、HTTP請求

Angular 有兩種變更檢測策略:DefaultOnPush

可以通過在@Component元數據中設置changeDetection: ChangeDetectionStrategy.OnPush進行切換

Default

優點:每一次有異步事件發生,Angular 都會觸發變更檢測,從根組件開始遍歷其子組件,對每一個組件都進行變更檢測,對dom進行更新。

缺點:有很多組件狀態沒有發生變化,無需進行變更檢測。如果應用程序中組件越多,性能問題會越來越明顯。

OnPush

優點:組件的變更檢測完全依賴于組件的輸入(@Input),只要輸入值不變就不會觸發變更檢測,也不會對其子組件進行變更檢測,在組件很多的時候會有明顯的性能提升。

缺點:必須保證輸入(@Input)是不可變的(可以用Immutable.js解決),每一次輸入變化都必須是新的引用。

父子組件通訊

深入了解Angular(新手入門指南)

父組件給子組件傳值 @input

父組件不僅可以給子組件傳遞簡單的數據,還可把自己的方法以及整個父組件傳給子組件。

// 父組件調用子組件的時候傳入數據 <app-header [msg]="msg"></app-header>  // 子組件引入 Input 模塊 import { Component, OnInit ,Input } from '@angular/core';  // 子組件中 @Input 裝飾器接收父組件傳過來的數據 export class HeaderComponent implements OnInit {   @Input() msg:string 	constructor() { } 	ngOnInit() { } }  // 子組件中使用父組件的數據 <h2>這是頭部組件--{{msg}}</h2>
登錄后復制

**子組件觸發父組件的方法 @Output **

// 子組件引入 Output 和 EventEmitter import { Component,OnInit,Input,Output,EventEmitter} from '@angular/core';  // 子組件中實例化 EventEmitter // 用 EventEmitter 和 @Output 裝飾器配合使用 <string> 指定類型變量 @Output() private outer=new EventEmitter<string>();  // 子組件通過 EventEmitter 對象 outer 實例廣播數據 sendParent(){   this.outer.emit('msg from child') }  // 父組件調用子組件的時候,定義接收事件,outer 就是子組件的 EventEmitter 對象 outer <app-header (outer)="runParent($event)"></app-header>  // 父組件接收到數據會調用自己的 runParent, 這個時候就能拿到子組件的數據 // 接收子組件傳遞過來的數據   runParent(msg:string){    alert(msg); }
登錄后復制

父組件通過 ViewChild 主動調用子組件DOM和方法

// 給子組件定義一個名稱 <app-footer #footerChild></app-footer>  // 引入 ViewChild import { Component, OnInit ,ViewChild} from '@angular/core';  // ViewChild 和子組件關聯起來 @ViewChild('footerChild') footer;  // 調用子組件 run(){    this.footer.footerRun(); }
登錄后復制

投影組件

深入了解Angular(新手入門指南)

由于組件過度嵌套會導致數據冗余和事件傳遞,因此引入投影組件的概念

投影組件 ng-content 作為一個容器組件使用

主要用于組件動態內容的渲染,而這些內容沒有復雜的業務邏輯,也不需要重用,只是一小部分 HTML 片段

使用 ng-content 指令將父組件模板中的任意片段投影到它的子組件上

組件里面的 ng-content 部分可以被組件外部包裹的元素替代

// 表現形式: <ng-content select="樣式類/HTML標簽/指令"></ng-content>  <ng-content select="[appGridItem]"></ng-content>
登錄后復制

select 表明包含 appGridItem 的指令的元素才能投影穿透過來

Angular指令

深入了解Angular(新手入門指南)

指令可以理解為沒有模版的組件,它需要一個宿主元素(Host)

推薦使用方括號 [] 指定 Selector,使它變成一個屬性

@Directive({ selector: '[appGridItem]' })
登錄后復制

內置屬性型指令

NgClass

ngClass 是自由度和拓展性最強的樣式綁定方式

<div [ngClass]="{'red': true, 'blue': false}">   這是一個 div </div>
登錄后復制

NgStyle

ngStyle由于是嵌入式樣式,因此可能會覆蓋掉其他樣式,需謹慎

<div [ngStyle]="{'background-color':'green'}">你好 ngStyle</div>
登錄后復制

NgModel

// 注意引入:FormsModule import { FormsModule } from '@angular/forms';  <input type="text" [(ngModel)]="inputValue"/> {{inputValue}}
登錄后復制

內置結構型指令

ngIf

ngIf 根據表達式是否成立,決定是否展示 DOM 標簽

<p *ngIf="list.length > 3">這是 ngIF 判斷是否顯示</p>
登錄后復制

ngIf else

<div *ngIf="show else ElseContent">這是 ngIF 內容</div> <ng-template #ElseContent>   <h2>這是 else 內容</h2> </ng-template>  // 結構性指令都依賴于 ng-template,*ngIf 實際上就是 ng-template 指令的 [ngIf] 屬性。
登錄后復制

ngFor

<ul>   <li *ngFor="let item of list;let i = index;">      {{item}} --{{i}}   </li> </ul>
登錄后復制

ngSwitch

<ul [ngSwitch]="score">    <li *ngSwitchCase="1">已支付</li>    <li *ngSwitchCase="2">已確認</li>    <li *ngSwitchCase="3">已發貨</li>    <li *ngSwitchDefault>已失效</li> </ul>
登錄后復制

指令事件樣式綁定

@HostBinding 綁定宿主的屬性或者樣式

@HostBinding('style.display') display = "grid";  // 用樣式綁定代替rd2的 this.setStyle('display','grid');
登錄后復制

@HostListener 綁定宿主的事件

@HostListener('click',['$event.target'])  // 第一個參數是事件名,第二個是事件攜帶參數
登錄后復制

Angular生命周期

生命周期函數通俗的講就是組件創建、組件更新、組件銷毀的時候會觸發的一系列的方法

當 Angular 使用構造函數新建一個組件或指令后,就會按下面規定的順序在特定時刻調用生命周期鉤子

  • constructor :構造函數永遠首先被調用,一般用于變量初始化以及類實例化

  • ngOnChanges :被綁定的輸入屬性變化時被調用,首次調用一定在 ngOnInit 之前。輸入屬性發生變化是觸發,但組件內部改變輸入屬性是不會觸發的。注意:如果組件沒有輸入,或者使用它時沒有提供任何輸入,那么框架就不會調用 ngOnChanges

  • ngOnInit :組件初始化時被調用,在第一輪 ngOnChanges 完成之后調用,只調用一次。使用 ngOnInit 可以在構造函數之后馬上執行復雜的初始化邏輯,同時在 Angular 設置完輸入屬性之后,可以很安全的對該組件進行構建

  • ngDoCheck :臟值檢測時調用,在變更檢測周期中 ngOnChanges 和 ngOnInit 之后

    • ngAfterContentInit :內容投影ng-content完成時調用,只在第一次 ngDoCheck 之后調用

    • ngAfterContentChecked: 每次完成被投影組件內容的變更檢測之后調用(多次)

    • ngAfterViewInit :組件視圖及子視圖初始化完成時調用,只在第一次 ngAfterContentChecked 調用一次

    • ngAfterViewChecked: 檢測組件視圖及子視圖變化之后調用(多次)

  • ngOnDestroy 當組件銷毀時調用,可以反訂閱可觀察對象和分離事件處理器,以防內存泄漏

Angular路由

路由(導航)本質上是切換視圖的一種機制,路由的導航URL并不真實存在

Angular 的路由借鑒了瀏覽器URL變化導致頁面切換的機制

Angular 是單頁程序,路由顯示的路徑不過是一種保存路由狀態的機制,這個路徑在 web 服務器上不存在

路由基本配置

/**  * 在功能模塊中定義子路由后,只要導入該模塊,等同于在根路由中直接定義  * 也就是說在 AppModule 中導入 HomeModule 的時候,  * 由于 HomeModule 中導入了 HomeRouting Module  * 在 HomeRoutingModule 中定義的路由會合并到根路由表  * 相當于直接在根模塊中定義下面的數組。  * const routes = [{  *   path: 'home',  *   component: HomeContainerComponent  * }]  */  const routes: Routes = [   {path: 'home', component: HomeComponent},   {path: 'news', component: NewsComponent},   {path: 'newscontent/:id', component: NewscontentComponent},  // 配置動態路由   {     path: '',     redirectTo: '/home',  // 重定向     pathMatch: 'full' 	},   //匹配不到路由的時候加載的組件 或者跳轉的路由   {      path: '**', /*任意的路由*/      // component:HomeComponent      redirectTo:'home'   } ]  @NgModule({   /**    * 根路由使用 `RouterModule.forRoot(routes)` 形式。    * 而功能模塊中的路由模塊使用 `outerModule.forChild(routes)` 形式。    * 啟用路由的 debug 跟蹤模式,需要在根模塊中設置 `enableTracing: true`    */   imports: [RouterModule.forRoot(routes, { enableTracing: true })],   exports: [RouterModule] }) export class AppRoutingModule { }
登錄后復制

激活路由

找到 app.component.html 根組件模板,配置 router-outlet

通過模版屬性訪問路由,即路由鏈接 routerLink

<h1>   <a [routerLink]="['/home']">首頁</a>   <a [routerLink]="['/home',tab.link]">首頁</a><!-- 路徑參數 -->   <a [routerLink]="['/home',tab.link,{name:'val1'}]">首頁</a> <!-- 路徑對象參數 -->   <a [routerLink]="['/home']" [queryParams]="{name:'val1'}">首頁</a> <!-- 查詢參數 --> </h1> <router-outlet></router-outlet>  <!-- 路由插座,占位標簽 --> <!--   路由顯示的內容是插入到 router-outlet 的同級的下方節點   而不是在 router-outlet 中包含 --> <!--   當事件處理或者達到某個條件時,可以使用手動跳轉 	this.router.navigate(['home']);  	this.router.navigate(['home',tab.link]);  	this.router.navigate(['home',tab.link,{name:'val1'}]);  	this.router.navigate(['home'],{queryParams:{name:'val1'}});  -->
登錄后復制

控制路由激活狀態的樣式 routerLinkActive

<h1>     <a routerLink="/home" routerLinkActive="active">首頁</a>     <a routerLink="/news" routerLinkActive="active">新聞</a> </h1>  <h1>    <a [routerLink]="[ '/home' ]" routerLinkActive="active">首頁</a>    <a [routerLink]="[ '/news' ]" routerLinkActive="active">新聞</a> </h1>  .active{    color:red; }
登錄后復制

路由參數

路徑參數讀取

this.route.paramsMap.subscribe(params => {...})
登錄后復制

查詢參數讀取

this.route.queryParamsMap.subscribe(params => {...})
登錄后復制

路由傳遞一個參數及其接收方法:

傳遞參數:path:’info/:id’

接收參數:

constructor(private routerInfo: ActivatedRoute){} ngOnInit(){ 	this.routerInfo.snapshot.params['id'] }
登錄后復制

路由傳遞多個參數及其接收方法:

傳遞:[queryParams]=‘{id:1,name:‘crm’}’

接收參數:

constructor(private routerInfo: ActivatedRoute){} ngOnInit(){ 	this.routerInfo.snapshot.params['id'] 	this.routerInfo.snapshot.params['name'] }
登錄后復制

路由懶加載

懶加載子模塊,子模塊需要配置路由設置啟動子模塊 loadChildren

const routes: Routes = [     {path:'user',loadChildren:'./module/user/user.module#UserModule' },     {path:'product',loadChildren:'./module/product/product.module#ProductModule'},     {path:'article',loadChildren:'./module/article/article.module#ArticleModule'},     {path:'**',redirectTo:'user'} ];  // 上面好像會報錯 Error find module  // 配置懶加載 const routes: Routes = [     {path:'user',loadChildren:()=>import('./module/user/user.module').then(mod=>mod.UserModule)},     {path:'article',loadChildren:()=>import('./module/article/article.module').then(mod=>mod.ArticleModule)},     {path:'product',loadChildren:()=>import('./module/product/product.module').then(mod=>mod.ProductModule)},     {path:'**',redirectTo:'user'} ];
登錄后復制

Angular服務

組件不應該直接獲取或保存數據,應該聚焦于展示數據,而把數據訪問的職責委托給某個服務

獲取數據和視圖展示應該相分離,獲取數據的方法應該放在服務中

類似 VueX,全局的共享數據(通用數據)及非父子組件傳值、共享數據放在服務中

組件之間相互調用各組件里定義的方法

多個組件都用的方法(例如數據緩存的方法)放在服務(service)里

import { Injectable } from '@angular/core'; @Injectable({   providedIn: 'root', }) export class HeroService {   aa = 'abc';   constructor(){ }   ngOnInit(){ } }  import { HeroService } from '../../../services/hero/hero.service'; export class AComponent implements OnInit{   constructor(private heroService : HeroService) {} //實例化   ngOnInit(){     console.log(this.heroService.aa)   } }
登錄后復制

@Injectable()裝飾器

在 Angular 中,要把一個類定義為服務,就要用 @Injectable() 裝飾器來提供元數據,以便讓 Angular 把它作為依賴注入到組件中。

同樣,也要使用 @Injectable () 裝飾器來表明一個組件或其它類(比如另一個服務、管道或 NgModule)擁有一個依賴。

@Injectable () 裝飾器把這個服務類標記為依賴注入系統的參與者之一,它是每個 Angular 服務定義中的基本要素。

在未配置好 Angular 的依賴注入器時,Angular 實際上無法將它注入到任何位置。

@Injectable () 裝飾器具有一個名叫 providedIn 的元數據選項,providedIn 設置為 'root',即根組件中,那么該服務就可以在整個應用程序中使用了。

providedIn 提供這些值:‘root''platform' 、'any' 、null

對于要用到的任何服務,必須至少注冊一個提供者。

服務可以在自己的元數據中把自己注冊為提供者,可以讓自己隨處可用,也可以為特定的模塊或組件注冊提供者。

要注冊提供者,就要在服務的 @Injectable () 裝飾器中提供它的元數據,或者在 @NgModule ()@Component () 的元數據中。

在組件中提供服務時,還可以使用 viewProdiversviewProviders 對子組件樹不可見

可以使用不同層級的提供者來配置注入器,也表示該服務的作用范圍

  • Angular 創建服務默認采用的方式:在服務本身的 @Injectable () 裝飾器中

  • 該服務只在某服務中使用:在 NgModule 的 @NgModule () 裝飾器中

  • 該服務在某組件中使用:在組件的 @Component () 裝飾器中

依賴注入

在項目中,有人提供服務,有人消耗服務,而依賴注入的機制提供了中間的接口,并替消費者創建并初始化處理

消費者只需要知道拿到的是完整可用的服務就好,至于這個服務內部的實現,甚至是它又依賴了怎樣的其他服務,都不需要關注。

Angular 通過 service共享狀態,而這些管理狀態和數據的服務便是通過依賴注入的方式進行處理的

Angular 的 service 的本質就是依賴注入,將service作為一個Injector注入到component

歸根到底,很多時候我們創建服務,是為了維護公用的狀態和數據,通過依賴注入的方式來規定哪些組件可共享

深入了解Angular(新手入門指南)

正是因為 Angular 提供的這種依賴注入機制,才能在構造函數中直接聲明實例化

  constructor(private heroService : HeroService) {} // 依賴注入
登錄后復制

深入了解Angular(新手入門指南)

先看一下 Angular 中 TS 單文件的注入

// 首先寫 @injectable 我們需要注入的東西,比如說 product @Injectable() class Product {   constructor(     private name: string,     private color: string,     private price: number,   ) { } }  class PurchaseOrder {   constructor(private product: Product){ } }   export class HomeGrandComponent implements OnInit {   constructor() { }   ngOnInit() {     // 構造一個 injector 用 create 方法 里面 providers 數組中寫我們需要構造的東西     const injector = Injector.create({       providers: [         {           provide: Product,           // 構造 Product 在 useFactory 中就會把上面定義的 product 注入到這里           useFactory: () => {             return new Product('大米手機', '黑色', 2999);           },           deps: []         },         {           provide: PurchaseOrder,           deps: [Product]         },         {           provide: token,           useValue: { baseUrl: 'http://local.dev' }         }       ]     });      console.log('injector獲取product', injector.get(PurchaseOrder).getProduct);     console.log(injector.get(token));   }
登錄后復制

再看一下Angular 中 module 模塊的注入

// .service.ts 中 @Injectable () 依賴注入 @Injectable() export class HomeService {   imageSliders: ImageSlider[] = [     {       imgUrl:'',       link: '',       caption: ''     }   ]   getBanners() {     return this.imageSliders;   } }  // 使用模塊對應的.module.ts 中 @NgModule({   declarations: [     HomeDetailComponent,   ],   providers:[HomeService], // 在 providers 直接寫對應服務,直接將服務注入模塊   imports: [SharedModule, HomeRoutingModule] })
登錄后復制

不管是在組件內還是在模塊內,我們使用 providers 的時候,就是進行了一次依賴注入的注冊和初始化

其實模塊類(NgModule)也和組件一樣,在依賴注入中是一個注入器,作為容器提供依賴注入的接口

NgModule 使我們不需要在一個組件中注入另一個組件,通過模塊類(NgModule)可以進行獲取和共享

Angular 管道

Angular 管道是編寫可以在 HTML 組件中聲明的顯示值轉換的方法

管道將數據作為輸入并將其轉換為所需的輸出

管道其實就是過濾器,用來轉換數據然后顯示給用戶

管道將整數、字符串、數組和日期作為輸入,用 | 分隔,然后根據需要轉換格式,并在瀏覽器中顯示出來

在插值表達式中,可以定義管道并根據情況使用

Angular 應用程序中可以使用許多類型的管道

內置管道

  • String -> String
    • UpperCasePipe 轉換成大寫字符
    • LowerCasePipe 轉換成小寫字符
    • TitleCasePipe 轉換成標題形式,第一個字母大寫,其余小寫
  • Number -> String
    • DecimalPipe 根據數字選項和區域設置規則格式化值
    • PercentPipe 將數字轉換為百分比字符串
    • CurrencyPipe 改變人名幣格式
  • Object -> String
    • JsonPipe 對象序列化
    • DatePipe 日期格式轉換
  • Tools
    • SlicePipe 字符串截取
    • AsyncPipe 從異步回執中解出一個值
    • I18nPluralPipe 復數化
    • I18nSelectPipe 顯示與當前值匹配的字符串

使用方法

<div>{{ 'Angular' | uppercase }}</div>  <!-- Output: ANGULAR -->  <div>{{ data | date:'yyyy-MM-dd' }}</div>  <!-- Output: 2022-05-17 -->  <div>{{ { name: 'ccc' } | json }}</div>  <!-- Output: { "name": "ccc" } -->  <!--  	管道可以接收任意數量的參數,使用方式是在管道名稱后面添加: 和參數值 	若需要傳遞多個參數則參數之間用冒號隔開  -->  <!-- 可以將多個管道連接在一起,組成管道鏈對數據進行處理 --> <div>{{ 'ccc' | slice:0:1 | uppercase }}</div>
登錄后復制

自定義管道

管道本質上就是個類,在這個類里面去實現 PipeTransfrom 接口的 transform 這個方法

  • 使用 @Pipe 裝飾器定義 Pipemetadata 信息,如 Pipe 的名稱 – 即 name 屬性
  • 實現 PipeTransform 接口中定義的 transform 方法

// 引入PipeTransform是為了繼承transform方法 import { Pipe, PipeTransform } form '@angular/core';  // name屬性值慣用小駝峰寫法, name的值為html中 | 后面的名稱 @Pipe({ name: 'sexReform' })  export class SexReformPipe implements PipeTransform {     transform(value: string, args?: any): string {     // value的值為html中 | 前面傳入的值, args為名稱后傳入的參數         switch(value){             case 'male': return '男';             case 'female': return '女';             default: return '雌雄同體';         }      } }  // demo.component.ts export Class DemoComponent {     sexValue = 'female'; }  // demo.component.html <span>{{ sexValue | sexReform }}</span>  // 瀏覽器輸出 女  // 管道可以鏈式使用,還可以傳參 <span> {{date | date: 'fullDate' | uppercase}} </span> // 每一個自定義管道都需要實現 PipeTransform 接口,這個接口非常簡單,只需要實現 transform 方法即可。 // transform()方法參數格式 - transform(value: string, args1: any, args2?: any):  // value為傳入的值(即為需要用此管道處理的值, | 前面的值);  // args 為傳入的參數(?:代表可選); // html 中使用管道格式 - {{ 數據 | 管道名 : 參數1 : 參數2 }} // 與 component 一樣,pipe 需要先在 declarations 數組中聲明后使用
登錄后復制

Angular操作DOM

原生JS操作

ngAfterViewInit(){    var boxDom:any=document.getElementById('box');    boxDom.style.color='red'; }
登錄后復制

ElementRef

ElementRef 是對視圖中某個原生元素的包裝類

因為 DOM 元素不是 Angular 中的類,所以需要一個包裝類以便在 Angular 中使用和標識其類型

ElementRef 的背后是一個可渲染的具體元素。在瀏覽器中,它通常是一個 DOM 元素

class ElementRef<T> {   constructor(nativeElement: T)   nativeElement: T  //背后的原生元素,如果不支持直接訪問原生元素,則為 null(比如:在 Web Worker 環境下運行此應用的時候)。 }
登錄后復制

當需要直接訪問 DOM 時,請把本 API 作為最后選擇 。優先使用 Angular 提供的模板和數據綁定機制

如果依賴直接訪問 DOM 的方式,就可能在應用和渲染層之間產生緊耦合。這將導致無法分開兩者,也就無法將應用發布到 Web Worker 中

ViewChild

使用模板和數據綁定機制,使用 @viewChild

// 模版中給 DOM 起一個引用名字,以便可以在組件類或模版中進行引用 <div #myattr></div>  // 引入 ViewChild import { ViewChild,ElementRef } from '@angular/core';  // 用 ViewChild 綁定 DOM	 @ViewChild('myattr') myattr: ElementRef;  // 在 ngAfterViewInit 生命周期函數里可以很安全的獲取 ViewChild 引用的 DOM ngAfterViewInit(){    let attrEl = this.myattr.nativeElement; }
登錄后復制

父組件中可以通過 ViewChild 調用子組件的方法

// 給子組件定義一個名稱 <app-footer #footerChild></app-footer>  // 引入 ViewChild import { Component, OnInit ,ViewChild} from '@angular/core';  // ViewChild 和子組件關聯起來  // 如果想引用模版中的 Angular 組件,ViewChild 中可以使用引用名,也可以使用組件類型 @ViewChild('footerChild') footer;  // @ViewChild('imageSlider', { static: true }) // static指定是動態還是靜態,在*ngFor或者*ngIf中是動態,否則即為靜態,動態為 true  // 調用子組件 run(){    this.footer.footerRun(); }
登錄后復制

引用多個模版元素,可以用@ViewChildren,在ViewChildren中可以使用引用名

或者使用 Angular 組件/指令的類型,聲明類型為 QueryList<?>

<img   #img   *ngFor="let slider of sliders"   [src]="slider.imgUrl"   [alt]="slider.capiton" >  // 使用 ViewChildren 引用獲取 @ViewChildren(’img‘);  // 使用類型引用獲取 imgs: QueryList<ElementRef>;
登錄后復制

Renderer2

Renderer2 是 Angular 提供的操作 element 的抽象類,使用該類提供的方法,能夠實現在不直接接觸 DOM 的情況下操作頁面上的元素。

Renderer2 的常用方法:

  • addClass /removeClassdirective 的宿主元素添加或刪除 class

import { Directive, Renderer2, ElementRef, OnInit } from '@angular/core';  @Directive({     selector: '[testRenderer2]' })  export class TestRenderer2Directive implements OnInit {     constructor(private renderer: Renderer2, private el: ElementRef) {} // 實例化      ngOnInit() {     this.renderer.addClass(this.el.nativeElement, 'test-renderer2');     // this.renderer.removeClass(this.el.nativeElement, 'old-class');     } }
登錄后復制

  • createElement /appendChild/createText 創建 DIV 元素,插入文本內容,并將其掛載到宿主元素上

import { Directive, Renderer2, ElementRef, OnInit } from '@angular/core';  constructor(private renderer: Renderer2, private el: ElementRef) {}  ngOnInit() {     const div = this.renderer.createElement('div');     const text = this.renderer.createText('Hello world!');          this.renderer.appendChild(div, text);     this.renderer.appendChild(this.el.nativeElement, div); }
登錄后復制

  • setAttribute /removeAttribute 在宿主元素上添加或刪除 attribute

import { Directive, Renderer2, ElementRef, OnInit } from '@angular/core';  constructor(private renderer: Renderer2, private el: ElementRef) {}  ngOnInit() {     this.renderer.setAttribute(this.el.nativeElement, 'aria-hidden', 'true'); }
登錄后復制

  • setStyle /removeStyle 在宿主元素上添加 inline-style

import { Directive, Renderer2, ElementRef, OnInit } from '@angular/core';  constructor(private renderer: Renderer2, private el: ElementRef) {}  ngOnInit() {     this.renderer.setStyle(         this.el.nativeElement,         'border-left',         '2px dashed olive'     ); }
登錄后復制

移除 inline-style :

constructor(private renderer: Renderer2, private el: ElementRef) {}  ngOnInit() {     this.renderer.removeStyle(this.el.nativeElement, 'border-left'); }
登錄后復制

  • setProperty 設置宿主元素的 property 的值

constructor(private renderer: Renderer2, private el: ElementRef) {}  ngOnInit() {     this.renderer.setProperty(this.el.nativeElement, 'alt', 'Cute alligator'); }
登錄后復制

直接操作DOM,Angular不推薦。盡量采用 @viewChildrenderer2 組合,Angular推薦使用 constructor(private rd2: Renderer2) {} 依賴注入,

import {   Component,   OnInit,   Renderer2,   ViewChild, } from '@angular/core'; import { AboxItemComponent } from './abox-item/abox-item.component';   @Component({   selector: 'app-abox',   templateUrl: './abox.component.html',   styleUrls: ['./abox.component.less'], }) export class AboxComponent implements OnInit {   private container;   activeIndex: number;   @ViewChild('containers') containers: any;   constructor(private rd2: Renderer2) {}     ngOnInit(): void {}     ngAfterViewInit(): void {     this.container = this.containers.nativeElement;     this.initCarouselWidth();   }        initCarouselWidth() {     this.rd2.setStyle(this.container, 'width', '100px');   } }
登錄后復制

Angular網絡請求

HttpClient

需導入 HttpClientModule ,只在根模塊中導入,并且整個應用只需導入一次,不用在其他模塊導入

在構造函數中注入HttpClient,get/post方法對應HTTP方法,這些方法是泛型的,可以直接把返回的JSON轉換成對應類型。若是不規范的請求,使用request方法

返回的值是 Observable,必須訂閱才會發送請求,否則不會發送

get 請求數據

// 在 app.module.ts 中引入 HttpClientModule 并注入 import {HttpClientModule} from '@angular/common/http'; imports: [   BrowserModule,   HttpClientModule ]  // 在用到的地方引入 HttpClient 并在構造函數聲明 import {HttpClient} from "@angular/common/http"; constructor(private http: HttpClient,private cd: ChangeDetectorRef) { } // 依賴注入  // get 請求數據 var api = "http://baidu.com/api/productlist"; this.http.get(api).subscribe(response => {   console.log(response);   this.cd.markForCheck();   // 如果改變了臟值檢測的變更原則 changeDetection: ChangeDetectionStrategy.OnPush   // 則需要使用 this.cd.markForCheck() 手動提醒 Angular 這里需要進行臟值檢測 });
登錄后復制

post 提交數據

// 在 app.module.ts 中引入 HttpClientModule 并注入 import {HttpClientModule} from '@angular/common/http'; imports: [    BrowserModule,    HttpClientModule ]  // 在用到的地方引入 HttpClient 、HttpHeaders 并在構造函數聲明 HttpClient import {HttpClient,HttpHeaders} from "@angular/common/http"; constructor(private http:HttpClient) { } // 實例化  // post 提交數據 const httpOptions = {     headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; var api = "http://127.0.0.1:4200/doLogin"; this.http.post(api,{username:'瑞萌萌',age:'22'},httpOptions).subscribe(response => { 		console.log(response); });
登錄后復制

Jsonp請求數據

// 在 app.module.ts 中引入 HttpClientModule、HttpClientJsonpModule 并注入 import {HttpClientModule,HttpClientJsonpModule} from'@angular/common/http'; imports: [    BrowserModule,    HttpClientModule,    HttpClientJsonpModule ]  // 在用到的地方引入 HttpClient 并在構造函數聲明 import {HttpClient} from "@angular/common/http"; constructor(private http:HttpClient) { } // 實例化  // jsonp 請求數據 var api = "http://baidu.com/api/productlist"; this.http.jsonp(api,'callback').subscribe(response => {    console.log(response); });
登錄后復制

攔截器

Angular 攔截器是 Angular 應用中全局捕獲和修改 HTTP 請求和響應的方式,例如攜帶 Token 和捕獲 Error

前提是只能攔截使用 HttpClientModule 發出的請求,如果使用 axios 則攔截不到

創建攔截器

// 使用命令 ng g interceptor name,在這里創建攔截器 ng g interceptor LanJieQi // cli 生成攔截器是沒有簡寫方式的  import { Injectable } from '@angular/core'; import {   HttpRequest,   HttpHandler,   HttpEvent,   HttpInterceptor } from '@angular/common/http'; import { Observable } from 'rxjs';  @Injectable() export class LanJieQiInterceptor implements HttpInterceptor {   constructor() {}   // 默認的 intercept() 方法只是單純的將請求轉發給下一個攔截器(如果有),并最終返回 HTTP 響應體的 Observable   // request: HttpRequest<unknown> 表示請求對象,包含了請求相關的所有信息,unknown指定請求體body的類型   // next: HttpHandler 請求對象修改完成,將修改后的請求對象通過next中的handle方法傳回真正發送請求的方法中   intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> 	{     // next 對象表示攔截器鏈表中的下一個攔截器(在應用中可以設置多個攔截器)     return next.handle(request);   } }
登錄后復制

注入攔截器

// 在 @NgModule 模塊中注入攔截器 // 攔截器也是一個由 Angular 依賴注入 (DI) 系統管理的服務,也必須先提供這個攔截器類,才能使用它 // 由于攔截器是 HttpClient 服務的依賴,所以必須在提供 HttpClient 的同一個(或其各級父注入器)注入器中提供這些攔截器 @NgModule({   imports: [     HttpClientModule     // others...   ],   providers: [     {       provide: HTTP_INTERCEPTORS,       useClass: LanJieQiInterceptor,       // multi: true 表明 HTTP_INTERCEPTORS 是一個多重提供者的令牌,表示這個令牌可以注入多個攔截器       multi: true     },   ],   bootstrap: [AppComponent] })  export class AppModule { }
登錄后復制

請求頭攔截

@Injectable()export class LanJieQiInterceptor implements HttpInterceptor {   constructor() {}   intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> 	{     // 為了統一設置請求頭,需要修改請求     // 但 HttpRequest 和 HttpResponse 實例的屬性卻是只讀(readonly)的     // 所以修改前需要先 clone 一份,修改這個克隆體后再把它傳給 next.handle()     let req = request.clone({     	setHeaders:{       	token:"123456" // 在請求頭中增加 token:123456     	} 			// setHeaders 和 headers: request.headers.set('token', '123456') 一致   	})   	return next.handle(req)// 將修改后的請求返回給應用   }}
登錄后復制

響應捕獲

@Injectable() export class LanJieQiInterceptor implements HttpInterceptor {   constructor() {}   intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> 	{     // 為了統一設置請求頭,需要修改請求     // 但 HttpRequest 和 HttpResponse 實例的屬性卻是只讀(readonly)的     // 所以修改前需要先 clone 一份,修改這個克隆體后再把它傳給 next.handle()     let req = request.clone({     	setHeaders:{       	token:"123456" // 在請求頭中增加 token:123456     	} 			// setHeaders 和 headers: request.headers.set('token', '123456') 一致   	})   	return next.handle(req)// 將修改后的請求返回給應用   } }
登錄后復制

如果有多個攔截器,請求順序是按照配置順序執行,響應攔截則是相反的順序

如果提供攔截器的順序是先 A再 B再 C,那么請求階段的執行順序就是 A->B->C,而響應階段的執行順序則是 C->B->A

Angular表單

模版驅動表單

模板驅動表單在往應用中添加簡單的表單時非常有用,但是不像響應式表單那么容易擴展

如果有非常基本的表單需求和簡單到能用模板管理的邏輯,就使用模板驅動表單

響應式表單和模板驅動表單共享了一些底層構造塊:

FormControl 實例用于追蹤單個表單控件的值和驗證狀態

FormGroup 用于追蹤一個表單控件組的值和狀態

FormArray 用于追蹤表單控件數組的值和狀態,有長度屬性,通常用來代表一個可以增長的字段集合

ControlValueAccessor 用于在 Angular 的 FormControl 實例和原生 DOM 元素之間創建一個橋梁

FormControlFormGroup 是 angular 中兩個最基本的表單對象

FormControl 代表單一的輸入字段,它是 Angular 表單中最小單員,它封裝了這些字段的值和狀態,比如是否有效、是否臟(被修改過)或是否有錯誤等

FormGroup 可以為一組 FormControl 提供總包接口(wrapper interface),來管理多個 FormControl

當我們試圖從 FormGroup 中獲取 value 時,會收到一個 “鍵值對” 結構的對象

它能讓我們從表單中一次性獲取全部的值而無需逐一遍歷 FormControl,使用起來相當順手

FormGroupFormControl 都繼承自同一個祖先 AbstractControltractControl(這是 FormControlFormGroupFormArray 的基類)

首先加載 FormsModule

// 先在 NgModule 中導入了 FormsModule 表單庫 // FormsModule 為我們提供了一些模板驅動的指令,例如:ngModel、NgForm import {    FormsModule } from '@angular/forms';   @NgModule({    declarations: [      FormsDemoApp,      DemoFormSku,      // ... our declarations here    ],    imports: [      BrowserModule,      FormsModule,   ],    bootstrap: [ FormsDemoApp ]  })  class FormsDemoAppModule {}
登錄后復制

接下來創建一個模版表單

 <div>       <h2>基礎表單:商品名稱</h2>       <form #f="ngForm" (ngSubmit)="onSubmit(f.value)">         <div class="sku">           <label for="skuInput">商品名稱:</label>           <input             type="text"             id="skuInput"             placeholder="商品名稱"             name="sku" //使用form時必須定義,可以理解為當前控件的名字             ngModel           />         </div>         <button>提交</button>       </form>     </div>
登錄后復制

我們導入了 FormsModule,因此可以在視圖中使用 NgForm

當這些指令在視圖中可用時,它就會被附加到任何能匹配其 selector 的節點上

NgForm 做了一件便利但隱晦的工作:它的選擇器包含 form 標簽(而不用顯式添加 ngForm 屬性)

這意味著當導入 FormsModule 時候,NgForm 就會被自動附加到視圖中所有的標簽上

NgForm 提供了兩個重要的功能:

  • 一個 ngFormFormGroup 對象
  • 一個輸出事件 (ngSubmit)

 <form #f="ngForm" (ngSubmit)="onSubmit(f.value)" >  <!--  	這里使用了 #f=“ngForm”,#v=thing 的意思是我們希望在當前視圖中創建一個局部變量 	這里為視圖中的 ngForm 創建了一個別名,并綁定到變量 #f 	這個 ngForm 是由 NgForm 指令導出的 	ngForm 的類型的對象是 FormGroup 類型的 	這意味著可以在視圖中把變量 f 當作 FormGroup 使用,而這也正是我們在輸出事件 (ngSubmit) 中的使用方法 	在表單中綁定 ngSubmit 事件 (ngSubmit)=“onSubmit (f.value)“ 	(ngSubmit) 來自 NgForm 指令 	onSubmit() 將會在組件類中進行定義 	f 就是 FormGroup ,而 .value 會以鍵值對的形式返回 FormGroup 中所有控件的值 	 	總結:當提交表單時,將會以該表單的值作為參數,調用組件實例上的 `onSubmit` 方法 -->
登錄后復制

NgModel 會創建一個新的 FormControl 對象,把它自動添加到父 FormGroup 上(這里也就是 form 表單對象)

并把這個 FormControl 對象綁定到一個 DOM 上

也就是說,它會在視圖中的 input 標簽和 FormControl 對象之間建立關聯

這種關聯是通過 name 屬性建立的,在本例中是 "name"

響應式表單

使用 ngForm 構建 FormControlFormGroup 很方便,但是無法提供定制化選項,因此引入響應式表單

響應式表單提供了一種模型驅動的方式來處理表單輸入,其中的值會隨時間而變化

使用響應式表單時,通過編寫 TypeScript 代碼而不是 HTML 代碼來創建一個底層的數據模型

在這個模型定義好以后,使用一些特定的指令將模板上的 HTML 元素與底層的數據模型連接在一起

FormBuilder 是一個名副其實的表單構建助手(可以把他看作一個 “工廠” 對象)

在先前的例子中添加一個 FormBuilder,然后在組件定義類中使用 FormGroup

// 先在 NgModule 中導入了 ReactiveFormsModule 表單庫 import {    ReactiveFormsModule  } from '@angular/forms';  @NgModule({   imports: [     FormsModule,     ReactiveFormsModule   ] })   // 使用 formGroup 和 formControl 指令來構建這個組件,需要導入相應的類 import {    FormBuilder,    FormGroup,   ReactiveFormsModule } from '@angular/forms';   // 在組件類上注入一個從 FormBuilder 類創建的對象實例,并把它賦值給 fb 變量(來自構造函數) export class DemoFormSkuBuilder {    myForm: FormGroup;  // myForm 是 FormGroup 類型   constructor(fb: FormBuilder) {      // FormBuilder 中的 group 方法用于創建一個新的 FormGroup     // group 方法的參數是代表組內各個 FormControl 的鍵值對     this.myForm = fb.group({  // 調用 fb.group () 來創建 FormGroup       // 設置一個名為 sku 的控件,控件的默認值為 "123456"       'sku': ['123456']      });    }   onSubmit(value: string): void {      console.log('submit value:', value);    }  }
登錄后復制

在視圖表單中使用自定義的 FormGroup

<h2 class="ui header">Demo Form: Sku with Builder</h2> <!--   	當導入 FormsModule 時,ngForm 就會自動創建它自己的 FormGroup 	但這里不希望使用外部的 FormGroup,而是使用 FormBuilder 創建這個 myForm 實例變量 	Angular提供了 formGroup 指令,能讓我們使用現有的 FormGroup 	NgForm 不會應用到帶 formGroup 屬性的節點上 	這里我們告訴Angular,想用 myForm 作為這個表單的 FormGroup --> <form [formGroup]="myForm"    <label for="skuInput"> SKU </label>    <input type="text"       id="skuInput"       placeholder="SKU"       [formControl]="myForm.controls['sku']"> <!--   	將 FormControl 綁定到 input 標簽上 :  	ngModel 會創建一個新的 FormControl 對象并附加到父 FormGroup 中 	但在例子中,我們已經用 FormBuilder 創建了自己的 FormControl 	要將現有的 FormControl 綁定到 input 上,可以用 formControl 指令 	將 input 標簽上的 formControl 指令指向 myForm.controls 上現有的 FormControl 控件 sku   -->
登錄后復制

記住以下兩點:

  1. 如果想隱式創建新的 FormGroup 和 FormControl,使用:ngForm、ngModel
  2. 如果要綁定一個現有的 FormGroup 和 FormControl,使用:formGroup、formControl

表單驗證

用戶輸入的數據格式并不總是正確的,如果有人輸入錯誤的數據格式,我們希望給他反饋并阻止他提交表單

因此,我們要用到驗證器,由 validators 模塊提供

Validators.required 是最簡單的驗證,表明指定的字段是必填項,否則就認為 FormControl 是無效的

如果 FormGroup 中有一個 FormControl 是無效的, 那整個 FormGroup 都是無效的

要為 FormControl 對象分配一個驗證器 ,可以直接把它作為第二個參數傳給 FormControl 的構造函數

const control = new FormControl('name', Validators.required);  // 在組件定義類中使用 FormBuilder   constructor(fb: FormBuilder) {      this.myForm = fb.group({        'name': ['',Validators.required]      });      this.name = this.myForm.controls['name'];    }
登錄后復制

在視圖中檢查驗證器的狀態,并據此采取行動

template:`<div>       <h2>商品表單:商品名稱</h2>       <form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm)">         <div>           <label for="nameInput">商品名稱:</label>           <input             type="text"             id="nameInput"             placeholder="請輸入名稱"             [formControl]="myForm.controls['name']"           />           <div style="color:red" *ngIf="!name.valid">             名稱無效           </div>           <div style="color:red" *ngIf="name.hasError('textinvalid')">             名稱不是以“123”開頭           </div>           <div *ngIf="name.dirty">             數據已變動           </div>         </div>         <div>           <label for="codeInput">商品料號:</label>           <input             type="text"             id="codeInput"             placeholder="請輸入料號"             [formControl]="myForm.controls['code']"           />           <div             style="color:red"             *ngIf="myForm.controls.code.hasError('required')"           >             該項必填           </div>           <div             style="color:red"             *ngIf="myForm.controls.code.hasError('pattern')"           >             只可輸入數字和英文           </div>         </div>         <div style="color:green" *ngIf="myForm.isvalid">           表單無效         </div>         <div style="color:green" *ngIf="myForm.valid">           表單有效         </div>         <button type="submit">提交</button>       </form>     </div>` export class NonInWarehouseComponent implements OnInit {   myForm: FormGroup;   name: AbstractControl;   constructor(fb: FormBuilder) {     this.myForm = fb.group({       name: ['牛奶', Validators.compose([Validators.required, textValidator])],       code: ['', [Validators.required, Validators.pattern('^[A-Za-z0-9]*$')]],     });     this.name = this.myForm.controls.name;   }   ngOnInit() {     const nameControl = new FormControl('nate');     console.log('nameControl', nameControl);   }   onSubmit(a: any) {     console.log('a', a);   } }
登錄后復制

內置校驗器

Angular 提供了幾個內置校驗器,下面是比較常用的校驗器:

  • Validators.required – 表單控件值非空
  • Validators.email – 表單控件值的格式是 email
  • Validators.minLength() – 表單控件值的最小長度
  • Validators.maxLength() – 表單控件值的最大長度
  • Validators.pattern() – 表單控件的值需匹配 pattern 對應的模式(正則表達式)

自定義驗證器

假設我們的 name 有特殊的驗證需求,比如 name 必須以 123 作為開始

當輸入值(控件的值 control.value)不是以 123 作為開始時,驗證器會返回錯誤代碼 invalidSku

// angular 源代碼中實現 Validators.required  export class Validators {   // 接收一個 AbstractControl 對象作為輸入 	static required(control: AbstractControl): ValidationErrors | null; } // 當驗證器失敗時,會返回一個 String Map<string,any> 對象,他的鍵是” 錯誤代碼 “,它的值是 true export declare type ValidationErrors = {     [key: string]: any; };  // 自定義驗證器 function textValidator(   controls: FormControl // 因為FormControl繼承于 AbstractControl 所以也可以寫成FormControl對象 ): {   [s: string]: boolean; } {   if (!controls.value.match(/^123/)) {     return { textinvalid: true };   } }
登錄后復制

FormControl 分配驗證器,但是 name 已經有一個驗證器了,如何在同一個字段上添加多個驗證器

Validators.compose 來實現

Validators.compose 把兩個驗證器包裝在一起,我們可以將其賦值給 FormControl

只有當兩個驗證器都合法時,FormControl 才是合法的

Validators.compose([Validators.required, textValidator]) // 不用compose   [Validators.required, textValidator] // 保留 compose 是為了向以前歷史版本進行兼容,不用 compose 也可實現
登錄后復制

動態表單

要實現 Angular 動態表單,主要使用 formArray 方法,formArray 生成的實例是一個數組,在這個數組中可以動態的放入 formGroupformControl,這樣便形成了動態表單。

export class ReativeFormsComponent implements OnInit {   ngOnInit() {     this.addContact()   }   //動態表單   personMess: FormGroup = new FormGroup({     //生成動態表單數組     contacts: new FormArray([])    })   //獲取數組對象   get contacts(){     return this.personMess.get('contacts') as FormArray   }   //增加一個表單組   addContact(){     let myContact = new FormGroup({       name: new FormControl(),       phone: new FormControl()     })     this.contacts.push(myContact)   }    //刪除一個表單組   deleteContact(i:number){     this.contacts.removeAt(i)   }   //提交表單   OnSubmit() {     console.log(this.personMess.value)   } }
登錄后復制

<form [formGroup]="personMess" (submit)="OnSubmit()">   <div formArrayName="contacts">     <!-- 注意:這里遍歷的時contacts.controls -->     <div *ngFor="let contact of contacts.controls;let i =index" [formGroupName]="i">       <input type="text" formControlName="name">       <input type="text" formControlName="phone">       <button (click)="deleteContact(i)">刪除信息</button>     </div>   </div>   <button (click)="addContact()">添加信息</button><br>   <input type="submit"> </form>
登錄后復制

Angular CDK

CDK 是 Component Dev kit 的簡稱,是 Angular Material 團隊在開發 Library 時發現組件有很多相似的地方,最后進行了抽取,提煉出了公共的邏輯,這部分即是 CDK

官方用了一個很形象的比喻:如果組件庫是火箭飛船,那么 CDK 就是發動機零件盒
深入了解Angular(新手入門指南)

贊(0)
分享到: 更多 (0)
?
網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
国产精品一线二线三线 | 欧美人与鲁交大毛片免费| 男人边吃奶边挵进去呻吟动态图| 美女扒开腿让男人桶爽揉| 欧美丰满少妇XXXXX| 人人妻人人添人人爽欧美一区| 日韩GAY小鲜肉啪啪18禁| 偷拍亚洲另类无码专区制服 | 秋霞国产午夜伦午夜福利片| 日韩综合亚洲色在线影院| 天天躁日日躁狠狠躁退| 亚洲AV无码乱码| 亚洲综合熟女久久久30P| 18款夜间禁用粉色短视频软件| 边做边爱完整版免费视频播放| 首页 综合国产 亚洲 丝袜日本 | 四虎库影必出精品8848| 无码人妻斩一区二区三区| 亚洲精品国产精品乱码不99| 中国女人内射69XXXXⅩ视频| 被部长灌醉后强行侵犯| 国产精品免费看久久久无码| 精品亚洲AV无码喷奶水A漫画| 蜜臀久久AV无码牛牛影视| 日本熟妇人妻XXXXX| 羞羞漫画_成人漫画_成人专用| 亚洲欧美乱日韩乱国产| 99国产欧美另娄久久久精品| 高H乱好爽要尿了潮喷了学生| 极品粉嫩小泬50P| 女人被弄到高潮的免费视频 | 丰满人妻一区二区三区Aⅴ在线| 韩国三级大全久久网站| 奶头从情趣内衣下露了出来AV| 少妇高潮惨叫久久久久久| 亚洲国产欧美在线综合| 99久久精品费精品国产| 久久人人爽人人爽人人片AV高清| 宝宝腿趴开一点就不会疼的原因| 国产精品无码午夜福利| 老妇做爰XXXⅩ性视频| 三上悠亚公侵犯344在线观看 | 国偷自产AV一区二区三区| 男人的天堂在线视频| 玩弄JaPan白嫩少妇一区二区| 亚洲少妇一区二区视频| 成人午夜福利视频网址| 精品人妻一区二区三区曰产乱码 | HD专干中国老太婆| 国产一区二区三区小说| 欧美成人精品高清在线播放 | 2021国产精品国产精华| 国产精品丝袜无码不卡一区| 免费专区SM美脚践踏调教视频| 无码人妻精品一区二区三区蜜桃| 中日韩人妻中文字幕视频在线| 人人妻人人爽人人澡人人| 亚洲AV成人精品五区| 99无码熟妇丰满人妻啪啪| 国产一区二区三区在线视頻| 欧美极品少妇XXXXⅩ喷水| 亚洲GV永久无码天堂网| 插花弄玉小说荔枝很甜| 久久精品成人无码观看不卡| 少妇激情AV一区二区三区| 在线国内永久免费CRM| 国产裸体舞一区二区三区| 欧美高清VIVO最新| 亚洲欧美日韩成人综合网| 丰满多毛的大隂户BEST| 老外免费CSGO交易网站有哪些 | 国产成人麻豆精品午夜福利在线| 迈开腿让我尝一下你的味道一| 香蕉久久AV一区二区三区APP| 亚洲AV无码一区二区少妇| 锕锕锕锕锕锕锕锕轻点好疼视频| 精品亚洲成在人线AV无码 | 丰满熟妇大肉唇张开| 麻豆国产成人AV| 欧美日韩一区二区在线视频精品 | 日本中文字幕乱码免费| 尤物爆乳AV导航| 国精产品W灬源码1688网站 | 每晚都被他添的流好多水| 亚洲AV无码成H人动漫网站| 大荫蒂又大又长又硬又紧又粗 | 色欲av一区二区三区蜜臀| 6080午夜三级中文在线观看| 精品一区二区三区免费视频| 五月丁香综合缴情六月小说| 成人污污污WWW网站免费| 男生把QQ放进女生的QQ里韩剧| 亚洲女人操BB在线| 国产精品一线二线三线| 手机国产乱子伦精品视频| GOGO人体大胆瓣开下部L| 久久影院九九被窝爽爽| 亚洲国产精品一区二区第四页| 国产精品导航一区二区| 日本一线和三线的区别| 99久久久成人国产精品免费| 久久人妻AV中文字幕| 亚洲国产AV一区二区三区四区 | 东京热无码AV一区二区| 欧美一区二区三区精品视频| 久久超碰97人人做人人爱| 东京热一区二区三区| 欧美人妻精品一区二区| 中国成熟妇女毛茸茸| 久久久久久精品天堂无码中文字幕| 亚洲AV无码成人精品国产| 国产精品免费久久久久影院| 日韩视频中文字幕精品偷拍| 把腿张开老子CAO烂你NP皇宫| 能在线观看的一区二区三区| 中国老太太BBVBBⅤ| 久久久一本精品99久久精品88| 亚洲国产日韩欧美一区二区三区| 国产精品美女久久久久久| 天天躁夜夜躁天干天干2020| 国产99久久精品一区二区| 少妇高潮惨叫久久久久久| 丰满爆乳BBWBBWBBW| 日韩人妻在线一区二区三区| 车上震动A级作爱视频| 人人狠狠综合久久亚洲婷婷| 凹凸视频免费在线| 日本XXXX洗澡ⅩXXX偷窥| 啊灬啊灬啊灬快高潮视频| 欧洲洲一区二区精华液| А√天堂资源地址在线| 人妻熟妇乱又伦精品HD | 亚洲综合色婷婷在线观看| 久久久久精品国产亚洲AV| 一本久久A久久精品亚洲| 爱丫爱丫影院在线| 人妻精品久久久久中文字幕一冢本 | 老外粗猛长爽的视频| 1313午夜精品理论片| 女人被狂C到高潮视频网站| FREE性丰满白嫩白嫩的HD| 人妻无码ΑV中文字幕久久琪琪布| WWW一区二区WWW免费| 人人爽天天碰天天躁夜夜躁| 赤裸羔羊Ⅲ致命快感| 天干天干天啪啪夜爽爽AV| 国产精品毛多多水多| 亚洲AV极品熟妇一品二品三品| 极品女教师波多野结衣电影衣| 亚洲欧美xxxx| 老男人久久青草AV高清| 97国语精品自产拍在线观看一| 护士交换粗吟配乱大交| 亚洲欧美中文日韩V在线观看不卡 亚洲欧美中文日韩V在线观看 | 亚洲 都市 校园 激情 另类| 精东传媒VS天美传媒合作| 一本大道色婷婷在线| 女人三A级毛片视频| 成在人线AV无码免费高潮水老板| 丝袜中文人妻无码有码久热| 国产无遮挡裸体免费视频在线观看 | 欧美黑人aAAAAAAa| 嗯快点别停舒服好爽受不了了| 午夜精品四季AV日日骚| 精品无码久久久久成人漫画 | 亚洲VA在线VA天堂VA不卡| 久久月本道色综合久久| YOUJIZZCOM中国熟妇| 天天躁夜夜躁很很躁| 护士高潮喷水白浆| 中文字幕久久久久久精品| 日产无人区一线二线三线最新版| 国产成人亚洲精品无码最新| 亚洲欧美日韩国产精品一区二区| 男男黄GAY片免费网站WWW| 从大树开始的进化漫画| 性少妇JEAⅠOUSVU片| 久久综合九色综合久99| 被男狂揉吃奶胸60分钟视频| 香草乱码一二三四区别| 看成年女人午夜毛片免费| 爆乳无码AV一区二区三区小说| 无码日韩精品一区二区免费暖暖| 久久av蜜臀人妻一区二区三区 | 欧美精品视频一区二区| 国产精华AV午夜在线观看| 亚洲一卡2卡3卡4卡精品分类| 人狗大战JAVA代码| 国产午夜视频在线观看| 18禁无码无遮挡H动漫免费看| 熟妇人妻久久中文字幕老熟妇| 娇妻被朋友征服中文字幕| JEALOUSVUE日本人护士| 性无码专区一色吊丝中文字幕 | 色舞月亚洲综合一区二区| 九九九九精品视频在线观看| 办公室被吃奶好爽在线观看| 亚洲国产AV无码专区亚洲AV| 人妻AV一区二区| 精品国产三级A∨在线无码|