欧美亚洲中文,在线国自产视频,欧洲一区在线观看视频,亚洲综合中文字幕在线观看

      1. <dfn id="rfwes"></dfn>
          <object id="rfwes"></object>
        1. 站長資訊網(wǎng)
          最全最豐富的資訊網(wǎng)站

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

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

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

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

          Angular概述

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

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

          Angular程序架構(gòu)

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

          Angular優(yōu)勢

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

          @angular/cli腳手架

          ng new 新建項目

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

          ng serve 啟動項目

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

          ng build 打包項目

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

          ng generate 創(chuàng)建模塊/組件/服務(wù)

          • module ——routing 創(chuàng)建模塊
          • component 創(chuàng)建組件
          • service / 創(chuàng)建服務(wù)

          文件加載順序

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

          項目目錄結(jié)構(gòu)

          |-- project 	|-- .editorconfig // 用于在不同編輯器中統(tǒng)一代碼風(fēng)格 	|-- .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 // 整個工作區(qū)的ts配置文件 	|-- tsconfig.spec.json // 用于測試的ts配置文件 	|-- tslint.json // ts的代碼靜態(tài)掃描配置 	|-- e2e // 自動化集成測試目錄 	|-- src // 源代碼目錄   |-- src // 源代碼目錄 	|-- favicon.ico // 收藏圖標(biāo) 	|-- index.html // 單頁應(yīng)用到宿主HTML 	|-- main.ts // 入口 ts 文件 	|-- polyfills.ts // 用于不同瀏覽器的兼容腳本加載 	|-- styles.css // 整個項目的全局css 	|-- test.ts // 測試入口 	|-- app // 工程源碼目錄 	|-- assets // 資源目錄 	|-- environments // 環(huán)境配置 		|-- environments.prod.ts // 生產(chǎn)環(huán)境 		|-- environments.ts // 開發(fā)環(huán)境
          登錄后復(fù)制

          Angular模塊

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

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

          @NgModule 裝飾器

          @NgModule 接受一個元數(shù)據(jù)對象,告訴 Angular 如何編譯和啟動應(yīng)用

          設(shè)計意圖

          • 靜態(tài)的元數(shù)據(jù)(declarations)
          • 運行時的元數(shù)據(jù)(providers)
          • 組合與分組(imports 和 exports)

          元數(shù)據(jù)

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

          內(nèi)置模塊

          常用的有:核心模塊、通用模塊、表單模塊、網(wǎng)絡(luò)模塊等

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

          自定義模塊

          當(dāng)項目比較小的時候可以不用自定義模塊

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

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

          模塊的tips

          導(dǎo)入其他模塊時,需要知道使用該模塊的目的

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

          需要在每個需要的模塊中進行導(dǎo)入的

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

          只在根模塊導(dǎo)入一次的

          • HttpClientModule / BrowerAnimationsModule NoopAnimationsModule
          • 只提供服務(wù)的模塊

          Angular組件

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

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

          @Component 元數(shù)據(jù)

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

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

          數(shù)據(jù)綁定

          • 數(shù)據(jù)綁定 {{data}}

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

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

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

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

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

          臟值檢測

          臟值檢測:當(dāng)數(shù)據(jù)改變時更新視圖(DOM)

          如何進行檢測:檢測兩個狀態(tài)值(當(dāng)前狀態(tài)和新狀態(tài))

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

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

          可以通過在@Component元數(shù)據(jù)中設(shè)置changeDetection: ChangeDetectionStrategy.OnPush進行切換

          Default

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

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

          OnPush

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

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

          父子組件通訊

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

          父組件給子組件傳值 @input

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

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

          **子組件觸發(fā)父組件的方法 @Output **

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

          父組件通過 ViewChild 主動調(diào)用子組件DOM和方法

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

          投影組件

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

          由于組件過度嵌套會導(dǎo)致數(shù)據(jù)冗余和事件傳遞,因此引入投影組件的概念

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

          主要用于組件動態(tài)內(nèi)容的渲染,而這些內(nèi)容沒有復(fù)雜的業(yè)務(wù)邏輯,也不需要重用,只是一小部分 HTML 片段

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

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

          // 表現(xiàn)形式: <ng-content select="樣式類/HTML標(biāo)簽/指令"></ng-content>  <ng-content select="[appGridItem]"></ng-content>
          登錄后復(fù)制

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

          Angular指令

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

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

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

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

          內(nèi)置屬性型指令

          NgClass

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

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

          NgStyle

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

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

          NgModel

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

          內(nèi)置結(jié)構(gòu)型指令

          ngIf

          ngIf 根據(jù)表達式是否成立,決定是否展示 DOM 標(biāo)簽

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

          ngIf else

          <div *ngIf="show else ElseContent">這是 ngIF 內(nèi)容</div> <ng-template #ElseContent>   <h2>這是 else 內(nèi)容</h2> </ng-template>  // 結(jié)構(gòu)性指令都依賴于 ng-template,*ngIf 實際上就是 ng-template 指令的 [ngIf] 屬性。
          登錄后復(fù)制

          ngFor

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

          ngSwitch

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

          指令事件樣式綁定

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

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

          @HostListener 綁定宿主的事件

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

          Angular生命周期

          生命周期函數(shù)通俗的講就是組件創(chuàng)建、組件更新、組件銷毀的時候會觸發(fā)的一系列的方法

          當(dāng) Angular 使用構(gòu)造函數(shù)新建一個組件或指令后,就會按下面規(guī)定的順序在特定時刻調(diào)用生命周期鉤子

          • constructor :構(gòu)造函數(shù)永遠首先被調(diào)用,一般用于變量初始化以及類實例化

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

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

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

            • ngAfterContentInit :內(nèi)容投影ng-content完成時調(diào)用,只在第一次 ngDoCheck 之后調(diào)用

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

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

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

          • ngOnDestroy 當(dāng)組件銷毀時調(diào)用,可以反訂閱可觀察對象和分離事件處理器,以防內(nèi)存泄漏

          Angular路由

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

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

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

          路由基本配置

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

          激活路由

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

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

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

          控制路由激活狀態(tài)的樣式 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; }
          登錄后復(fù)制

          路由參數(shù)

          路徑參數(shù)讀取

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

          查詢參數(shù)讀取

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

          路由傳遞一個參數(shù)及其接收方法:

          傳遞參數(shù):path:’info/:id’

          接收參數(shù):

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

          路由傳遞多個參數(shù)及其接收方法:

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

          接收參數(shù):

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

          路由懶加載

          懶加載子模塊,子模塊需要配置路由設(shè)置啟動子模塊 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'} ];
          登錄后復(fù)制

          Angular服務(wù)

          組件不應(yīng)該直接獲取或保存數(shù)據(jù),應(yīng)該聚焦于展示數(shù)據(jù),而把數(shù)據(jù)訪問的職責(zé)委托給某個服務(wù)

          獲取數(shù)據(jù)和視圖展示應(yīng)該相分離,獲取數(shù)據(jù)的方法應(yīng)該放在服務(wù)中

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

          組件之間相互調(diào)用各組件里定義的方法

          多個組件都用的方法(例如數(shù)據(jù)緩存的方法)放在服務(wù)(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)   } }
          登錄后復(fù)制

          @Injectable()裝飾器

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

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

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

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

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

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

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

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

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

          在組件中提供服務(wù)時,還可以使用 viewProdivers,viewProviders 對子組件樹不可見

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

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

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

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

          依賴注入

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

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

          Angular 通過 service共享狀態(tài),而這些管理狀態(tài)和數(shù)據(jù)的服務(wù)便是通過依賴注入的方式進行處理的

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

          歸根到底,很多時候我們創(chuàng)建服務(wù),是為了維護公用的狀態(tài)和數(shù)據(jù),通過依賴注入的方式來規(guī)定哪些組件可共享

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

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

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

          深入了解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() {     // 構(gòu)造一個 injector 用 create 方法 里面 providers 數(shù)組中寫我們需要構(gòu)造的東西     const injector = Injector.create({       providers: [         {           provide: Product,           // 構(gòu)造 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));   }
          登錄后復(fù)制

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

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

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

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

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

          Angular 管道

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

          管道將數(shù)據(jù)作為輸入并將其轉(zhuǎn)換為所需的輸出

          管道其實就是過濾器,用來轉(zhuǎn)換數(shù)據(jù)然后顯示給用戶

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

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

          Angular 應(yīng)用程序中可以使用許多類型的管道

          內(nèi)置管道

          • String -> String
            • UpperCasePipe 轉(zhuǎn)換成大寫字符
            • LowerCasePipe 轉(zhuǎn)換成小寫字符
            • TitleCasePipe 轉(zhuǎn)換成標(biāo)題形式,第一個字母大寫,其余小寫
          • Number -> String
            • DecimalPipe 根據(jù)數(shù)字選項和區(qū)域設(shè)置規(guī)則格式化值
            • PercentPipe 將數(shù)字轉(zhuǎn)換為百分比字符串
            • CurrencyPipe 改變?nèi)嗣麕鸥袷?/li>
          • Object -> String
            • JsonPipe 對象序列化
            • DatePipe 日期格式轉(zhuǎn)換
          • Tools
            • SlicePipe 字符串截取
            • AsyncPipe 從異步回執(zhí)中解出一個值
            • I18nPluralPipe 復(fù)數(shù)化
            • I18nSelectPipe 顯示與當(dāng)前值匹配的字符串

          使用方法

          <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" } -->  <!--  	管道可以接收任意數(shù)量的參數(shù),使用方式是在管道名稱后面添加: 和參數(shù)值 	若需要傳遞多個參數(shù)則參數(shù)之間用冒號隔開  -->  <!-- 可以將多個管道連接在一起,組成管道鏈對數(shù)據(jù)進行處理 --> <div>{{ 'ccc' | slice:0:1 | uppercase }}</div>
          登錄后復(fù)制

          自定義管道

          管道本質(zhì)上就是個類,在這個類里面去實現(xiàn) PipeTransfrom 接口的 transform 這個方法

          • 使用 @Pipe 裝飾器定義 Pipemetadata 信息,如 Pipe 的名稱 – 即 name 屬性
          • 實現(xiàn) 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中 | 前面?zhèn)魅氲闹担?args為名稱后傳入的參數(shù)         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>  // 瀏覽器輸出 女  // 管道可以鏈?zhǔn)绞褂?,還可以傳參 <span> {{date | date: 'fullDate' | uppercase}} </span> // 每一個自定義管道都需要實現(xiàn) PipeTransform 接口,這個接口非常簡單,只需要實現(xiàn) transform 方法即可。 // transform()方法參數(shù)格式 - transform(value: string, args1: any, args2?: any):  // value為傳入的值(即為需要用此管道處理的值, | 前面的值);  // args 為傳入的參數(shù)(?:代表可選); // html 中使用管道格式 - {{ 數(shù)據(jù) | 管道名 : 參數(shù)1 : 參數(shù)2 }} // 與 component 一樣,pipe 需要先在 declarations 數(shù)組中聲明后使用
          登錄后復(fù)制

          Angular操作DOM

          原生JS操作

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

          ElementRef

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

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

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

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

          當(dāng)需要直接訪問 DOM 時,請把本 API 作為最后選擇 。優(yōu)先使用 Angular 提供的模板和數(shù)據(jù)綁定機制

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

          ViewChild

          使用模板和數(shù)據(jù)綁定機制,使用 @viewChild

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

          父組件中可以通過 ViewChild 調(diào)用子組件的方法

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

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

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

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

          Renderer2

          Renderer2 是 Angular 提供的操作 element 的抽象類,使用該類提供的方法,能夠?qū)崿F(xiàn)在不直接接觸 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');     } }
          登錄后復(fù)制

          • createElement /appendChild/createText 創(chuàng)建 DIV 元素,插入文本內(nèi)容,并將其掛載到宿主元素上

          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); }
          登錄后復(fù)制

          • 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'); }
          登錄后復(fù)制

          • 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'     ); }
          登錄后復(fù)制

          移除 inline-style :

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

          • setProperty 設(shè)置宿主元素的 property 的值

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

          直接操作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');   } }
          登錄后復(fù)制

          Angular網(wǎng)絡(luò)請求

          HttpClient

          需導(dǎo)入 HttpClientModule ,只在根模塊中導(dǎo)入,并且整個應(yīng)用只需導(dǎo)入一次,不用在其他模塊導(dǎo)入

          在構(gòu)造函數(shù)中注入HttpClientget/post方法對應(yīng)HTTP方法,這些方法是泛型的,可以直接把返回的JSON轉(zhuǎn)換成對應(yīng)類型。若是不規(guī)范的請求,使用request方法

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

          get 請求數(shù)據(jù)

          // 在 app.module.ts 中引入 HttpClientModule 并注入 import {HttpClientModule} from '@angular/common/http'; imports: [   BrowserModule,   HttpClientModule ]  // 在用到的地方引入 HttpClient 并在構(gòu)造函數(shù)聲明 import {HttpClient} from "@angular/common/http"; constructor(private http: HttpClient,private cd: ChangeDetectorRef) { } // 依賴注入  // get 請求數(shù)據(jù) 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 這里需要進行臟值檢測 });
          登錄后復(fù)制

          post 提交數(shù)據(jù)

          // 在 app.module.ts 中引入 HttpClientModule 并注入 import {HttpClientModule} from '@angular/common/http'; imports: [    BrowserModule,    HttpClientModule ]  // 在用到的地方引入 HttpClient 、HttpHeaders 并在構(gòu)造函數(shù)聲明 HttpClient import {HttpClient,HttpHeaders} from "@angular/common/http"; constructor(private http:HttpClient) { } // 實例化  // post 提交數(shù)據(jù) 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); });
          登錄后復(fù)制

          Jsonp請求數(shù)據(jù)

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

          攔截器

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

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

          創(chuàng)建攔截器

          // 使用命令 ng g interceptor name,在這里創(chuàng)建攔截器 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() 方法只是單純的將請求轉(zhuǎn)發(fā)給下一個攔截器(如果有),并最終返回 HTTP 響應(yīng)體的 Observable   // request: HttpRequest<unknown> 表示請求對象,包含了請求相關(guān)的所有信息,unknown指定請求體body的類型   // next: HttpHandler 請求對象修改完成,將修改后的請求對象通過next中的handle方法傳回真正發(fā)送請求的方法中   intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> 	{     // next 對象表示攔截器鏈表中的下一個攔截器(在應(yīng)用中可以設(shè)置多個攔截器)     return next.handle(request);   } }
          登錄后復(fù)制

          注入攔截器

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

          請求頭攔截

          @Injectable()export class LanJieQiInterceptor implements HttpInterceptor {   constructor() {}   intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> 	{     // 為了統(tǒng)一設(shè)置請求頭,需要修改請求     // 但 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)// 將修改后的請求返回給應(yīng)用   }}
          登錄后復(fù)制

          響應(yīng)捕獲

          @Injectable() export class LanJieQiInterceptor implements HttpInterceptor {   constructor() {}   intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> 	{     // 為了統(tǒng)一設(shè)置請求頭,需要修改請求     // 但 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)// 將修改后的請求返回給應(yīng)用   } }
          登錄后復(fù)制

          如果有多個攔截器,請求順序是按照配置順序執(zhí)行,響應(yīng)攔截則是相反的順序

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

          Angular表單

          模版驅(qū)動表單

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

          如果有非?;镜谋韱涡枨蠛秃唵蔚侥苡媚0骞芾淼倪壿?,就使用模板驅(qū)動表單

          響應(yīng)式表單和模板驅(qū)動表單共享了一些底層構(gòu)造塊:

          FormControl 實例用于追蹤單個表單控件的值和驗證狀態(tài)

          FormGroup 用于追蹤一個表單控件組的值和狀態(tài)

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

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

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

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

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

          當(dāng)我們試圖從 FormGroup 中獲取 value 時,會收到一個 “鍵值對” 結(jié)構(gòu)的對象

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

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

          首先加載 FormsModule

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

          接下來創(chuàng)建一個模版表單

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

          我們導(dǎo)入了 FormsModule,因此可以在視圖中使用 NgForm

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

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

          這意味著當(dāng)導(dǎo)入 FormsModule 時候,NgForm 就會被自動附加到視圖中所有的標(biāo)簽上

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

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

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

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

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

          也就是說,它會在視圖中的 input 標(biāo)簽和 FormControl 對象之間建立關(guān)聯(lián)

          這種關(guān)聯(lián)是通過 name 屬性建立的,在本例中是 "name"

          響應(yīng)式表單

          使用 ngForm 構(gòu)建 FormControlFormGroup 很方便,但是無法提供定制化選項,因此引入響應(yīng)式表單

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

          使用響應(yīng)式表單時,通過編寫 TypeScript 代碼而不是 HTML 代碼來創(chuàng)建一個底層的數(shù)據(jù)模型

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

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

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

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

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

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

          記住以下兩點:

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

          表單驗證

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

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

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

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

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

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

          在視圖中檢查驗證器的狀態(tài),并據(jù)此采取行動

          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">             數(shù)據(jù)已變動           </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')"           >             只可輸入數(shù)字和英文           </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);   } }
          登錄后復(fù)制

          內(nèi)置校驗器

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

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

          自定義驗證器

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

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

          // angular 源代碼中實現(xiàn) Validators.required  export class Validators {   // 接收一個 AbstractControl 對象作為輸入 	static required(control: AbstractControl): ValidationErrors | null; } // 當(dāng)驗證器失敗時,會返回一個 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 };   } }
          登錄后復(fù)制

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

          Validators.compose 來實現(xiàn)

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

          只有當(dāng)兩個驗證器都合法時,FormControl 才是合法的

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

          動態(tài)表單

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

          export class ReativeFormsComponent implements OnInit {   ngOnInit() {     this.addContact()   }   //動態(tài)表單   personMess: FormGroup = new FormGroup({     //生成動態(tài)表單數(shù)組     contacts: new FormArray([])    })   //獲取數(shù)組對象   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)   } }
          登錄后復(fù)制

          <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>
          登錄后復(fù)制

          Angular CDK

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

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

          贊(0)
          分享到: 更多 (0)
          網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號