應用場景
在視頻回放的第 34 講 34.2 節, 講了Angular 雙向數據綁定 (ngModel)的應用。 在工程示例中,出現了一個詭異的現象, 就是 child component 的編輯數據與 parent component 列表的數據出現了同步,正常情況下,應該是只有點擊 “添加商品”后, 商品列表才會更新, 這是什么原因造成的呢?
為什么出現錯誤的判斷?
既然是雙向數據綁定, 就意味著只要數據發生變化,就會觸發相應的onChange事件, 由此下意識地得出結論: 是 ngModel 造成的, 從而對 ngModel進行了批判。接著對工程進行了改造, 用 模板引用變量
(template reference variable)解決了這個問題。 問題是解決了,但問題產生判斷真正原因真的是 ngModel
造成的嗎?
問題解讀與剖析
// product-create.component.ts
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { Product } from "../product";
@Component({
selector: 'app-product-create',
templateUrl: './product-create.component.html',
styleUrls: ['./product-create.component.css']
})
export class ProductCreateComponent implements OnInit {
@Output() created = new EventEmitter<Product>();
product : Product;
constructor() {
this.product = new Product;
}
ngOnInit() {
}
createProduct( ){
this.created.emit( this.product );
console.log("子組件發射的數據=",this.product );
}
}
既然懷疑是 ngModel 觸發的, 那么,在 createProduct() 方法中,添加一個log,如下:
createProduct( ){
this.created.emit( this.product );
console.log("子組件發射的數據=",this.product );
}
重新運行。 在創建商品的編輯框中,編輯數據時,發現 在瀏覽器的console中,數據并沒有發生變化,這說明, ngModel 在數據變化時,沒有觸發 createProduct( ) 方法。
找到真正原因
出現bug, 調試是最好的方法。 商品(product) 存放在一個數組中, log 如下:
前4項是初始化的商品列表, 從第5項開始,是新添加的商品。
發現一個奇怪的現象: 這個商品列表數組存放的是 Product,也就是說,存放了一個新添加的對象,而這個對象正是 ngModel 對象
import { Component, OnInit } from '@angular/core';
import { Product } from "../product";
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.css']
})
export class ProductListComponent implements OnInit {
products: Product[];
myProduct: any;
constructor() {
this.products = new Array <Product>(); // 必須的,否則報錯, 創建對象的實例
}
ngOnInit() {
this.products = [
{ title: "第 1 件商品", price: 11 },
{ title: "第 2 件商品", price: 22 },
{ title: "第 3 件商品", price: 33 },
{ title: "第 4 件商品", price: 44 },
{ title: "第 5 件商品", price: 55 },
];
}
addProduct( product: Product ){
this.products.push(product);
console.log("收到了來自 child 組件的數據",this.products);
}
}
關鍵代碼:
addProduct( product: Product ){
this.products.push(product);
}
真正的原因在于::
子組件
發射的對象是:
this.created.emit( this.product );
父組件
接收到這個product 對象后, 進行了壓棧 ,如下:
> this.products.push(product);
解決方案
不再存儲這個與 ngModel 關聯的對象,而是重新構建一個Product 對象, 代碼改造如下:
// product-list.component.ts
addProduct( product: Product ){
this.myProduct = {
title : product.title,
price: product.price
}
this.products.push( this.myProduct );
}
運行測試,之前的詭異現象不見了:
小結
用到 ngModel 時, 要特別注意。 從以上代碼可以看出, ngModel 的雙向數據綁定,是建立在一個共享內存(地址)的基礎之上的。 所以才出現,一變俱變!