介紹 ngOnChanges 比較進階的用法
在之前學習Angular 的過程中,保哥都有強調
不要在子元件內直接異動父元件傳來的資料,
直接異動的話其他的子元件會無法知道這些異動。
因此,我們在實作的過程中,都會刻意在子元件內避免使用 [(ngModle)] 雙向繫結 來直接繫結 父元件的來源資料
直接繫結的話子元件修改資料就直接異動到父元件的資料,這不是一個好的設計模式
可以活用 ngOnChanges 來改善這個問題,
首先複習一下ngOnChanges 的觸發時機點:
當父類別透過透過Property Binding (屬性繫結)將資料傳到子元件,
子元件使用宣告 @Input() 的變數來接收父類別傳來的資料,
這時候子元件的ngOnChange就會被觸發,當父類別的來源資料有異動的時候,也會重新觸發 ngOnChanges()
範例如下
首先定義Component.ts
export class DemoChildComponent implements OnInit, OnChanges {
isEdit = false;
// 宣告一個Original Item 去保留父元件傳過來的物件原始資料
// template在雙向繫結的時候不繫結這個物件,這樣就不會直接異動到父元件的來源資料
OriItem = {};
@Input() inItem;
// 真正要異動父元件資料的時候,透過底下宣告的兩個 @Output() 去觸發父元件,由父元件內部實際去修改
// 修改完成之後,父元件會觸發 ngOnChanges,資料就會重新整理
@Output() ouEditContent = new EventEmitter<any>();
@Output() ouDelItem = new EventEmitter<any>();
constructor() {}
ngOnInit() {}
// changeItems 是一個物件,裡面可能有一個以上的屬性(每一個 @Input() 就會占用一個屬性)
ngOnChanges(changeItems) {
// inItem 這個屬性是 @Input() 變數名稱,用判斷屬性名稱的方式來設計之後要做的事情
if (changeItems.inItem) {
// currentValue 是真正使用的@Input() 資料內容,將這個值傳給 OriItem (保留父元件的原始資料)
this.OriItem = changeItems.inItem.currentValue;
// 透過產生 "新"物件的方式來重新產生 inItem 物件內容,畫面上雙向繫結的對象就是這個新的物件
this.inItem = Object.assign({}, changeItems.inItem.currentValue);
}
}
// 確認存檔(異動父元件內容)
SaveEdit() {
// 確定要存檔的時候,使用上雙向繫結對應物件的內容來異動父元件資料
this.ouEditContent.emit(this.inItem);
}
// 取消修改
CancelEdit() {
// 將ngOnChanges 一開始保留在OriItem地資料內容,透過產生 "新"物件的方式來重新產生 inItem 物件內容
this.inItem = Object.assign({}, this.OriItem);
this.isEdit = false;
}
DeleteItem() {
// 確認要刪除
this.ouDelItem.emit(this.inItem);
}
}
template:
這邊的重點是,[(ngModel)] 雙向繫結的對象,是在ts內透過Object.assign 產生的新物件 inItem,不會直接修改到父元件的資料
<div>
<div *ngIf="!isEdit">
<a [href]="inItem['href-link']">{{inItem.title}}</a>
<br>
<span>Date:{{inItem.date}}</span>
<br>
<span>{{inItem.content}}</span>
</div>
<div *ngIf="isEdit">
<span>content:</span>
<input type="text" [(ngModel)]="inItem.content" (keyup.enter)="SaveEdit()" (keyup.escape)="CancelEdit()">
</div>
<div>
<button (click)="DeleteItem()">Delete</button>
<button (click)="this.isEdit = true;">Edit</button>
</div>
<span>============================================================</span>
</div>
一旦父元件的資料內容被異動後,
就會再透過觸發 ngOnChanges 來重新更新子元件的內容。
結論:
1.
透過重新產生新物件的方式,讓 template 雙向繫結新的物件,
這樣既不會直接異動到父元件的物件,又可以使用好用的雙向繫結。
2.
使用 ngOnChanges 來更新子元件的資料內容,
ngOnChanges()的參數是一個物件,
裡面可能有一個以上的屬性(每一個 @Input() 就會占用一個屬性),
每一個屬性會對應一個物件,
物件內容為: currentValue 、 firstChange、previousValue