Suche…


Einführung

Teilen Sie Informationen zwischen verschiedenen Richtlinien und Komponenten.

Übergabe der Daten von einem Elternteil an ein Kind mit Eingabe

HeroChildComponent verfügt über zwei Eingabeeigenschaften, die normalerweise mit @Input-Dekorationen versehen sind.

import { Component, Input } from '@angular/core';
import { Hero } from './hero';
@Component({
  selector: '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;
}

Änderungen der Eingabeeigenschaften mit einem Setter abfangen

Verwenden Sie einen Eingabeeigenschaften-Setter, um einen Wert vom übergeordneten Element abzufangen und darauf zu reagieren.

Der Setter der Namenseingangseigenschaft in der untergeordneten NameChildComponent schneidet den Leerraum aus einem Namen ab und ersetzt einen leeren Wert durch den Standardtext.

import { Component, Input } from '@angular/core';
@Component({
  selector: '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; }
}

Hier ist die NameParentComponent, die Namensvariationen zeigt, einschließlich eines Namens mit allen Leerzeichen:

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

Elternteil hört auf Kindereignis

Die untergeordnete Komponente macht eine EventEmitter-Eigenschaft verfügbar, mit der Ereignisse ausgelöst werden, wenn etwas passiert. Das übergeordnete Element bindet an diese Ereigniseigenschaft und reagiert auf diese Ereignisse.

Die EventEmitter-Eigenschaft des Kindes ist eine Ausgabeeigenschaft, die normalerweise mit einer @Output-Verzierung versehen ist, wie in dieser VoterComponent zu sehen ist:

import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
  selector: 'my-voter',
  template: `
    <h4>{{name}}</h4>
    <button (click)="vote(true)"  [disabled]="voted">Agree</button>
    <button (click)="vote(false)" [disabled]="voted">Disagree</button>
  `
})
export class VoterComponent {
  @Input()  name: string;
  @Output() onVoted = new EventEmitter<boolean>();
  voted = false;
  vote(agreed: boolean) {
    this.onVoted.emit(agreed);
    this.voted = true;
  }
}

Durch Klicken auf eine Schaltfläche wird eine wahr oder falsch ausgegeben (die boolesche Nutzlast).

Die übergeordnete VoteTakerComponent bindet einen Ereignishandler (onVoted), der auf die untergeordneten Ereignisnutzdaten ($ event) reagiert und einen Leistungsindikator aktualisiert.

import { Component }      from '@angular/core';
@Component({
  selector: 'vote-taker',
  template: `
    <h2>Should mankind colonize the Universe?</h2>
    <h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3>
    <my-voter *ngFor="let voter of voters"
      [name]="voter"
      (onVoted)="onVoted($event)">
    </my-voter>
  `
})
export class VoteTakerComponent {
  agreed = 0;
  disagreed = 0;
  voters = ['Mr. IQ', 'Ms. Universe', 'Bombasto'];
  onVoted(agreed: boolean) {
    agreed ? this.agreed++ : this.disagreed++;
  }
}

Das Elternteil interagiert mit dem Kind über die lokale Variable

Eine übergeordnete Komponente kann keine Datenbindung verwenden, um untergeordnete Eigenschaften zu lesen oder untergeordnete Methoden aufzurufen. Beides können Sie tun, indem Sie eine Vorlagenreferenzvariable für das untergeordnete Element erstellen und dann auf diese Variable innerhalb der übergeordneten Vorlage verweisen, wie im folgenden Beispiel gezeigt.

Wir haben eine untergeordnete CountdownTimerComponent, die wiederholt auf null herunter zählt und eine Rakete startet. Es verfügt über Start- und Stopp-Methoden, die die Uhr steuern, und es wird eine Countdown-Statusmeldung in einer eigenen Vorlage angezeigt.

import { Component, OnDestroy, OnInit } from '@angular/core';
@Component({
  selector: '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);
  }
}

Sehen wir uns die CountdownLocalVarParentComponent an, die die Timer-Komponente hostet.

