一、Overview
angular 入坑記錄的筆記第二篇,介紹組件中的相關概念,以及如何在 angular 中通過使用組件來完成系統功能的實作
對應官方檔案地址:
- 顯示資料
- 模板語法
- 用戶輸入
- 組件之間的互動
- 管道
- 生命周期鉤子
配套代碼地址:angular-practice/src/components-guide
二、Contents
- Angular 從入坑到棄坑 - Angular 使用入門
- Angular 從入坑到挖坑 - 組件食用指南
三、Knowledge Graph

四、Step by Step
4.1、組件與模板
4.1.1、組件的基礎概念
組件包含了一組特定的功能,每個組件的功能都單一且獨立,可以進行重復使用;組件可以通過 angular cli 進行創建,生成的組件位于作業空間的 src/app/ 路徑下面
## 創建一個 product-list 組件
ng g component product-list
當需要將組件放置在某個指定的目錄下時,可以直接在 ng g 命令中添加路徑
## 將 hero 組件生成到 components 路徑下
ng g component components/hero

angular 應用就是通過一個個的組件所構成的組件樹,一個組件包含了如下的四個部分
- product-list.component.ts:組件類,用來處理資料和功能,為視圖呈現提供支持
- product-list.component.html:組件對應的頁面 HTML 模板,用來呈現組件的功能
- product-list.component.scss:只針對當前組件的樣式
- product-list.component.spec.ts:當前組件的單元測驗檔案(非必須)
當通過命令列創建一個新的組件之后,會自動將新創建的組件注冊到應用的根模塊(app.module.ts)中

在組件類中,通過使用 @Component 裝飾器 [1] 用來將類宣告為組件類,并為這個組件類配置一些元資料 [2],以決定該組件在運行期間該如何處理、實體化和使用
裝飾器中存在三個基礎的配置引數,用來完成組件與視圖之間的關聯
- selector:選擇器,當我們在頁面上添加了這個選擇器指定的標簽(
<app-product-list></app-product-list>)后,就會在當前使用位置上創建并插入這個組件的一個實體 - templateUrl:該組件所對應的 HTML 模板檔案地址
- styleUrls:該組件視圖所特有的 css 樣式檔案地址

當需要使用這個組件時,直接在頁面上添加選擇器對應的標簽就可以了

4.1.2、模板系結語法
在 angular 應用中,組件扮演著控制器或是視圖模型的作用,在創建組件時會關聯一個 html 檔案,這個 html 檔案則是一個基礎的 angular 模板檔案
在這個模板檔案中,可以通過 angular 內置的模板語法與 html 元素進行結合,從而告訴 angular 如何根據我們的應用邏輯和資料來渲染頁面
4.1.2.1、插值運算式
插值運算式可以將組件中的屬性值或者是模板上的資料通過模板運算式運算子進行計算,最終將值渲染到視圖頁面上
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.scss']
})
export class ProductListComponent implements OnInit {
public title = '我是 title 屬性值';
constructor() { }
ngOnInit(): void {
}
}
<p>title:{{title}}</p>
<p>1+2+3+4+5={{1+2+3+4+5}}</p>

