Angular学习笔记31:组件之间的交互

组件之间的交互

在实际的工作中,有时候会有这样的需要,在同一个页面,这个页面又可能是不止一个组件构成,可能需要两个,甚至多个组件时,做个组件还需要进行数据信息的共享。

用两个组件之间的通讯进行举例

1.创建两个组件,一个父组件,一个子组件。

ng g c parentCom
ng g c childCom
wujiayudeMacBook-Pro:demo-test wjy$ ng g c parentCom
CREATE src/app/parent-com/parent-com.component.less (0 bytes)
CREATE src/app/parent-com/parent-com.component.html (29 bytes)
CREATE src/app/parent-com/parent-com.component.spec.ts (650 bytes)
CREATE src/app/parent-com/parent-com.component.ts (285 bytes)
UPDATE src/app/app.module.ts (991 bytes)
wujiayudeMacBook-Pro:demo-test wjy$ ng g c childCom
CREATE src/app/child-com/child-com.component.less (0 bytes)
CREATE src/app/child-com/child-com.component.html (28 bytes)
CREATE src/app/child-com/child-com.component.spec.ts (643 bytes)
CREATE src/app/child-com/child-com.component.ts (281 bytes)
UPDATE src/app/app.module.ts (1083 bytes)

2.在父组件中使用子组件。

在父组件中增加一个按钮,每点击一次这个按钮,就会向子组件中传输一次数据。在父组件中使用子组件。(这里使用了 ng-zorro);

<nz-row>
  <nz-form-label nz-col nzOffset="3" [nzSpan]="3">
    父组件
  </nz-form-label>
  <nz-form-control nz-col nzSpan="5" >
    <button nz-button (click)="parentEvent()">父组件的按钮</button>
  </nz-form-control>
</nz-row>
<hr/>
<nz-row>
  <nz-col nzOffset="3">
    <app-child-com></app-child-com>
  </nz-col>
</nz-row>

保存,浏览器中的页面如下:

为了区别,特意在子组件和父组件中加了一条线。现在要做的就是怎样向子组件传输数据?

通过输入型绑定把数据从父组件传到子组件

在子组件中使用输入型属性,即使用;@Input()装饰器;

编辑子组件的类文件,增加两个输入型属性的变量,在Angular中,对于输入型属性,是可以设置别名的。如下图:

但是根据Angular的风格指南,不推荐对输入型属性使用别名。

在子组件的显示这两个输入型属性;

<nz-row>
  <nz-form-label nz-col nzOffset="3" [nzSpan]="3">
    fromParent
  </nz-form-label>
  <nz-form-control nz-col nzSpan="5">
    {{fromParent}}
  </nz-form-control>
</nz-row>
<nz-row>
  <nz-form-label nz-col nzOffset="3" [nzSpan]="3">
    fromParentAlias
  </nz-form-label>
  <nz-form-control nz-col nzSpan="5">
    {{fromParentAlias}}
  </nz-form-control>
</nz-row>

在父组件的模版文件,对子组件的两个输入型属性进行变量的绑定:

在父组件的类文件中,新增两个变量,并添加button的点击事件。

保存刷新浏览器的页面:

然后点击按钮:页面就会发生如下变化:

这样,就将父组件中的toChild属性和子组件中的两个输入型属性变量进行了绑定。

获取输入型属性的变化

1.通过 setter 截听输入属性值的变化

修改子组件的类文件如下:

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

@Component({
  selector: 'app-child-com',
  templateUrl: './child-com.component.html',
  styleUrls: ['./child-com.component.less']
})
export class ChildComComponent implements OnInit {
  private fromParentData: string;

  constructor() {
  }

  @Input()
  set fromParent(info: string) {
    // 处理得到的info
    this.fromParentData = info;
  }

  get fromParent() {
    return this.fromParentData;
  }


  ngOnInit() {
  }

}

这个时候,就会由一个输入属性的 setter,来获取来自父组件的值,在这个中,可以对获取到的值进行处理,通过get fromParent() 去得到这个来自父组件并处理后的值,在子组件的模版文件上展示;当然,也可以不使用get fromParent() , 直接将属性 fromParentData 展示在模版文件中,但是要将 fromParentData 的private改为public 或者删掉。

<nz-row>
  <nz-form-label nz-col nzOffset="3" [nzSpan]="3">
    fromParent
  </nz-form-label>
  <nz-form-control nz-col nzSpan="5">
    {{fromParent}}
  </nz-form-control>
</nz-row>

保存,浏览器刷新的页面为:

2.通过ngOnChanges()来截听输入属性值的变化

这个时候,就需要用到生命周期钩子:OnChanges,修改子组件如下:

