应用场景
这是一个带有数据交互的网页,如下图。
上半部分实现添加商品,下半部分是商品列表展示。 当添加一个商品后, 商品列表会自动展示出来,无需刷新页面。
实现思路:
在直播课程中,我们讲述了它的实现方法, 当时是通过 @Output()
装饰符实现的。 @Output()
理解起来还是有些“绕”。
这次,我们换一种实现方法, 通过 @ViewChild()
装饰符来实现。
关于 @ViewChild()
与 @Input
、 @Output()
一样, @ViewChild
也是一个很重要的 Decorator (装饰符), 它的应用场景是:
当Parent component 对 child component 进行存取操作时,可用到 @ViewChild
。
关键技术点
在 Parent Component ,要用到 AfterViewInit
按照 Angular 机制, 我们可以在 parent component 中,通过 @ViewChild() decorator (装饰符)来声明一个属于 child component 的成员属性变量, 然后,就可以对这个 child component 进行存在操作,比如: 调用 child component 的属性变量,调用 child component 的方法。
@ViewChild()
里面的参数,要传入所引入的 child component。
需要注意的是: 要想通过 @ViewChild()
存取 child component 的 pubic field (默认的是 pubic 属性), 必须在 ngAfterViewInit()
lifecycle hook 中才能抓到它,不可以在 ngOnInit()
中。
说了这么多, 还是上代码吧!
代码实现
实现思路:
把整个页面放在一个component中 (home.component), 下方的商品列表放在另外一个独立的 component 中(product-list.component)。 首页(home)是一个筐,里面有多个 component 组成。
// home.component.html
<div class="container">
<h3 class="text-center"> 创建商品 </h3>
<form >
<div class="form-group">
<label for="username"> 商品名称: </label>
<input type="text" class="form-control" placeholder="请输入商品名称" [(ngModel)]= "title" name = "key_title">
</div>
<div class="form-group">
<label for="text">商品价格: </label>
<input type="text" class="form-control" placeholder="请输入商品价格" [(ngModel)]= 'price' name = "key_price">
</div>
<div class="form-group">
<input type = "button" class = "btn btn-primary" value ="添加商品" (click)="createProduct()">
</div>
</form>
</div>
<app-product-list> </app-product-list>>
注意上面的代码:
<app-product-list> </app-product-list>>
// home.component.ts
import { Component, OnInit, ViewChild ,AfterViewInit } from '@angular/core';
import {ProductListComponent} from '../product-list/product-list.component'
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit, AfterViewInit {
@ViewChild (ProductListComponent) private productListComponent : ProductListComponent;
title: string;
price: string;
ngAfterViewInit(): void { }
constructor() { }
ngOnInit() {
}
createProduct(){
let myProduct : any = {
title: this.title,
price: this.price,
};
this.productListComponent.addProduct( myProduct )
}
}
注意以上关键代码:
@ViewChild (ProductListComponent) private productListComponent : ProductListComponent;
再来看商品列表组件的实现
// product-list.component.html
<div class="container">
<h3 class="text-center">商品列表</h3>
<table class="table table-dark table-hover table-striped">
<thead class="thead-light">
<tr>
<th>商品名称</th>
<th>商品价格</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of products">
<td>{{p.title}}</td>
<td>{{p.price}}</td>
</tr>
</tbody>
</table>
</div>
// product-list.component.ts
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( obj: any ){
this.myProduct = {
title : obj.title,
price: obj.price
}
this.products.push( this.myProduct );
console.log("收到了来自 child 组件的数据",this.products);
}
}
product.ts
export class Product {
title : string;
price : number;
}
小结
当一个页面由多个组件构成,且组件之间有数据交互发生时, 可以通过 @ViewChild 装饰符来实现。 可以这样说, 只要是 UI 相关的开发,都要解决 组件之间的数据交互问题, 不同框架的命名方式都很接近。都带有 child 字样,比如 iOS 中的 addChildViewController 等等。 一通百通!