在用angular前端中,组件之间的交互十分重要,该篇文件将会对angular框架中组件交互的方式进行一个汇总
组件交互大概分为五种类型
1.@input 父传子
2.@Output 子传父
3.本地变量
4.@viewChild
5.服务
@input 父传子
父子组件的交互是开发中比较常见的,大家用的也比较熟练,但还是统一记一下吧,就当是复习了
父传子时 传引用类型的数据时,在子组件发生改变后,父组件也会发生改变;值类型的数据不会发生变化
父传子 会用到@Input 装饰器
父组件
//模板
<app-parent-one-child [text]="text"></app-parent-one-child>
//ts
text = '这是父组件传给子组件的一段话';
子组件
//模板
<p>父传子:{{text}}</p>
//ts
@Input() text: string = ''
当父传子的属性发生变化,有两种方法进行获取到
setter截听输入值的变化
父组件
在父组件中添加一个定时器,每一秒在text后面添加一个数字
ngOnInit(): void {
interval(1000).pipe(take(4)).subscribe((res) => this.text = this.text + res)
}
子组件
time会变成5 是因为在最初赋值时也会走set方法
//模板
<p>父传子:{{text}}</p>
<p>当父传子属性发生变化时,time会加一:{{time}}</p>
//ts
@Input()
get text() {
return this._text
}
set text(text) {
console.log(text)
this.time++;
this._text = text
}
_text: string = '';
time: number = 0;
通过生命周期ngChages来截听属性值的变化
//子组件
ngOnChanges(changes:SimpleChanges):void{
console.log(changes)
}
changes参数一个包含所有输入属性值的对象,每一个输入属性也是一个对象包含currentValue,previousValue,firstChange;
说的多不如show me your code
例子中changes:
changes:{
text:{
currentValue:'xxx', //当前值
previousValue:'xxx', //上一次的值
firstChange:false //boolean 是否为第一次发生变化
}
}
同样也是定义一个用来计数的变量
//模板
<p>onchange当父传子属性发生变化时,time会加一:{{changeTime}}</p>
changeTime:number = 0;
//ts
ngOnChanges(changes:SimpleChanges):void{
this.changeTime ++;
}
@Output子传父
子组件暴露出EventEmitter属性,子组件利用该属性向上发送事件。父组件绑定这个事件属性,并在事件属性变化后作出回应
父组件
//模板
<app-parent-one-child [text]="text" [staticText]="staticText" (textChange)="textChange($event)"></app-parent-one-child>
//ts
textChange(text:string){
this.text = text
}
子组件
//模板
<button nz-button nzType="primary" (click)="textChangeEmit()">子传父</button>
//ts
@Output() textChange = new EventEmitter<string>();
textChangeEmit(){
this.text= '在子组件被修改了,传给父组件'
this.textChange.emit(this.text)
}
父子组件通过本地变量进行交互
父组件不能通过数据绑定来读取子组件的属性或调用子组件的方法。但可以在父组件的模板中新建一个本地变量来代表子组件,然后通过这个变量来获取子组件中的属性或方法
父组件
//模板
<app-parent-two-child #child></app-parent-two-child>
<hr>
<p>{{child.name}}</p>
<p>{{child.personInfo.name}}</p>
<button nz-button nzType="primary" (click)="child.say(child.name)">调用子组件的方法</button>
<button nz-button nzType="primary" (click)="changeChildState(child.name,child.personInfo)">改变</button>
//ts
changeChildState(name:string,peronInfo:{name:string}){
name = 'aaa'
peronInfo.name = 'bbbb'
}
子组件
//ts
name: string = '值类型name';
personInfo = {
name: '引用类型name'
}
say(name: string) {
alert(`${name}say:hello,world`)
}
@viewChild
如果父组件的类需要依赖于子组件类,就不能使用本地变量方法。组件之间的父子关系 组件的父子关系不能通过在每个组件的类中各自定义本地变量来建立。这是因为这两个类的实例互相不知道,因此父类也就不能访问子类中的属性和方法。
当父组件类需要这种访问时,可以把子组件作为 ViewChild,注入到父组件里面。
@ViewChild(ParentTwoChildComponent) childComponent!:ParentTwoChildComponent
通过this.childComponent 同样可以访问到子组件的属性和方法
组件通过服务来通讯
1.利用根服务进行通讯 但是不利于模块化
2.模块内部 利用普通服务,在模块的根组件中进行引入服务
同样分为两种,一种是将变量定义在服务中,所有的组件都访问服务中的变量
另一种为利用rxjs中subject进行广播 将事件传给多为观察者
首先生成一个服务
@Injectable()
export class ComponentInteractionService {
//生成相应组件的主体物件
//组件a
private missionAnnouncedSource = new Subject<string>();
//组件b
private missionConfirmedSource = new Subject<string>();
//将组件a 对应的主体转为观察者物件 组件a发出的 由这个观察者订阅
missionAnnounced$ = this.missionAnnouncedSource.asObservable();
missionConfirmed$ = this.missionConfirmedSource.asObservable();
constructor() {
}
//组件中方法通过调用下面的方法来执行next,发出广播,推送个多个观察者
//每个主体物件都应有自己的推送方法
announceMission(mission: string) {
this.missionAnnouncedSource.next(mission);
}
confirmMission(astronaut: string) {
this.missionConfirmedSource.next(astronaut);
}
}
接收订阅的广播
this.missionService.missionConfirmed$.subscribe(res=>{
this.fromChildData.push(res)
})
发出推送
this.missionService.announceMission(this.parentData[this.index ++ ])