кодесурса
«Угловое

Взаимодействие компонентов

script1adsense2code
script1adsense3code

В этом руководстве мы рассмотрим некоторые способы и сценарии, в которых компоненты взаимодействуют друг с другом, передавая данные по мере их взаимодействия. Мы также рассмотрим некоторые способы передачи этих данных.

Передача данных от родителя к потомку с привязкой ввода

В этом примере HeroChildComponent имеет два входных свойства , обычно украшенных украшениями @Input.

Код TypeScript:

import { Component, Input } 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() hero: Hero;
  @Input('master') masterName: string;
}

Демонстрация в реальном времени:

См. Раздел «Передача данных пером» от родителя к потомку с привязкой ввода по w3resource ( @ w3resource ) в CodePen .


В приведенном выше примере второй @Input псевдоним другого свойства дочернего компонента с именем `masterName` как` master`.

В следующем примере HeroParentComponent вкладывает дочерний HeroChildComponent в репитер * ngFor, связывая его строковое свойство master с псевдонимом master, а экземпляр героя каждой итерации со свойством hero.

Код TypeScript:

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';
}

Демонстрация в реальном времени:

См. Раздел «Передача данных пером от родителя к потомку» с привязкой ввода 2 с помощью w3resource ( @ w3resource ) в CodePen .


Перехватывать изменения входных свойств с помощью установщика

Установщик входного свойства может использоваться для перехвата и воздействия на значение от родителя. Например, в приведенном ниже примере установщик свойства ввода имени в дочернем элементе NameChildComponent удаляет пробел из имени и заменяет пустое значение текстом по умолчанию.

Код TypeScript:

import { Component, Input } from '@angular/core';
@Component({
  selector: 'app-name-child',
  template: '<h3>"{{name}}"</h3>'
})
export class NameChildComponent {
  private _name = '';
 
  @Input()
  set name(name: string) {
    this._name = (name && name.trim()) || '<no name set>';
  }
 
  get name(): string { return this._name; }
}

Демонстрация в реальном времени:

Посмотрите изменения входного свойства перехвата пера с помощью установщика w3resource ( @ w3resource ) на CodePen .


Вот NameParentComponent, демонстрирующий изменения имени, включая имя со всеми пробелами:

Код TypeScript:

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 'Mr. IQ', '<no name set>', 'Bombasto'
  names = ['Mr. IQ', '   ', '  Bombasto  '];
}

Демонстрация в реальном времени:

Посмотрите изменения входного свойства перехвата пера с помощью установщика 2 от w3resource ( @ w3resource ) на CodePen .


Перехватывать изменения входных свойств с помощью ngOnChanges ()

Мы можем отслеживать, обнаруживать и реагировать на изменения значения входного свойства с помощью метода ngOnChanges () интерфейса ловушки жизненного цикла OnChanges.

Этот подход часто предпочтительнее установщика свойств при просмотре нескольких взаимодействующих входных свойств.

Чтобы проиллюстрировать это, VersionChildComponent обнаруживает изменения основных и второстепенных свойств ввода и создает сообщение журнала, в котором сообщается об этих изменениях, если таковые имеются.

Код TypeScript:

import { Component, Input, OnChanges, SimpleChange } from '@angular/core';
@Component({
  selector: 'app-version-child',
  template: `
    <h3>Version {{major}}.{{minor}}</h3>
    <h4>Change log:</h4>
    <ul>
      <li *ngFor="let change of changeLog">{{change}} </li>
    </ul>
  `
})
export class VersionChildComponent implements OnChanges {
  @Input() major: number;
  @Input() minor: number;
  changeLog: string [] = [];
 
  ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
    let log: string[] = [];
    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(', '));
  }
}

Демонстрация в реальном времени:

См. Изменения входного свойства перехвата пера с помощью ngOnChanges () от w3resource ( @ w3resource ) на CodePen .


VersionParentComponent предоставляет значения `minor` и` major` и привязывает кнопки к методам, которые их изменяют.

Код TypeScript:

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;
  }
}
 

Демонстрация в реальном времени:

Посмотрите изменения входного свойства перехвата пера с помощью ngOnChanges () 2 от w3resource ( @ w3resource ) на CodePen .


Родитель слушает событие ребенка

Дочерний компонент предоставляет свойство EventEmitter, с которым он генерирует события, когда что-то происходит. Родитель связывается с этим свойством события и реагирует на эти события.

Свойство EventEmitter дочернего объекта является выходным свойством, обычно украшенным украшением @Output, как показано в этом VoterComponent:

Код TypeScript:

import { Component, EventEmitter, Input, Output } 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() name: string;
  @Output() voted = new EventEmitter<boolean>();
  didVote = false;
 
  vote(agreed: boolean) {
    this.voted.emit(agreed);
    this.didVote = true;
  }
}

Демонстрация в реальном времени:

Посмотрите, как родительский родитель прослушивает дочернее событие от w3resource ( @ w3resource ) на CodePen .


Нажатие на кнопку запускает выдачу значения true или false, булевой полезной нагрузки. Родительский VoteTakerComponent связывает обработчик события onVoted (), который отвечает на событие $ payload дочернего события и обновляет счетчик.

Код TypeScript:

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 = ['Mr. IQ', 'Ms. Universe', 'Bombasto'];
 
  onVoted(agreed: boolean) {
    agreed ? this.agreed++ : this.disagreed++;
  }
}

Демонстрация в реальном времени:

Посмотрите, как родительский родитель прослушивает дочернее событие от w3resource ( @ w3resource ) на CodePen .


Каркас передает аргумент события, представленный в $ event, методу-обработчику, и метод обрабатывает его:

Родитель взаимодействует с ребенком через локальную переменную

Родительский компонент не может использовать привязку данных для чтения дочерних свойств или вызова дочерних методов. Вы можете сделать и то и другое, создав переменную ссылки на шаблон для дочернего элемента, а затем сослаться на эту переменную в родительском шаблоне, как показано в следующем примере.

Следующее является дочерним CountdownTimerComponent, который многократно ведет обратный отсчет до нуля и запускает ракету. Он имеет методы запуска и остановки, которые контролируют часы, и отображает сообщение о состоянии обратного отсчета в своем собственном шаблоне.

Код TypeScript:

import { Component, OnDestroy, OnInit } from '@angular/core';
@Component({
  selector: 'app-countdown-timer',
  template: '<p>{{message}}</p>'
})
export class CountdownTimerComponent implements OnInit, OnDestroy {
  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);
  }
}

Демонстрация в реальном времени:

Посмотрите, как родительский элемент Pen взаимодействует с дочерним элементом через локальную переменную с помощью w3resource ( @ w3resource ) в CodePen .


CountdownLocalVarParentComponent, в котором размещается компонент таймера, выглядит следующим образом:

Код TypeScript:

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 { }

Демонстрация в реальном времени:

Посмотрите, как родительский элемент Pen взаимодействует с дочерним элементом через локальную переменную с помощью w3resource ( @ w3resource ) в CodePen .


Родительский компонент не может привязывать данные ни к методам start и stop, ни к свойству секунд.

Вы можете поместить локальную переменную #timer в тег <countdown-timer>, представляющий дочерний компонент. Это дает вам ссылку на дочерний компонент и возможность доступа к любым его свойствам или методам из родительского шаблона.

В этом примере родительские кнопки связываются со стартом и остановом дочернего элемента и используют интерполяцию для отображения свойства секунд дочернего элемента.

Здесь мы видим, как родитель и ребенок работают вместе.

Родитель вызывает @ViewChild ()

Подход локальной переменной прост и легок. Но это ограничено, потому что соединение родитель-потомок должно быть сделано полностью в родительском шаблоне. Сам родительский компонент не имеет доступа к дочернему.

Вы не можете использовать локальную переменную технику , если экземпляр класса компонента родительского должен считывать или записывать значение компонентов ребенка или должен вызывать методы компоненты ребенка.

Когда класс родительского компонента требует такого доступа, внедрите дочерний компонент в родительский как ViewChild .