模板運算式的變數來源
- 模板本身的變數
- 指令的背景關系變數
- 組件的成員資訊(屬性 or 方法)
在使用模板運算式時,如果變數名在多個來源中都存在的話,則模板變數是最優先的,其次是指令的背景關系變數,最后是組件的成員
在使用模板運算式時應該遵循如下的原則
- 簡單:正常情況下,應該將業務邏輯或是資料運算放到組件中,模板運算式只作為屬性或方法的呼叫
- 快速執行:模板運算式得出的資料應該快速結束,否則就會對于用戶體驗造成影響
- 沒有可見的副作用:模板運算式只作為資料的展示,不應該改變任何的資料;應該構建出冪等的運算式,除非依賴的值發生變化,否則多次呼叫時,應該回傳相同的資料資訊
4.1.2.2、模板系結語法
通過資料系結機制,將資料源與視圖進行系結,從而實作源資料與用戶呈現的一致性
- 從資料源到視圖:插值、組件中的屬性、dom 元素的 property [3]、css 樣式、css 類
- 從視圖到資料源:事件
- 視圖與資料源之間的雙向系結:資料物件
| 分類 | 語法 |
|---|---|
| 單向 從資料源到視圖 |
1、插值運算式:{{expression}} 2、使用 [] 進行系結: <a [href]='expression'></a>3、使用 bind 進行系結: <a bind-href='https://www.cnblogs.com/danvic712/p/expression'></a> |
| 單向 從視圖到資料源 |
1、使用 () 進行系結:<a (click)='statement'></a>2、使用 on 進行系結: <a on-click='statement'></a> |
| 雙向 視圖到資料源;資料源到視圖 |
1、使用 [()] 進行系結:<input type="text" [(ngModel)]="product.Name">2、使用 bindon 進行系結: <input type="text" bindon-ngModel="product.Name"> |
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.scss']
})
export class ProductListComponent implements OnInit {
public title = '我是 title 屬性值';
public styleProperty = '<b>我是包含 html 標簽的屬性</b>';
public fontColor = 'red';
public url = 'https://yuiter.com';
public name: string;
constructor() { }
ngOnInit(): void {
}
getUser() {
alert('111111111');
}
}
<h3>2.1、從資料源到視圖</h3>
<p>
<a href='https://www.cnblogs.com/danvic712/p/{{url}}'>使用插值運算式進行系結</a>
</p>
<p>
<a [href]='url' [style.color]='fontColor'>使用 [] 進行系結</a>
</p>
<p>
<a bind-href='https://www.cnblogs.com/danvic712/p/url'>使用 bind 進行系結</a>
</p>
<p>
<span [innerHtml]="styleProperty"></span>
</p>
<h3>2.2、從視圖到資料源</h3>
<p>
<button (click)="getUser()">使用 () 進行系結</button>
</p>
<p>
<button on-click="getUser()">使用 on 進行系結</button>
</p>
<h3>2.3、資料雙向系結 --- 需要在 AppModule 中添加對于 FormsModule 的參考</h3>
<p>
<input type="text" id="userName" [(ngModel)]="name">
</p>
<p>
<input type="text" bindon-ngModel="name">
</p>

4.1.3、資料系結
-
單向資料系結
<p>{{title}}</p> -
雙向資料系結
<input type="text" id="userName" [(ngModel)]="name"> <!-- 當沒有 NgModel 時,雙向資料系結等同于下面的寫法 --> <input type="text" id="userName" [value]="name" (input)="name=$event.target.value">
4.1.4、屬性、樣式系結
-
dom 元素的 property 系結
<img [src]="productImageUrl"> <img bind-src="https://www.cnblogs.com/danvic712/p/productImageUrl"> -
html 標簽的 attribute 系結
attribute 系結的語法類似于 property 系結,由前綴
attr、點(.)和 attribute 名稱組成attribute 系結的主要用例之一是設定 ARIA attribute(給殘障人士提供便利)
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button> -
style 行內樣式系結
// 1、[style.width]="width" :string | undefined | null public width = "100px"; //2、[style.width.px]="width":number | undefined | null public width = "20"; // 3、[style]="styleExpr":string public styleExpr = "width: 100px; color:red"; // 4、[style]="styleExpr":{key:value} public styleExpr = {width: '100px', height: '100px'}; // 5、[style]="styleExpr":array public styleExpr = ["width", "100px"]; -
class 屬性系結
// 1、[class.foo]="hasFoo":bool | undefined | null public hasFoo = true; // 2、[class]="classExpr":string public classExpr = "my-class1 my-class2"; // 3、[class]="classExpr":{key:value} public classExpr= {my-class1: true, my-class2: true}; // 4、[class]="classExpr":array public classExpr= ["my-class1", "my-class2"];
4.1.5、事件系結
在事件系結中,可以通過 $event 引數獲取到 dom 事件物件的屬性從而獲取到模板資訊
<input type="text" (keyup)="getMsg($event)">
<p>輸入的值:{{msg}}</p>
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.scss']
})
export class ProductListComponent implements OnInit {
public msg: string;
constructor() { }
ngOnInit(): void {
}
getMsg(event: KeyboardEvent) {
console.log(event);
this.msg = (event.target as HTMLInputElement).value;
}
}