import { Component }                from '@angular/core';
import { CountdownTimerComponent }  from './countdown-timer.component';
@Component({
  selector: '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>
  <countdown-timer #timer></countdown-timer>
  `,
  styleUrls: ['demo.css']
})
export class CountdownLocalVarParentComponent { }

Die übergeordnete Komponente kann weder an die Start- und Stop-Methoden des Kindes noch an die Sekunden-Eigenschaft der Daten binden.

Wir können eine lokale Variable (#timer) auf das Tag () setzen, das die untergeordnete Komponente darstellt. Dies gibt uns einen Verweis auf die untergeordnete Komponente selbst und die Möglichkeit, innerhalb der übergeordneten Vorlage auf ihre Eigenschaften oder Methoden zuzugreifen.

In diesem Beispiel verbinden wir übergeordnete Schaltflächen mit dem Start und Stopp des Kindes und verwenden die Interpolation, um die Sekunden-Eigenschaft des Kindes anzuzeigen.

Hier sehen wir, wie Eltern und Kind zusammenarbeiten.

Übergeordnete ruft eine ViewChild auf

Der Ansatz der lokalen Variablen ist einfach und leicht. Dies ist jedoch begrenzt, da die Parent-Child-Verkabelung vollständig innerhalb der Parent-Vorlage erfolgen muss. Die übergeordnete Komponente selbst hat keinen Zugriff auf das untergeordnete Element.

Die lokale Variablentechnik kann nicht verwendet werden, wenn eine Instanz der übergeordneten Komponentenklasse untergeordnete Komponentenwerte lesen oder schreiben oder untergeordnete Komponentenmethoden aufrufen muss.

Wenn die übergeordnete Komponentenklasse diese Art von Zugriff erfordert, injizieren wir die untergeordnete Komponente als ViewChild in die übergeordnete Komponente.

Wir werden diese Technik anhand des gleichen Countdown-Timers veranschaulichen. Wir werden sein Aussehen oder Verhalten nicht ändern. Die untergeordnete CountdownTimerComponent ist ebenfalls dieselbe.

Wir wechseln ausschließlich zu Demonstrationszwecken von der lokalen Variablen zur ViewChild-Technik. Hier ist das übergeordnete CountdownViewChildParentComponent:

import { AfterViewInit, ViewChild } from '@angular/core';
import { Component }                from '@angular/core';
import { CountdownTimerComponent }  from './countdown-timer.component';
@Component({
  selector: '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>
  <countdown-timer></countdown-timer>
  `,
  styleUrls: ['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(); }
}

Es dauert etwas mehr Arbeit, um die untergeordnete Ansicht in die übergeordnete Komponentenklasse zu bringen.

Wir importieren Verweise auf den ViewChild-Dekorator und den Lebenszyklus-Hook AfterViewInit.

Wir injizieren die untergeordnete CountdownTimerComponent-Eigenschaft über die @ViewChild-Eigenschaftendekoration in die private timerComponent-Eigenschaft.

Die lokale Variable #timer wurde aus den Komponenten-Metadaten entfernt. Stattdessen binden wir die Schaltflächen an die Start- und Stop-Methoden der übergeordneten Komponente und präsentieren die tickenden Sekunden in einer Interpolation um die Sekunden-Methode der übergeordneten Komponente.

Diese Methoden greifen direkt auf die eingespritzte Zeitgeberkomponente zu.

Der ngAfterViewInit-Lebenszyklus-Hook ist eine wichtige Falte. Die Timerkomponente ist erst verfügbar, nachdem Angular die übergeordnete Ansicht angezeigt hat. Wir zeigen also zunächst 0 Sekunden an.

Dann ruft Angular den Lebenszyklus-Hook ngAfterViewInit auf. Zu diesem Zeitpunkt ist es zu spät, um die Anzeige der Countdown-Sekunden in der übergeordneten Ansicht zu aktualisieren. Die unidirektionale Datenflussregel von Angular verhindert, dass wir die übergeordneten Ansichten im selben Zyklus aktualisieren. Wir müssen eine Runde warten, bevor wir die Sekunden anzeigen können.

Wir verwenden setTimeout, um einen Tick zu warten, und überarbeiten die Sekunden-Methode, sodass zukünftige Werte aus der Timerkomponente übernommen werden.

Eltern und Kinder kommunizieren über einen Dienst

Eine übergeordnete Komponente und ihre untergeordneten Elemente teilen sich einen Dienst, dessen Schnittstelle die bidirektionale Kommunikation innerhalb der Familie ermöglicht.

Der Umfang der Dienstinstanz ist die übergeordnete Komponente und ihre untergeordneten Elemente. Komponenten außerhalb dieser Komponenten-Unterstruktur haben keinen Zugriff auf den Dienst oder deren Kommunikation.

Dieser MissionService verbindet die MissionControlComponent mit mehreren AstronautComponent-untergeordneten Objekten.

import { Injectable } from '@angular/core';
import { Subject }    from 'rxjs/Subject';
@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);
  }
}

Die MissionControlComponent stellt die Instanz des Dienstes bereit, die sie mit ihren untergeordneten Objekten (über das Metadaten-Array des Providers) gemeinsam nutzt, und fügt diese Instanz über ihren Konstruktor in sich ein:

import { Component }          from '@angular/core';
import { MissionService }     from './mission.service';
@Component({
  selector: 'mission-control',
  template: `
    <h2>Mission Control</h2>
    <button (click)="announce()">Announce mission</button>
    <my-astronaut *ngFor="let astronaut of astronauts"
      [astronaut]="astronaut">
    </my-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; }
  }
}

Die AstronautComponent fügt den Dienst auch in den Konstruktor ein. Jede AstronautComponent ist ein untergeordnetes Element der MissionControlComponent und empfängt daher die Dienstinstanz ihres übergeordneten Objekts:

import { Component, Input, OnDestroy } from '@angular/core';
import { MissionService } from './mission.service';
import { Subscription }   from 'rxjs/Subscription';
@Component({
  selector: 'my-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();
  }
}

Beachten Sie, dass wir das Abonnement erfassen und das Abonnement abbestellen, wenn die AstronautComponent zerstört wird. Dies ist ein Schritt zum Schutz vor Speicherlecks. In dieser App gibt es kein tatsächliches Risiko, da die Lebensdauer einer AstronautComponent der Lebensdauer der App selbst entspricht. Das wäre in einer komplexeren Anwendung nicht immer der Fall.

Wir fügen diesen Schutz nicht zur MissionControlComponent hinzu, da er als übergeordnetes Element die Lebensdauer des MissionService steuert. Das Verlaufsprotokoll zeigt, dass Nachrichten in beide Richtungen zwischen der übergeordneten MissionControlComponent-Komponente und der untergeordneten AstronautComponent-Komponente gesendet werden, was durch den Dienst erleichtert wird:



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow