Data Output
Mit der modernen Angular API gibt es eine neue Möglichkeit, Outputs zu definieren: die output()
Funktion.
Component Outputs ermöglichen es einer Komponente, Ereignisse an ihre Elternkomponente zu senden. Sie implementieren ein Event-basiertes Kommunikationsmodell von “unten nach oben” in der Komponentenhierarchie.
Inhaltsverzeichnis
Funktion output()
Die output()
Funktion ist ein Teil der modernen Angular API und bietet eine Alternative zur Verwendung des traditionellen @Output()
Dekorators mit EventEmitter
.
Beispiel - Einfache Verwendung (ohne Daten)
Im nächsten Beispiel wird eine Kommunikationsbrücke zwischen einer Eltern- und einer Kind-Komponente aufgebaut.
In der Kind-Komponente wird ein Output definiert, welcher der Eltern-Komponente mitteilt, dass irgendein Ereignis in der Kind-Komponente passiert ist.
import { Component, input, output } from '@angular/core';
@Component({
selector: 'app-child-component',
imports: [],
templateUrl: './child-component.component.html',
styleUrl: './child-component.component.scss'
})
export class ChildComponent {
buttonLabel = input<string>('');
buttonClicked = output<void>();
handleButtonClick() {
this.buttonClicked.emit();
}
}
Zusätzlich zu buttonClicked
als Output wurde buttonLabel
als ein Input definiert, wobei das Setzen dieser Variable (dieses Inputs) nicht notwendig ist, da im Template mit einem Standardwert gearbeitet wird.
<button (click)="handleButtonClick()">
{{ buttonLabel() || 'Klick mich' }}
</button>
Mit dieser Definition wird ein Signal nach außen (zu allen Components, die Eltern dieses Components sind) emittiert.
Im Parent-Component wird eine Methode benötigt, die auf die Änderung (einen emittierten Wert oder Signal) im Child-Component reagiert. Ein sogenannter Handler wird benötigt.
@Component({ ... })
export class ParentComponent {
localCounterVariable = 0;
handleChildEmittedEvent() {
this.localCounterVariable++;
console.log('Child emitted event/value');
}
}
Und im Template wird entsprechend das Child-Component eingesetzt.
<app-child-component
[buttonLabel]="'Klick mich (Custom label)'"
(buttonClicked)="handleChildEmittedEvent()"
></app-child-component>
Beispiel - Output mit Werten
Wenn man bestimmte Werte vom Child-Component zum Parent-Component providen möchte, kann man es ebenfalls tun.
Im folgenden Beispiel wird ein String in ein Child-Component hineingereicht, dort modifiert und dann zum Parent-Component das modifizierte Ergebnis übermittelt.
import { Component, output, input, OnInit } from '@angular/core';
@Component({
selector: 'app-child',
imports: [],
templateUrl: './child.component.html',
styleUrl: './child.component.scss'
})
export class ChildComponent implements OnInit {
inputString = input<string>('');
outputString = output<string>();
ngOnInit(): void {
this.emitTransformedString();
}
emitTransformedString() {
const newString = this.inputString().split("").join("*");
this.outputString.emit(newString);
}
}
Mittels inputString
wird eine Zeichenkette angenommen und über outputString
zurück zum Parent-Component übergeben. Beide Variablen wurden vom Typ String definiert.
Dieses Component startet die Arbeit, sobald es initialisiert wird.
Das Template des Child-Components ist in diesem Fall nicht von Bedeutung, da uns lediglich der Wert interessiert, welcher zurück das Parent-Component gesendet wird.
Im Parent-Component benötigen wir in diesem Beispiel ein paar Methoden und ein paar Variablen.
import { Component, output, input } from '@angular/core';
import { ChildComponent } from './child.component';
@Component({
selector: 'app-parent',
imports: [],
templateUrl: './parent.component.html',
styleUrl: './parent.component.scss'
})
export class ParentComponent {
transformInputValue = '';
stringTransformerEnabled = false;
resultString = '';
enableStringTransformer() {
this.stringTransformerEnabled = true;
}
showTransformedString(value: string) {
this.resultString = value;
}
onInputChange(event: Event) {
const input = event.target as HTMLInputElement;
this.transformInputValue = input.value;
}
}
Und das dazugehörige Parent-Template sieht dann mit der Verwendung des Child-Components wie folgt aus.
<input
type="text"
style="margin-right: 10px"
[value]="transformInputValue"
(input)="onInputChange($event)"
>
<button (click)="enableStringTransformer()">
Transform string
</button>
@if (stringTransformerEnabled) {
<hr>
<app-child
[inputString]="transformInputValue"
(outputString)="showTransformedString($event)"
></app-child>
<hr>
Result: {{ resultString }}
}
Automatische Emits mit Signals
Mit Angulars neuen Primitiven ist es möglich, Ereignisse automatisch basierend auf Zustandsänderungen, mit signal
auszulösen, ohne explizit eine Methode zum Auslösen des Events zu definieren.
import {
Component,
effect,
output,
signal
} from '@angular/core';
@Component({
selector: 'app-child',
template: `
<button (click)="increment()">Add</button>
<p>Aktueller Wert: {{ counter() }}</p>
`
})
export class ChildComponent {
private counter = signal<number>(0);
readonly counterChange = output<number>();
constructor() {
effect(() => {
this.counterChange.emit(this.counter());
})
}
increment(): void {
this.counter.update(value => value + 1);
}
}
Aliase für output
Man hat die Möglichkeit eigene Namen für die Output-Variablen zu vergeben. Dabei wird lediglich der Name geändert, welcher bei der Verwendung des Components zum Einsatz kommt.
@Component({ ... })
export class ChildComponent {
changed = output({ alias: 'volumeChanged' });
}
Im Parent-Component würde man dann volumeChanged
verwendet und nicht changed
. In TypeScript wird weiterhin changed
verwendet.
@Component({ ... })
export class ChildComponent {
changed = output({ alias: 'volumeChanged' });
emitState() {
this.changed.emit();
}
}
@Output
- klassisch
Nach wie vor gibt es die Möglichkeit die klassische Variante @Output
zu verwenden, um die Kommunikation von Child-Component zum Parent-Component aufzubauen.
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-output-classic',
imports: [],
templateUrl: './output-classic.component.html',
styleUrl: './output-classic.component.scss'
})
export class OutputClassicComponent {
@Output() childEventTriggered = new EventEmitter<void>();
emitChangeEvent() {
this.childEventTriggered.emit();
}
}
Die Verwendung im Parent-Component ist identisch mit der Verwendung bei Einsatz der modernen output()
Funktion.
@Component({ ... })
export class ParentComponent {
handleClassicOutput() {
console.log('Child component emitted event');
}
}
Und im Template des Parent-Components sollte irgendwo das Child-Component verwendet werden.
<app-output-classic (childEventTriggered)="handleClassicOutput()"></app-output-classic>