import {Component, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core';

@Component({
  selector: 'app-child-com',
  templateUrl: './child-com.component.html',
  styleUrls: ['./child-com.component.less']
})
export class ChildComComponent implements OnInit, OnChanges {
  private fromParentData: string;
  @Input() fromParent: string;

  constructor() {
  }

  // @Input()
  // set fromParent(info: string) {
  //   // 处理得到的info
  //   this.fromParentData = info;
  // }
  //
  // get fromParent() {
  //   return this.fromParentData;
  // }


  ngOnInit() {
  }

  ngOnChanges(changes: SimpleChanges): void {
    // 处理得到的fromParent
    console.log(changes);
  }

}

保存,刷新页面,就可以看到每次的变化

同过ngOnChanges() 可以清楚的得到输入型属性的每一次的变化和其前一次的变化的值,并且是不是第一次发生变化都可以知道。

父组件监听子组件的事件

有时候,子组件的操作完成了,或者事件处理完成了,需要向父组件传输数据。

在子组件中,创建一个button,每当子组件的这个button在用户点击的时候,就向父组件发送数据。这个时候就要用到@Output()装饰器,

修改子组件的模版文件如下:

<nz-row>
  <nz-form-label nz-col nzOffset="3" [nzSpan]="3">
    fromParent
  </nz-form-label>
  <nz-form-control nz-col nzSpan="5">
    {{fromParent}}
  </nz-form-control>
</nz-row>
<nz-row>
  <nz-form-label nz-col nzOffset="3" [nzSpan]="3">
    子组件
  </nz-form-label>
  <nz-form-control nz-col nzSpan="5">
   <button nz-button (click)="toParentEvent()">子组件的按钮</button>
  </nz-form-control>
</nz-row>

修改子组件的类文件如下:

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

@Component({
  selector: 'app-child-com',
  templateUrl: './child-com.component.html',
  styleUrls: ['./child-com.component.less']
})
export class ChildComComponent implements OnInit, OnChanges {
  private fromParentData: string;
  @Input() fromParent: string;
  @Output() toParent = new EventEmitter<string>();

  constructor() {
  }

  // @Input()
  // set fromParent(info: string) {
  //   // 处理得到的info
  //   this.fromParentData = info;
  // }
  //
  // get fromParent() {
  //   return this.fromParentData;
  // }


  ngOnInit() {
  }

  ngOnChanges(changes: SimpleChanges): void {
    console.log(changes);
  }

  toParentEvent() {
    this.toParent.emit('点击了子组件');
  }
}

子组件的 EventEmitter属性是一个输出属性,通常需要@Output装饰器。

修改父组件的模版文件如下:

<nz-row>
  <nz-form-label nz-col nzOffset="3" [nzSpan]="3">
    父组件
  </nz-form-label>
  <nz-form-control nz-col nzSpan="5">
    <button nz-button (click)="parentEvent()">父组件的按钮</button>
  </nz-form-control>
</nz-row>
<hr/>
<nz-row>
  <nz-col nzOffset="3">
    <app-child-com [fromParent]="toChild" (toParent)="getChild($event)"></app-child-com>
  </nz-col>
</nz-row>

修改父组件的类文件如下:

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

@Component({
  selector: 'app-parent-com',
  templateUrl: './parent-com.component.html',
  styleUrls: ['./parent-com.component.less']
})
export class ParentComComponent implements OnInit {
  toChild: string;
  count = 0;

  constructor() {
  }

  ngOnInit() {
  }

  parentEvent() {
    this.toChild = 'click' + this.count++;
  }

  getChild(info) {
    console.log(info);
  }
}

保存,刷新页面显示如下:

父组件与子组件通过本地变量互动

有时候,在父组件中,父组件不能使用数据绑定来读取子组件的属性或调用子组件的方法。但可以在父组件模板里,新建一个本地变量来代表子组件,然后利用这个变量来读取子组件的属性和调用子组件的方法。

修改父组件模版文件如下:

<nz-row>
  <nz-form-label nz-col nzOffset="3" [nzSpan]="3">
    父组件
  </nz-form-label>
  <nz-form-control nz-col nzSpan="5">
    <button nz-button (click)="isChild.toParentEvent()">父组件的按钮</button>
  </nz-form-control>
</nz-row>
<hr/>
<nz-row>
  <nz-col nzOffset="3">
    <app-child-com #isChild></app-child-com>
  </nz-col>
</nz-row>

修改子组件的类文件如下:

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

@Component({
  selector: 'app-child-com',
  templateUrl: './child-com.component.html',
  styleUrls: ['./child-com.component.less']
})
export class ChildComComponent implements OnInit, OnChanges {
  private fromParentData: string;
  // @Input() fromParent: string;
  // @Output() toParent = new EventEmitter<string>();

  constructor() {
  }

  // @Input()
  // set fromParent(info: string) {
  //   // 处理得到的info
  //   this.fromParentData = info;
  // }
  //
  // get fromParent() {
  //   return this.fromParentData;
  // }


  ngOnInit() {
  }

  ngOnChanges(changes: SimpleChanges): void {
    console.log(changes);
  }

  toParentEvent() {
    console.log('点击了子组件');
  }
}

保存,每次点击按钮,控制台会输出:

这个时候,子组件就在父组件中作为一个本地变量,父组件中的button 就可以直接调用子组件中的方法了,这个时候,这个父组件本地变量就可以使用子组件中变量和方法了。虽然子组件作为本地变量存在于父组件中,但是这个时候是有一定局限性的,因为这个本地变量的所有属性和方法只能在父组件的模板中进行,不能在父组件的类文件中使用。

注意: 经测试发现,使用下列方法也可以调用到子组件的方法:

父组件模版文件:

<nz-row>
  <nz-form-label nz-col nzOffset="3" [nzSpan]="3">
    父组件
  </nz-form-label>
  <nz-form-control nz-col nzSpan="5">
    <button nz-button (click)="parentEvent(isChild)">父组件的按钮</button>
  </nz-form-control>
</nz-row>
<hr/>
<nz-row>
  <nz-col nzOffset="3">
    <app-child-com #isChild></app-child-com>
  </nz-col>
</nz-row>

父组件类文件:

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

@Component({
  selector: 'app-parent-com',
  templateUrl: './parent-com.component.html',
  styleUrls: ['./parent-com.component.less']
})
export class ParentComComponent implements OnInit {
  toChild: string;
  count = 0;

  constructor() {
  }

  ngOnInit() {
  }

  parentEvent(child: any) {
    child.toParentEvent();
  }

  // getChild(info) {
  //   console.log(info);
  // }
}

保存以后,页面

在这里,是将这个本地变量,通过点击事件传参数的方式传进来了,然后在类文件中调用,同样能达到调用子组件的效果,但是不推荐使用。

父组件调用@ViewChild()于子组件交互

如果父组件的类文件需要读取子组件的属性值或调用子组件的方法,就不能使用本地变量方法。当父组件需要这种访问时,可以把子组件作为 ViewChild注入到父组件里面。然后就可以读取子组件的属性和子组件的方法了。

修改父组件模版文件为:

<nz-row>
  <nz-form-label nz-col nzOffset="3" [nzSpan]="3">
    父组件
  </nz-form-label>
  <nz-form-control nz-col nzSpan="5">
    <button nz-button (click)="parentEvent()">父组件的按钮</button>
  </nz-form-control>
</nz-row>
<hr/>
<nz-row>
  <nz-col nzOffset="3">
    <app-child-com></app-child-com>
  </nz-col>
</nz-row>

修改父组件模版文件为:

import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core';
import {Template} from '@angular/compiler/src/render3/r3_ast';
import {ChildComComponent} from '../child-com/child-com.component';

@Component({
  selector: 'app-parent-com',
  templateUrl: './parent-com.component.html',
  styleUrls: ['./parent-com.component.less']
})
export class ParentComComponent implements OnInit {
  // toChild: string;
  // count = 0;
  @ViewChild(ChildComComponent)
  private child: ChildComComponent;

  constructor() {
  }

  ngOnInit() {
  }

  parentEvent() {
    this.child.toParentEvent();
  }

  // getChild(info) {
  //   console.log(info);
  // }
}

这个时候,通过@ViewChild装饰器就可以访问到子组件的属性和方法了。

父组件和子组件通过服务来通讯

1.新建一个服务

ng g service parentChildService
wujiayudeMacBook-Pro:demo-test wjy$ ng g service parentChildService
CREATE src/app/parent-child-service.service.spec.ts (395 bytes)
CREATE src/app/parent-child-service.service.ts (147 bytes)

修改服务文件如下:

import {Injectable} from '@angular/core';
import {Subject} from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ParentChildServiceService {
  private transformData = new Subject<string>();
  transform = this.transformData.asObservable();

  constructor() {
  }

  setTransformData(data: string) {
    this.transformData.next(data);
  }
}

这里@Injectable({  providedIn: 'root'}) 装饰器是将这个服务注册到了整个应用都可以用了。在专注于两个组件时,将其注入于两个组件即可。这里的transformData用于接受每次传输过来的值,然后transformData作为一个Observable赋值给transform。

在子组件的类文件中:

import {Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges} from '@angular/core';
import {ParentChildServiceService} from '../parent-child-service.service';
import {Subscription} from 'rxjs';

@Component({
  selector: 'app-child-com',
  templateUrl: './child-com.component.html',
  styleUrls: ['./child-com.component.less']
})
export class ChildComComponent implements OnInit, OnChanges, OnDestroy {
  fromServiceData: Subscription;

  constructor(private transformService: ParentChildServiceService) {
    this.fromServiceData = this.transformService.transform.subscribe(data => {
      console.log(data);
    });
  }


  ngOnInit() {
  }

  ngOnDestroy(): void {
    this.fromServiceData.unsubscribe();
  }

  ngOnChanges(changes: SimpleChanges): void {
    console.log(changes);
  }

}

在子组件中,fromServiceData作为一个Subscription对象将服务中的Observable进行订阅,从而,每当服务中的transform发生变化时,fromServiceData就会得到变化后的值。

页面如下:

注意:在这个页面中使用fromServiceData 变量,并在 ChildComComponent 被销毁时调用 unsubscribe() 退订。 这是一个用于防止内存泄漏的保护措施。实际上,在这个应用程序中并没有这个风险,因为 ChildComComponent 的生命期和应用程序的生命期一样长。但在更复杂的应用程序环境中就不一定了。

 

 

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值