Angular的核心思想就是组件话,我们可以将组件做成一个独立的应用模块来使用。实际开发过程中,我们会发现在应用中有很多功能可以抽象化,可以复用。
想要把一个通用的功能封装成组件,实现功能复用,那么我们的组件就需要有输入、输出,这就是组件交互。

输入型属性

在Angular中,我们使用@Input装饰器来为组件创建输入型属性。

我们定义一个DemoComponent,包含两个输入型属性:

  1. import { Component, Input } from '@angular/core';
  2. @Component({
  3. selector: 'app-demo',
  4. template: `
  5. <p>name值为: {{name}}</p>
  6. <p>masterName值为:{{masterName}}</p>
  7. `
  8. })
  9. export class DemoComponent {
  10. @Input() name: string;
  11. @Input('master') masterName: string;
  12. }

第一个输入属性是name。
第二个输入属性是masterName,通过@Input装饰器,我们为该属性指定了一个别名master。

这样,组件的输入型属性我们就定义完成了。
我们接下来就可以在html中使用demo组件:

  1. <app-demo name="Angular" master="真棒!"></app-demo>

输入属性setter截听

使用一个输入属性的setter,可以截听从父组件传过来的属性内容,并做相应处理。
Demo2Component的输入属name上使用setter,使用trim方法,trim掉输入的空格,并把空值替换成默认字符串。

  1. import { Component, Input } from '@angular/core';
  2. @Component({
  3. selector: 'app-demo2',
  4. template: '<h3>"{{name}}"</h3>'
  5. })
  6. export class Demo2Component {
  7. private _name = '';
  8. @Input()
  9. set name(name: string) {
  10. this._name = (name && name.trim()) || '没有输入';
  11. }
  12. get name(): string { return this._name; }
  13. }

在父组件中使用方式还是和上面一样:

  1. <app-demo2 name=" 我前面有空格"></app-demo2>
  2. <app-demo2 name="我后面后空格"></app-demo2>
  3. <app-demo2 name=" "></app-demo2>

实际渲染效果如下:

  1. 我前面有空格
  2. 我后面后空格
  3. 没有输入

ngOnChanges()

使用OnChanges生命周期钩子接口ngOnChanges()方法来监测输入属性值的变化并做出相应处理。

组件所有输入属性的变更都会被OnChanges生命周期钩子捕获到。

这个 VersionChildComponent 会监测输入属性 major 和 minor 的变化,并把这些变化编写成日志以报告这些变化。

  1. import { Component, Input, OnChanges, SimpleChange } from '@angular/core';
  2. @Component({
  3. selector: 'app-version-child',
  4. template: `
  5. <h3>Version {{major}}.{{minor}}</h3>
  6. <h4>Change log:</h4>
  7. <ul>
  8. <li *ngFor="let change of changeLog">{{change}}</li>
  9. </ul>
  10. `
  11. })
  12. export class VersionChildComponent implements OnChanges {
  13. @Input() major: number;
  14. @Input() minor: number;
  15. changeLog: string[] = [];
  16. ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
  17. let log: string[] = [];
  18. for (let propName in changes) {
  19. let changedProp = changes[propName];
  20. let to = JSON.stringify(changedProp.currentValue);
  21. if (changedProp.isFirstChange()) {
  22. log.push(`Initial value of ${propName} set to ${to}`);
  23. } else {
  24. let from = JSON.stringify(changedProp.previousValue);
  25. log.push(`${propName} changed from ${from} to ${to}`);
  26. }
  27. }
  28. this.changeLog.push(log.join(', '));
  29. }
  30. }

VersionParentComponent 提供 minor 和 major 值,把修改它们值的方法绑定到按钮上。

  1. import { Component } from '@angular/core';
  2. @Component({
  3. selector: 'app-version-parent',
  4. template: `
  5. <h2>Source code version</h2>
  6. <button (click)="newMinor()">New minor version</button>
  7. <button (click)="newMajor()">New major version</button>
  8. <app-version-child [major]="major" [minor]="minor"></app-version-child>
  9. `
  10. })
  11. export class VersionParentComponent {
  12. major = 1;
  13. minor = 23;
  14. newMinor() {
  15. this.minor++;
  16. }
  17. newMajor() {
  18. this.major++;
  19. this.minor = 0;
  20. }
  21. }

下面是点击按钮的结果。

组件交互 - 图1

输出型属性

就像我们的方法一样,有输入参数,有返回值。同样,我们的组件交互也需要有返回值(即输出型属性)。通过输出型属性,我们可以就将子组件的内容反馈给父组件,从而实现从子组件到父组件的交互。

在Angular中,我们使用@Output来标识一个输出型属性,输出型属性都需要定义成EventEmitter类型。

  1. @Output change: EventEmitter<boolean> = new EventEmitter<boolean>();

在父组件中使用 () 来绑定子组件的输出型属性:

  1. <app-child (change)="doChange($event)"></app-child>

$event 的类型就是子组件中EventEmitter的泛型值boolean。

在子组件的中通过change属性传值:

  1. this.change.emit(true);

这样我们的父组件中的doChange中就可以获取true值:

  1. doChange(event: boolean) {
  2. // 此时的event就是true
  3. console.log(event);
  4. }
  5. // 浏览器空时