Angular组件通信方式

78 阅读3分钟

父传子

父组件向子组件传值,主要是使用到了@Input
例子:
父组件:

//app.component.html
<app-child [childMsg]="parentMsg"></app-child>
//app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  parentMsg = 'parent component message!';
}

子组件:

//child.component.ts
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css'],
})
export class ChildComponent {
  @Input() childMsg: string = '';
}
// child.component.html

<div>父组件传递的内容:{{ childMsg }}</div>

子传父

子组件使用@Output装饰器允许数据从子组件传给父组件。为了引发事件,@Input()必须是EventEmitter类型,它是@agular/core中用来发出自定义事件的类。
例子:
父组件

 // app.component.ts
 import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  items = ['item1', 'item2', 'item3'];
  addItem(newItem: string) {
    this.items.push(newItem);
  }
}
// app.component.html

<app-child (newItemEvent)="addItem($event)"></app-child>
<ul>
  <li *ngFor="let item of items">{{ item }}</li>
</ul>

子组件

//child.component.ts
import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css'],
})
export class ChildComponent {
  @Output() newItemEvent = new EventEmitter<string>();
  addNewItem(value: string) {
    this.newItemEvent.emit(value);
  }
}
//child.component.html 
<label>输入项目名:<input type="text" #newItem /></label>
<button type="button" (click)="addNewItem(newItem.value)">
  添加项目到父组件
</button>

实现的效果如下:

angular组件通信.gif

本地变量

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

子组件

//child.component.ts
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css'],
})
export class ChildComponent {
  count: number = 0;
  add() {
    this.count++;
  }
  decrease() {
    this.count--;
  }
}

父组件

// app.component.html
<app-child #child></app-child>
<button type="button" (click)="child.add()">加一</button>
<button type="button" (click)="child.decrease()">减一</button>
<span>{{ child.count }}</span>

最后的结果如下:

angular组件通信.gif

本地变量的局限性

由于父组件和子组件的连接必须全部在父组件的模板中进行,父组件本身的代码对子组件没有访问权。换句话说,在子组件标签中通过#+变量名的方式新建一个本地变量代表子组件的引用。本地变量方式使用简单明了,但也有局限性,只能在模板html中使用,无法在ts文件中使用。

@ViewChild

通过@ViewChild装饰器,将子组件注入到父组件。
例子
父组件

// app.component.html
<app-child></app-child>
<button type="button" (click)="add(2)">加2</button>
// app.component.ts
import { Component, ViewChild } from '@angular/core';
import { ChildComponent } from './child/child.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  @ViewChild(ChildComponent) private Child: ChildComponent;

  add(num: number) {
    this.Child.add(num);
  }
}

子组件

// child.component.ts
import { Component} from '@angular/core';

@Component({
  selector: 'app-child',
  template: '<span>{{count}}</span>',
  styleUrls: ['./child.component.css'],
})
export class ChildComponent {
  count: number = 0;
  add(num: number) {
    this.count = this.count + num;
  }
}

效果如下:

angular组件通信.gif

通过服务来通信

组件之间共享一个service服务,那么组件之间就可以通过service实现通信 。
创建两个不相关的组件AB,通过服务文件data.service.ts进行关联。
首先,创建data.service.ts

//创建service命令
ng g service data
// data.service.ts
import { Injectable } from '@angular/core';
// 1.引入
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class DataService {
  // 2.创建subject
  subject: BehaviorSubject<any> = new BehaviorSubject<any>(0);

  constructor() {}
}

需要在app.module.ts文件中引入公共data.service.ts文件,并声明。

// app.module.ts
import { DataService } from './data.service';

@NgModule({
  providers: [DataService],
})
export class AppModule {}

其次,创建组件A,用于发布数据

//创建组件A命令  
ng generate component A
// a.component.ts
import { Component } from '@angular/core';
// 1. 引入
import { DataService } from '../data.service';

@Component({
  selector: 'app-a',
  templateUrl: './a.component.html',
})
export class AComponent {
  // 2. 注册
  constructor(public dataService: DataService) {}

  inputValue: string = '';

  send(): void {
    // 3. 发布
    this.dataService.subject.next(this.inputValue);
  }
}
// a.component.html
<label>A组件</label>
<input type="text" [(ngModel)]="inputValue" />
<button type="button" (click)="send()">发送数据</button>

创建组件B,用于接收数据。

//创建组件B命令  
ng generate component B
//b.component.ts
import { Component } from '@angular/core';
// 1. 引入
import { Subscription } from 'rxjs';
import { DataService } from '../data.service';

@Component({
  selector: 'app-b',
  templateUrl: './b.component.html',
})
export class BComponent {
  data: any;

  // 2. subscription
  subscription: Subscription;

  constructor(public dataService: DataService) {
    // 3. 订阅
    this.subscription = this.dataService.subject.subscribe((data) => {
      this.data = data;
    });
  }

  ngOndestry(): void {
    // 4. 取消订阅
    this.subscription.unsubscribe();
  }
}
//b.component.html
<label>B组件接收到的数据:</label>
<span>{{ data }}</span>
// app.component.html
<app-a></app-a>
<br />
<app-b></app-b>

实现的效果如下:

angular组件通信.gif

参考文章

Angular中文文档
玩转Angular系列:组件间各种通信方式详解