通過使用 $event 作為方法的引數會將許多用不到的模板資訊傳遞到組件中,導致我們在僅僅是為了獲取資料的前提下,卻需要對于頁面元素十分了解,違背了模板(用戶所能看到的)與組件(應用如何去處理用戶資料)之間的關注點分類的原則,因此,這里應該使用模板參考變數的方式獲取資料資訊,
模板參考變數是對模板中 DOM 元素的參考,提供了從模塊中直接訪問元素的能力,
<input type="text" #refMsgInput (keyup)="getRefMsg(refMsgInput.value)">
<p>通過模板引入變數的方式獲取到輸入的值:{{refMsg}}</p>
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.scss']
})
export class ProductListComponent implements OnInit {
public refMsg: string;
constructor() { }
ngOnInit(): void {
}
getRefMes(msg: string) {
this.refMsg = msg;
}
}
模板參考變數的作用域是整個模板,因此要確保一個模板中的參考變數名稱是唯一的,同時,在宣告參考變數時,也可以使用 ref- 代替 #
<input type="text" ref-refMsgInput (keyup)="getRefMsg(refMsgInput.value)">
<p>通過模板引入變數的方式獲取到輸入的值:{{refMsg}}</p>
4.2、指令
4.2.1、屬性型指令
屬性型指令被應用在視圖 dom 元素上,用來改變 dom 元素的外觀或行為
-
NgClass:用來設定元素的多個 css 類屬性,如果只設定一個 css 類,應該使用模板系結語法中 class 類系結
<p [ngClass]="inlineStyle">NgClass 屬性指令</p>import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-product-list', templateUrl: './product-list.component.html', styleUrls: ['./product-list.component.scss'] }) export class ProductListComponent implements OnInit { public inlineStyle: {}; constructor() { } ngOnInit(): void { this.setInlineStyle(); } setInlineStyle() { this.inlineStyle = { 'text-red': true, 'bg-blue': false, }; } }這里的 text-red、bg-blue 都是 css 類名,如果想要在指定的元素上添加該類,則 css 類名對應的值為 true,反之則為 false
-
NgStyle:用來設定元素的多個行內樣式,如果只設定一個行內樣式,應該使用模板系結語法中的樣式系結
<p [ngStyle]="currentStyles">NgStyle 屬性指令</p>import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-product-list', templateUrl: './product-list.component.html', styleUrls: ['./product-list.component.scss'] }) export class ProductListComponent implements OnInit { public currentStyles: {}; constructor() { } ngOnInit(): void { this.setCurrentStyles(); } setCurrentStyles() { this.currentStyles = { 'font-style': 'italic', 'font-weight': 'bold', 'font-size': '24px' }; } }通過在組件的屬性中設定多個行內樣式物件的形式,完成對于頁面元素樣式的批量設定
-
NgModel:雙向資料系結
<input type="text" id="userName" [(ngModel)]="name">
4.2.2、結構型指令
結構型指令用來操作 dom 樹,通過進行一些的邏輯判斷,從而完成對于頁面布局的修改
-
NgIf:根據運算式的值(true or false)來創建或者銷毀 dom 元素
<p *ngIf="expr">NgIf 結構型指令</p>當 expr 屬性為 true 時,這個元素則會顯示在頁面上,當屬性值為 false 時,則不顯示該元素
ngIf 指令并不是通過使用 css 樣式來隱藏元素的,當值為 false 時,則這些元素會從 dom 中被銷毀,并且所有監聽該 dom 元素的事件會被取消,當重新顯示該元素時,會重新執行初始化的程序
與銷毀元素不同,對于隱藏的元素來說,所有的元素監聽事件還會執行監聽的,再次顯示時不用重新進行初始化程序
-
NgFor:通過定義單條資料的顯示格式,angular 以此為模板,回圈渲染出所有的資料
<p *ngFor="let item of products; let i = index">{{i+1}} - {{item.name}} --- {{item.price}}</p>import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-product-list', templateUrl: './product-list.component.html', styleUrls: ['./product-list.component.scss'] }) export class ProductListComponent implements OnInit { public products = [{ 'name': 'lalala', 'price': '$200' }, { 'name': 'hehehe', 'price': '$400' }, { 'name': 'wuwuwu', 'price': '$120' }, { 'name': 'xixi', 'price': '$570' }]; constructor() { } ngOnInit(): void { } }NgFor 指令背景關系中的 index 屬性在每次迭代中,會獲取到條資料的索引值
當渲染的資料發生改變時 [4],會導致 dom 元素的重新渲染,此時可以采用 trackBy 的方式,通過在組件中添加一個方法,指定回圈需要跟蹤的屬性值,此時當渲染的資料發生改變時,只會重新渲染變更了指定的屬性值的資料
<p>不使用 trackBy 跟蹤屬性</p> <div> <p *ngFor="let item of products; let i = index;"> {{i+1}} - {{item.name}} --- {{item.price}} </p> </div> <p>使用 trackBy 跟蹤屬性</p> <div> <p *ngFor="let item of products; let i = index; trackBy: trackByIndex"> {{i+1}} - {{item.name}} --- {{item.price}} </p> </div> <button (click)="addProduct()">新增</button>import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-product-list', templateUrl: './product-list.component.html', styleUrls: ['./product-list.component.scss'] }) export class ProductListComponent implements OnInit { public products = [{ 'name': 'lalala', 'price': '$200' }, { 'name': 'hehehe', 'price': '$400' }, { 'name': 'wuwuwu', 'price': '$120' }, { 'name': 'xixi', 'price': '$570' }]; constructor() { } ngOnInit(): void { } trackByIndex(index: number, item: any): string { return item.price; } addProduct() { this.products = [{ 'name': 'lalala', 'price': '$200' }, { 'name': 'hehehe', 'price': '$400' }, { 'name': 'wuwuwu', 'price': '$120' }, { 'name': 'xixi', 'price': '$570' }, { 'name': 'lululu', 'price': '$' + (Math.random() * 100).toFixed() }]; } }
-
NgSwitch:根據條件切換,從候選的幾個元素中選擇匹配的,放到 dom 元素中
<p> 請選擇配置 <select [(ngModel)]="config"> <option value="">請選擇</option> <option value="https://www.cnblogs.com/danvic712/p/r7-3700x">AMD Ryzen 7 3700X</option> <option value="https://www.cnblogs.com/danvic712/p/i5-9400f">Intel i5 9400F</option> <option value="https://www.cnblogs.com/danvic712/p/i5-9600kf">Intel i5 9600KF</option> </select> </p> <p> 配置描述 </p> <div [ngSwitch]="config"> <p *ngSwitchCase="'r7-3700x'"> 一個能打得都木的~~~ </p> <p *ngSwitchCase="'i5-9400f'"> 擠牙膏的,,, </p> <p *ngSwitchCase="'i5-9600kf'"> 別看了,我不是開封菜,,, </p> <p *ngSwitchDefault> 你選一個啊~~~ </p> </div>import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-product-list', templateUrl: './product-list.component.html', styleUrls: ['./product-list.component.scss'] }) export class ProductListComponent implements OnInit { public config = ''; constructor() { } ngOnInit(): void { } }
NgSwitch 本身是一個屬性型指令,它不會直接操作 dom 元素,而是通過它所控制的兩個結構型指令(NgSwitchCase、ngSwitchDefault)來操作 dom 元素
4.3、管道
在使用模板運算式系結資料時,可以使用管道對于運算式的結果進行轉換
管道是一種簡單的函式,它們接受輸入值并回傳轉換后的值,通過在模板運算式中使用管道運算子(|)則可以完成相應的結果轉換
4.3.1、模板運算式中的特殊運算子
angular 模板運算式是 javascript 的子集,相對于常見的 javascript 運算子,添加了三個特殊的運算子
-
管道運算子
管道是一種特殊的函式,可以把運算子(|)左邊的資料轉換成期望呈現給視圖的資料格式,例如,將時間進行格式化、將資料轉換成 json 字串的形式等等
可以針對一個資料使用多個管道進行串聯,并且管道運算子的優先級比三元運算子( ?: )高
<h3>5.1、管道運算子</h3> <div> <p>產品資訊 json 字串</p> {{products | json}} </div>
-
安全導航運算子
在視圖中使用的屬性值為 null or undefined 時,javascript 和 angular 會引發空指標例外并中斷視圖的渲染程序, 從而視圖會渲染失敗,而使用了安全導航運算子(?)后,視圖依然會渲染,只是顯示的值為空白
<h3>5.2、安全導航運算子</h3> <p>第五個專案的名稱為:{{products[5].name}}</p>
<p>第五個專案的名稱為:{{products[5]?.name}}</p>
-
非空斷言運算子
在 tsconfig.json 中啟用 strictNullChecks 屬性,typescript 將會強制開啟嚴格的空值檢查,在這種模式下,所有定義了型別的屬性是不允許賦值為 null 的,當將屬性賦值為 null,則會編譯報錯

非空斷言運算子用來告訴編譯器對特定的屬性不做嚴格的空值校驗,當屬性值為 null or undefined 時,不拋錯誤,在下面的代碼中,在判斷 obj 存在后,就不再針對 obj.name 進行校驗
import { Component, OnInit } from '@angular/core'; interface Person { name: string; age: number; } @Component({ selector: 'app-product-list', templateUrl: './product-list.component.html', styleUrls: ['./product-list.component.scss'] }) export class ProductListComponent implements OnInit { public obj: Person; constructor() { } ngOnInit(): void { } }<p *ngIf="obj"> <span>{{obj!.name}}</span> </p>非空斷言運算子不會防止出現 null 或 undefined,只是不提示
4.3.2、常用的管道函式
-
純管道
只有在它檢測到輸入值發生了純變更時才會執行,但是會忽略物件內部的變更
純變更是指對原始型別值(String、Number、Boolean、Symbol)的更改, 或者對物件參考(Date、Array、Function、Object)的更改
-
非純管道
每個組件的變更周期都會執行
| 管道 | 作用 |
|---|---|
| JsonPipe | 將一個值轉換成 json 字串 |
| DatePipe | 根據區域設定規則格式化日期值 |
| UpperCasePipe | 把文本轉換成全大寫形式 |
| LowerCasePipe | 把文本轉換成全小寫形式 |
<h3>6.1、json 管道</h3>
<p>{{products | json}}</p>
<h3>6.2、date 管道</h3>
<p>現在時間:{{date | date:'yyyy-MM-dd HH:mm:ss'}}</p>
<h3>6.3、upper 管道</h3>
<p>轉換成全大寫:{{url | uppercase}}</p>
<h3>6.4、lower 管道</h3>
<p>轉換成全小寫:{{url | lowercase}}</p>

