Angular组件——父子组件通讯

Angular组件间通讯

组件树,1号是根组件AppComponent。

组件之间松耦合,组件之间知道的越少越好。

组件4里面点击按钮,触发组件5的初始化逻辑。

传统做法:在按钮4的点击事件里调用组件5的方法。紧密耦合。

Angular:在组件4根本不知道组件5存在的情况下实现。

使用松耦合的方式在组件之间传递数据开发出高重用性的组件。

使用输入输出属性在父子关系的组件之间传递数据。

一、输入输出属性概述

组件设计成黑盒模型,用输入属性声明从外部世界接收什么东西。不需要知道这些东西从哪里来来。

组件只需要知道当它需要的东西外部世界提供给它以后它应该怎么做。

组件通过输出属性发射事件告诉外部世界可能感兴趣的东西。至于事件发射给谁组件也不需要知道。

谁感兴趣谁自己订阅组件发射的事件。

二、输入属性

子组件定义了2个输入属性,被@Input()装饰器装饰的属性。

  @Input()
  stockCode:string;
  @Input()
  amount:number;

父组件通过属性绑定到子组件输入属性的方式把stock属性绑定到子组件的stockCode属性上。

<div>
  我是父组件
</div>
<div>
  <input type="text" [(ngModel)]="stock" placeholder="请输入股票代码">
  <app-order [stockCode]=stock [amount]="100"></app-order>
</div>

三、属性绑定是单向的,从父组件到子组件 

 每隔3s重置子组件的stockCode的值为Apple。

export class OrderComponent implements OnInit {

  @Input()
  stockCode:string;
  @Input()
  amount:number;

  constructor() { 
    setInterval(()=>{
      this.stockCode='Apple'
    },3000)
  }

  ngOnInit() {
  }
}

当子组件的stockCode的值变为Apple的时候,父组件的stock的值并没有改变。说明绑定是单向的,只能是父组件改变子组件,子组件属性改变不会影响到父组件。

 

四,输出属性

Angular组件可以使用EventEmitter对象发射自定义事件,这些事件可以被其它组件处理。 EventEmitter是Rxjs中Subject类的一个子类,在响应式编程中,它既可以作为被观察者,也可以作为观察者。就是说EventEmitter对象即可以通过它的emit方法发射自定义事件,也可以通过subscribe方法来订阅EventEmitter发射出来的事件流。

如何使用EventEmit从组件内部向外发射事件?

例子场景:报价组件

假设需要一个组件,可以连接到股票交易所,并且实时的显示变动的股票价格,为了让这个组件可以在不同的金融类的应用中重用,除了实时显示股票价格,组件还应该将最新的股票价格发送到组件之外,这样其它的组件就可以针对变动的股票价格执行相应的业务逻辑。

Note:将特定的数据结构用类或接口来明确定义是一个良好的习惯

1、先模拟一个实时变动的IBM的股票价格

export class PriceQuoteComponent implements OnInit {

  //不连接股票服务,用一个随机数生成器模拟股票价格的变化,并将股票代码和最新的价格显示出来

  stockCode:string="IBM";
  price:number;

  constructor() {
    setInterval(()=>{
      let priceQuote:PriceQuote=new PriceQuote(this.stockCode,100*Math.random());
      this.price=priceQuote.lastPrice;
    },1000)
  }

  ngOnInit() {
  }

}


//封装一个报价对象来封装股票价格信息
//将特定的数据结构用类或接口来明确定义是一个良好的习惯
export class PriceQuote {
  constructor(public stockCode: string, //股票代码
    public lastPrice: number //最新价格
  ) {
  }
}

2、把信息输出出去,告诉组件外部,谁感兴趣谁来订阅

EventEmit后面的范型是要往出发射的事件的数据是什么类型的。

import { Component, OnInit, EventEmitter, Output } from '@angular/core';

@Component({
  selector: 'app-price-quote',
  templateUrl: './price-quote.component.html',
  styleUrls: ['./price-quote.component.css']
})
export class PriceQuoteComponent implements OnInit {

  //不连接股票服务,用一个随机数生成器模拟股票价格的变化,并将股票代码和最新的价格显示出来

  stockCode: string = "IBM";
  price: number;

  @Output() //发射事件需要写上Output
  //EventEmitter需要一个范型
  lastPrice: EventEmitter<PriceQuote> = new EventEmitter();
  //

  constructor() {
    setInterval(() => {
      let priceQuote: PriceQuote = new PriceQuote(this.stockCode, 100 * Math.random());
      this.price = priceQuote.lastPrice;
      //用lastPrice emit一个值出去
      this.lastPrice.emit(priceQuote);
    }, 1000)
  }

  ngOnInit() {
  }

}
//封装一个报价对象来封装股票价格信息 //将特定的数据结构用类或接口来明确定义是一个良好的习惯 export class PriceQuote { constructor(public stockCode: string, //股票代码 public lastPrice: number //最新价格 ) { } }

3、在父组件中接收报价信息并显示

父组件模版中通过事件绑定的方式来捕获并处理。 

export class AppComponent {
  stock = "";
  priceQuote: PriceQuote = new PriceQuote("", 0);

  //event的类型就是子组件emit的时候发射出来的数据的类型
  //父组件中通过event就可以拿到
  priceQuoteHandler(event:PriceQuote){
    this.priceQuote=event;
  }
}

模版

<!--默认情况下,事件名字就是output输出属性的名字-->
<app-price-quote (lastPrice)="priceQuoteHandler($event)"></app-price-quote>

<div>
  这是在报价组件外部<br/>
  股票代码是{{priceQuote.stockCode}},
  股票价格是{{priceQuote.lastPrice | number:"2.0-2"}}
</div>

默认情况下,事件名字就是output输出属性的名字,可以改变事件名字,通过

  @Output("priceChange") //发射事件需要写上Output
  //EventEmitter需要一个范型
  lastPrice: EventEmitter<PriceQuote> = new EventEmitter();

模版中也改为

<app-price-quote (priceChange)="priceQuoteHandler($event)"></app-price-quote>

总结:通过输出属性发射事件,并通过事件携带数据,在父组件模版中通过事件绑定的方式来捕获并处理。 

 

如果两个组件之间不存父子关系,如何以一种松耦合的方式来传递数据。此时需要使用中间人模式

 

本文作者starof,因知识本身在变化,作者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/8636579.html 有问题欢迎与我讨论,共同进步。

posted @ 2018-03-24 11:24  starof  阅读(9617)  评论(0编辑  收藏  举报