Следующий пример иллюстрирует эту технику с тем же примером таймера обратного отсчета. Ни его внешний вид, ни его поведение не изменится. Дочерний элемент CountdownTimerComponent такой же.

Переход от локальной переменной к технике ViewChild предназначен исключительно для демонстрации.

Вот родительский элемент, CountdownViewChildParentComponent:

Код TypeScript:

import { AfterViewInit, ViewChild } from '@angular/core';
import { Component }                from '@angular/core';
import { CountdownTimerComponent }  from './countdown-timer.component';
 
@Component({
  selector: 'app-countdown-parent-vc',
  template: `
  <h3>Countdown to Liftoff (via ViewChild)</h3>
  <button (click)="start()">Start</button>
  <button (click)="stop()">Stop</button>
  <div class="seconds">{{ seconds() }}</div>
  <app-countdown-timer></app-countdown-timer>
  `,
  styleUrls: ['../assets/demo.css']
})
export class CountdownViewChildParentComponent implements AfterViewInit {
 
  @ViewChild(CountdownTimerComponent)
  private timerComponent: CountdownTimerComponent;
 
  seconds() { return 0; }
 
  ngAfterViewInit() {
    // Redefine `seconds()` to get from the `CountdownTimerComponent.seconds` ...
    // but wait a tick first to avoid one-time devMode
    // unidirectional-data-flow-violation error
    setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
  }
 
  start() { this.timerComponent.start(); }
  stop() { this.timerComponent.stop(); }
}

Демонстрация в реальном времени:

Посмотрите, как родитель Pen вызывает функцию @ViewChild () от w3resource ( @ w3resource ) на CodePen .


Требуется немного больше работы, чтобы получить дочернее представление в класс родительского компонента.

Сначала необходимо импортировать ссылки на декоратор ViewChild и хук жизненного цикла AfterViewInit.

Затем вставьте дочерний CountdownTimerComponent в частное свойство timerComponent с помощью украшения @ViewChildproperty.

Локальная переменная #timer ушла из метаданных компонента. Вместо этого свяжите кнопки с собственными методами запуска и остановки родительского компонента и представьте тикающие секунды в интерполяции вокруг метода секунд родительского компонента.

Эти методы напрямую обращаются к внедренному компоненту таймера.

Хук жизненного цикла ngAfterViewInit () - важная проблема. Компонент таймер не доступен , пока Угловая отображает родительский вид. Итак, изначально отображается 0 секунд.

Затем Angular вызывает ловушку жизненного цикла ngAfterViewInit, когда уже слишком поздно обновлять отображение обратного отсчета в родительском представлении. Однонаправленное правило потока данных Angular предотвращает обновление родительского представления в том же цикле. Приложение должно подождать один ход, прежде чем оно сможет отображать секунды.

Используйте setTimeout (), чтобы подождать один тик, а затем пересмотрите метод seconds (), чтобы он брал будущие значения из компонента таймера.

Родитель и дети общаются через сервис

Родительский компонент и его дети совместно используют службу, интерфейс которой обеспечивает двунаправленное общение в семье .

Область действия экземпляра службы - родительский компонент и его дочерние элементы. Компоненты вне этого поддерева компонентов не имеют доступа к сервису или их связи.

Этот сервис MissionService соединяет компонент MissionControlComponent с несколькими дочерними компонентами AstronautComponent.

Код TypeScript:

import { Injectable } from '@angular/core';
import { Subject }    from 'rxjs';
 
@Injectable()
export class MissionService {
 
  // Observable string sources
  private missionAnnouncedSource = new Subject<string>();
  private missionConfirmedSource = new Subject<string>();
 
  // Observable string streams
  missionAnnounced$ = this.missionAnnouncedSource.asObservable();
  missionConfirmed$ = this.missionConfirmedSource.asObservable();
 
  // Service message commands
  announceMission(mission: string) {
    this.missionAnnouncedSource.next(mission);
  }
 
  confirmMission(astronaut: string) {
    this.missionConfirmedSource.next(astronaut);
  }
}