4.4、組件之間的通信
4.4.1、輸入屬性與輸出屬性
輸入屬性(@Input)和輸出屬性(@Output)用來在父子組件或指令中進行共享資料,@Input 用來獲取資料,@Output 用來向外發送資料
4.4.2、子組件獲取父組件資訊
-
在父組件中,添加對于子組件的參考,并將需要傳遞的資料 or 方法系結到子組件上
傳遞資料直接將父組件中的屬性值賦值給系結在子組件上的屬性就可以了
傳遞方法時,系結在子組件上的屬性是父組件方法的名稱,此處不能加 () ,否則就會直接執行該父組件的方法
在傳遞資料給子組件時,也可以通過 this 來指代父組件,從而將整個父組件作為資料系結子組件上
<h2>父組件內容:</h2> <p> <label for="title">標題:</label> <input id="title" type="text" [(ngModel)]="title"> </p> <hr> <h2>子組件內容:</h2> <!-- 將父組件的資料系結到子組件上 --> <app-child-component [parentTitle]="title" [parentGetMsg]='getMsg'></app-child-component>
-
在子組件中引入 Inupt,同時使用 @Input 裝飾器來接收父組件傳遞的資料
// 引入 Input 介面 import { Component, OnInit, Input } from '@angular/core'; @Component({ selector: 'app-child-component', templateUrl: './child-component.component.html', styleUrls: ['./child-component.component.scss'] }) export class ChildComponentComponent implements OnInit { // 獲取父組件的資料 @Input() parentGetMsg: any; // 使用 setter 對父組件的資料進行深加工 private _title: string; @Input() set parentTitle(title: string) { this._title = (title && title.trim()) || '父組件的 title 屬性值為空'; } get parentTitle(): string { return this._title; } constructor() { } ngOnInit(): void { } runParentFunc() { this.parentGetMsg(); } }<p>父組件的 title 屬性值:{{parentTitle}}</p> <p> <button (click)="runParentFunc()">呼叫父組件的方法</button> </p>對于使用 @Input 裝飾器獲取到的父組件資料,可以通過輸入屬性中的 setter 方法中進行重新賦值

