应用场景
在视频回放的第 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;
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 (); // 必须的,否则报错, 创建对象的实例
}
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 的双向数据绑定,是建立在一个共享内存(地址)的基础之上的。 所以才出现,一变俱变!