Демонстрация в реальном времени:

Посмотрите взаимодействие компонента Pen с помощью w3resource ( @ w3resource ) на CodePen .


MissionControlComponent предоставляет экземпляр службы, которой он делится со своими дочерними элементами (через массив метаданных провайдеров), и внедряет этот экземпляр в себя через свой конструктор:

Код TypeScript:

import { Component }          from '@angular/core';
 
import { MissionService }     from './mission.service';
 
@Component({
  selector: 'app-mission-control',
  template: `
    <h2>Mission Control</h2>
    <button (click)="announce()">Announce mission</button>
    <app-astronaut *ngFor="let astronaut of astronauts"
      [astronaut]="astronaut">
    </app-astronaut>
    <h3>History</h3>
    <ul>
      <li *ngFor="let event of history">{{event}}</li>
    </ul>
  `,
  providers: [MissionService]
})
export class MissionControlComponent {
  astronauts = ['Lovell', 'Swigert', 'Haise'];
  history: string[] = [];
  missions = ['Fly to the moon!',
              'Fly to mars!',
              'Fly to Vegas!'];
  nextMission = 0;
 
  constructor(private missionService: MissionService) {
    missionService.missionConfirmed$.subscribe(
      astronaut => {
        this.history.push(`${astronaut} confirmed the mission`);
      });
  }
 
  announce() {
    let mission = this.missions[this.nextMission++];
    this.missionService.announceMission(mission);
    this.history.push(`Mission "${mission}" announced`);
    if (this.nextMission >= this.missions.length) { this.nextMission = 0; }
  }
}

Демонстрация в реальном времени:

Посмотрите взаимодействие компонента Pen по w3resource ( @ w3resource ) на CodePen .


AstronautComponent также внедряет сервис в своем конструкторе. Каждый AstronautComponent является дочерним по отношению к MissionControlComponent и поэтому получает экземпляр службы своего родителя:

Код TypeScript:

import { Component, Input, OnDestroy } from '@angular/core';
 
import { MissionService } from './mission.service';
import { Subscription }   from 'rxjs';
 
@Component({
  selector: 'app-astronaut',
  template: `
    <p>
      {{astronaut}}: <strong>{{mission}}</strong>
      <button
        (click)="confirm()"
        [disabled]="!announced || confirmed">
        Confirm
      </button>
    </p>
  `
})
export class AstronautComponent implements OnDestroy {
  @Input() astronaut: string;
  mission ='<no mission announced>';
  confirmed = false;
  announced = false;
  subscription: Subscription;
 
  constructor(private missionService: MissionService) {
    this.subscription = missionService.missionAnnounced$.subscribe(
      mission => {
        this.mission = mission;
        this.announced = true;
        this.confirmed = false;
    });
  }
 
  confirm() {
    this.confirmed = true;
    this.missionService.confirmMission(this.astronaut);
  }
 
  ngOnDestroy() {
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();
  }
}

Демонстрация в реальном времени:

См. Взаимодействие компонентов пера с помощью w3resource ( @ w3resource ) в CodePen .


Обратите внимание, что в этом примере фиксируются подписка и метод unsubscribe () при уничтожении компонента AstronautComponent. Это шаг защиты от утечки памяти. В этом приложении нет никакого реального риска, потому что время жизни AstronautComponent совпадает со временем жизни самого приложения. Это не всегда будет верно в более сложном приложении.

Вы не добавляете эту охрану в MissionControlComponent, потому что, как родитель, он контролирует время жизни MissionService.

Журнал истории показывает, что сообщения перемещаются в обоих направлениях между родительским элементом MissionControlComponent и дочерними элементами AstronautComponent, чему способствует служба.

Предыдущая: Синтаксис шаблона
Далее: Обзор хуков жизненного цикла компонентов

Новый контент: Composer: менеджер зависимостей для PHP , R программирования


script1adsense4code
script1adsense5code
disqus2code
script1adsense6code
script1adsense7code
script1adsense8code
buysellads2code