4.4.3、父組件獲取子組件資訊
-
使用 @ViewChild 裝飾器獲取
在子組件上定義一個模板參考變數
<h2>父組件內容:</h2> <h3>1、使用 @ViewChild 裝飾器獲取子組件資料</h3> <p> <button (click)="getChildMsg()">獲取子組件的 msg 資料</button> </p> <p> <button (click)="runChildFunc()">呼叫子組件的方法</button> </p> <hr> <h2>子組件內容:</h2> <!-- 在子組件上定義一個模板參考變數 --> <app-child-component #childComponent></app-child-component>在父組件中添加對于 ViewChild 的參考,然后使用 @ViewChild 裝飾器來接收子組件的 dom 資訊,從而獲取到子組件的資料或方法
// 引入 ViewChild import { Component, OnInit, ViewChild } from '@angular/core'; @Component({ selector: 'app-parent-component', templateUrl: './parent-component.component.html', styleUrls: ['./parent-component.component.scss'] }) export class ParentComponentComponent implements OnInit { // 通過 @ViewChild 裝飾器來接收字組件的 dom 資訊 @ViewChild('childComponent') child: any; constructor() { } ngOnInit(): void { } getMsg() { alert('我是父組件的 getMsg 方法'); } getChildMsg() { alert(this.child.msg); } }
-
使用 @Output 裝飾器配合 EventEmitter 實作
在子組件中引入 Output 和 EventEmitter,通過 @Output 裝飾器定義一個事件觸發器,然后就可以通過這個事件觸發器的 emit 方法進行事件廣播
// 引入 Output、EventEmitter import { Component, OnInit, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-child-component', templateUrl: './child-component.component.html', styleUrls: ['./child-component.component.scss'] }) export class ChildComponentComponent implements OnInit { public msg = 'child title'; // 定義一個事件觸發器 @Output() childEmitter = new EventEmitter<string>(); constructor() { } ngOnInit(): void { } runParentFunc() { this.parentGetMsg(); } sendMsg() { this.childEmitter.emit(this.msg); } }當子組件進行事件廣播時,就可以通過在子組件上使用事件系結的方式系結到一個父組件事件,通過 $event 獲取到子組件傳遞的資料值
<h2>父組件內容:</h2> <h3>2、使用 @Output 裝飾器配合 EventEmitter 獲取子組件資料</h3> <p>{{childMsg}}</p> <hr> <h2>子組件內容:</h2> <!-- 將子組件的事件廣播系結到父組件事件上 --> <app-child-component (childEmitter)='childEmitMsg($event)'></app-child-component>import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-parent-component', templateUrl: './parent-component.component.html', styleUrls: ['./parent-component.component.scss'] }) export class ParentComponentComponent implements OnInit { public childMsg: string; constructor() { } ngOnInit(): void { } childEmitMsg(event) { this.childMsg = event; } }
4.4.4、非父子組件之間的通信
不管組件之間是否具有關聯關系,都可以通過共享一個服務的方式來進行資料互動,也可以將需要進行共享的資料存盤到一些存盤介質中,通過直接讀取這個存盤介質中的資料進行通信
-
創建一個服務,并添加到模塊中
## 在 services/storage 路徑下創建一個 storage 服務 ng g service services/storage/storageimport { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { ProductListComponent } from './product-list/product-list.component'; import { FormsModule } from '@angular/forms'; import { ParentComponentComponent } from './parent-component/parent-component.component'; import { ChildComponentComponent } from './child-component/child-component.component'; // 引入自定義的服務 import { StorageService } from './services/storage/storage.service'; @NgModule({ declarations: [ AppComponent, ProductListComponent, ParentComponentComponent, ChildComponentComponent ], imports: [ BrowserModule, AppRoutingModule, FormsModule ], // 配置自定義的服務 providers: [StorageService], bootstrap: [AppComponent] }) export class AppModule { }
-
在組件中使用服務
在需要使用的組件中引入服務,然后在組件的建構式中通過依賴注入的方式注入這個服務,就可以在組件中完成對于這個服務的使用
在父組件中對資料進行賦值,然后呼叫服務的方法改變資料資訊
import { Component, OnInit } from '@angular/core'; // 引入服務 import { StorageService } from '../services/storage/storage.service'; @Component({ selector: 'app-parent-component', templateUrl: './parent-component.component.html', styleUrls: ['./parent-component.component.scss'] }) export class ParentComponentComponent implements OnInit { public msg = 'this is a service default value writen in parent component'; constructor(private storage: StorageService) { this.storage.setMsg(this.msg); } ngOnInit(): void { } submit() { this.storage.setMsg(this.msg); } }<h2>父組件內容:</h2> <h3>3、通過服務在屬性中共享資料</h3> <p> 修改服務中的資料值 <input type="text" [(ngModel)]="msg"> <button (click)="submit()">提交</button> </p> <p>服務中的資料:{{msg}}</p> <hr> <h2>子組件內容:</h2> <app-child-component></app-child-component>在子組件中引入服務,從而同步獲取到父組件修改后的服務中的資料資訊
import { Component, OnInit } from '@angular/core'; // 引入服務 import { StorageService } from '../services/storage/storage.service'; @Component({ selector: 'app-child-component', templateUrl: './child-component.component.html', styleUrls: ['./child-component.component.scss'] }) export class ChildComponentComponent implements OnInit { public storageMsg: string; constructor(private storage: StorageService) { } ngOnInit(): void { } getServiceMsg() { this.storageMsg = this.storage.getMsg(); } }<button (click)="getServiceMsg()">獲取服務中的資料值</button> <p> 服務中 msg 屬性值:{{storageMsg}} </p>
五、組件的生命周期鉤子函式
當 angular 在創建、更新、銷毀組件時都會觸發組件的生命周期鉤子函式,通過在組件中實作這些生命周期函式,從而介入到這些關鍵時刻
| 鉤子函式 | 觸發時機 |
|---|---|
| ngOnChanges | 被系結的輸入屬性值發生變化時觸發,會呼叫多次;如果沒有使用到父子組件傳值,則不會觸發 |
| ngOnInit | 初始化組件時會呼叫一次,一般是用來在建構式之后執行組件復雜的初始化邏輯 |
| ngDoCheck | 只要資料發生改變就會被呼叫 |
| ngAfterContentInit | 組件內容渲染完成后呼叫一次 |
| ngAfterContentChecked | 只要組件的內容發生改變就會被呼叫 |
| ngAfterViewInit | 視圖加載完成后觸發一次,一般用來對視圖的 dom 元素進行操作 |
| ngAfterViewChecked | 視圖發生變化時呼叫,在組件的生命周期中會呼叫多次 |
| ngOnDestroy | 只在銷毀組件時呼叫一次,一般用來在組件銷毀前執行某些操作 |
在組件加載程序中,會按照上面列出的鉤子函式順序,在組件的建構式執行之后依次執行,在頁面加載程序中會涉及系結資料的操作,因此會再次出發 ngDoCheck、ngAfterContentChecked、ngAfterViewChecked 這三個生命周期鉤子函式,后續只要頁面資料有發生改變,都會觸發這幾個事件

裝飾器是一種特殊型別的宣告,它能夠被附加到類宣告,方法, 訪問符,屬性或引數上,就像是 C# 中的特性 ??
元資料是用來描述資料的資料項,例如這里的 selector 是為了描述 Component 這個資料資訊資源中抽取出來用于說明其特征的一個結構化的資料 ??
property 是 dom 元素默認的基本屬性,在 dom 初始化時會被全部創建,而 attribute 是 html 標簽上定義的屬性和值 =》DOM 中 Property 和 Attribute 的區別 ??
這里的資料改變指的是會將原來的資料物件重新銷毀然后重建的程序,因此像 push、unshift 這樣的方法即使不添加 trackBy 也不會重新渲染整個 DOM,只會重新渲染改變的資料 ??
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/147691.html
標籤:JavaScript
下一篇:RX.js6變化
