发表于: 2020-06-12 20:50:10

1 2041


今天完成的事情:今天重新看了父子组件通信的方法
明天计划的事情:继续后续的任务
遇到的问题:实践才能更好地掌握还是需要多练习
收获:通过输入型绑定把数据从父组件传到子组件。

HeroChildComponent 有两个输入型属性,它们通常带@Input 装饰器。

import { ComponentInput } from '@angular/core';

import { Hero } from './hero';

@Component({
  selector: 'app-hero-child',
  template: `
    <h3>{{hero.name}} says:</h3>
    <p>I, {{hero.name}}, am at your service, {{masterName}}.</p>
  `
})
export class HeroChildComponent {
  @Input() heroHero;
  @Input('master'masterNamestring;
}

第二个 @Input 为子组件的属性名 masterName 指定一个别名 master

父组件 HeroParentComponent 把子组件的 HeroChildComponent 放到 *ngFor 循环器中,把自己的 master 字符串属性绑定到子组件的 master 别名上,并把每个循环的 hero 实例绑定到子组件的 hero 属性。

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

import { HEROES } from './hero';

@Component({
  selector: 'app-hero-parent',
  template: `
    <h2>{{master}} controls {{heroes.length}} heroes</h2>
    <app-hero-child *ngFor="let hero of heroes"
      [hero]="hero"
      [master]="master">
    </app-hero-child>
  `
})
export class HeroParentComponent {
  heroes = HEROES;
  master = 'Master';
}

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

使用一个输入属性的 setter,以拦截父组件中值的变化,并采取行动。

子组件 NameChildComponent 的输入属性 name 上的这个 setter,会 trim 掉名字里的空格,并把空值替换成默认字符串。

import { ComponentInput } from '@angular/core';

@Component({
  selector: 'app-name-child',
  template: '<h3>"{{name}}"</h3>'
})
export class NameChildComponent {
  private _name = '';

  @Input()
  set name(namestring) {
    this._name = (name && name.trim()) || '<no name set>';
  }

  get name(): string { return this._name; }
}

下面的 NameParentComponent 展示了各种名字的处理方式,包括一个全是空格的名字。

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

@Component({
  selector: 'app-name-parent',
  template: `
  <h2>Master controls {{names.length}} names</h2>
  <app-name-child *ngFor="let name of names" [name]="name"></app-name-child>
  `
})
export class NameParentComponent {
  // Displays 'Dr IQ', '<no name set>', 'Bombasto'
  names = ['Dr IQ''   ''  Bombasto  '];
}

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

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

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

export class VersionChildComponent implements OnChanges {
  @Input() majornumber;
  @Input() minornumber;
  changeLogstring[] = [];

  ngOnChanges(changes: {[propKeystring]: SimpleChange}) {
    let logstring[] = [];
    for (let propName in changes) {
      let changedProp = changes[propName];
      let to = JSON.stringify(changedProp.currentValue);
      if (changedProp.isFirstChange()) {
        log.push(`Initial value of ${propName} set to ${to}`);
      } else {
        let from = JSON.stringify(changedProp.previousValue);
        log.push(`${propName} changed from ${from} to ${to}`);
      }
    }
    this.changeLog.push(log.join(', '));
  }
}

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

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

@Component({
  selector: 'app-version-parent',
  template: `
    <h2>Source code version</h2>
    <button (click)="newMinor()">New minor version</button>
    <button (click)="newMajor()">New major version</button>
    <app-version-child [major]="major" [minor]="minor"></app-version-child>
  `
})
export class VersionParentComponent {
  major = 1;
  minor = 23;

  newMinor() {
    this.minor++;
  }

  newMajor() {
    this.major++;
    this.minor = 0;
  }
}

父组件监听子组件的事件

子组件暴露一个 EventEmitter 属性,当事件发生时,子组件利用该属性 emits(向上弹射)事件。父组件绑定到这个事件属性,并在事件发生时作出回应。

子组件的 EventEmitter 属性是一个输出属性,通常带有@Output 装饰器,就像在 VoterComponent 中看到的。

import { ComponentEventEmitterInputOutput } from '@angular/core';

@Component({
  selector: 'app-voter',
  template: `
    <h4>{{name}}</h4>
    <button (click)="vote(true)"  [disabled]="didVote">Agree</button>
    <button (click)="vote(false)" [disabled]="didVote">Disagree</button>
  `
})
export class VoterComponent {
  @Input()  namestring;
  @Output() voted = new EventEmitter<boolean>();
  didVote = false;

  vote(agreedboolean) {
    this.voted.emit(agreed);
    this.didVote = true;
  }
}

点击按钮会触发 true 或 false(布尔型有效载荷)的事件。

父组件 VoteTakerComponent 绑定了一个事件处理器(onVoted()),用来响应子组件的事件($event)并更新一个计数器。

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

@Component({
  selector: 'app-vote-taker',
  template: `
    <h2>Should mankind colonize the Universe?</h2>
    <h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3>
    <app-voter *ngFor="let voter of voters"
      [name]="voter"
      (voted)="onVoted($event)">
    </app-voter>
  `
})
export class VoteTakerComponent {
  agreed = 0;
  disagreed = 0;
  voters = ['Narco''Celeritas''Bombasto'];

  onVoted(agreedboolean) {
    agreed ? this.agreed++ : this.disagreed++;
  }
}

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

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

子组件 CountdownTimerComponent 进行倒计时,归零时发射一个导弹。start 和 stop 方法负责控制时钟并在模板里显示倒计时的状态信息。

import { ComponentOnDestroyOnInit } from '@angular/core';

@Component({
  selector: 'app-countdown-timer',
  template: '<p>{{message}}</p>'
})
export class CountdownTimerComponent implements OnInitOnDestroy {

  intervalId = 0;
  message = '';
  seconds = 11;

  clearTimer() { clearInterval(this.intervalId); }

  ngOnInit()    { this.start(); }
  ngOnDestroy() { this.clearTimer(); }

  start() { this.countDown(); }
  stop()  {
    this.clearTimer();
    this.message = `Holding at T-${this.seconds} seconds`;
  }

  private countDown() {
    this.clearTimer();
    this.intervalId = window.setInterval(() => {
      this.seconds -= 1;
      if (this.seconds === 0) {
        this.message = 'Blast off!';
      } else {
        if (this.seconds < 0) { this.seconds = 10; } // reset
        this.message = `T-${this.seconds} seconds and counting`;
      }
    }, 1000);
  }
}

计时器组件的宿主组件 CountdownLocalVarParentComponent 如下:

import { Component }                from '@angular/core';
import { CountdownTimerComponent }  from './countdown-timer.component';

@Component({
  selector: 'app-countdown-parent-lv',
  template: `
  <h3>Countdown to Liftoff (via local variable)</h3>
  <button (click)="timer.start()">Start</button>
  <button (click)="timer.stop()">Stop</button>
  <div class="seconds">{{timer.seconds}}</div>
  <app-countdown-timer #timer></app-countdown-timer>
  `,
  styleUrls: ['../assets/demo.css']
})
export class CountdownLocalVarParentComponent { }

父组件不能通过数据绑定使用子组件的 start 和 stop 方法,也不能访问子组件的 seconds 属性。

把本地变量(#timer)放到(<countdown-timer>)标签中,用来代表子组件。这样父组件的模板就得到了子组件的引用,于是可以在父组件的模板中访问子组件的所有属性和方法。

这个例子把父组件的按钮绑定到子组件的 start 和 stop 方法,并用插值来显示子组件的 seconds 属性。

剩下的几个方法明天再继续看


返回列表 返回列表
评论

    分享到