navigation Navigation


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.

    child.component.ts
    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.

    child.component.html
    <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.

    parent.component.ts
    @Component({ ... })
    export class ParentComponent {
        localCounterVariable = 0;
    
        handleChildEmittedEvent() {
            this.localCounterVariable++;
            console.log('Child emitted event/value');
        }
    }

    Und im Template wird entsprechend das Child-Component eingesetzt.

    parent.component.html
    <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.

    child.component.ts
    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.

    parent.component.ts
    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.

    parent.component.html
    <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.

    child.component.ts
    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.

    child.component.ts
    @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.

    child.component.ts
    @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.

    child.component.ts
    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.

    parent.component.ts
    @Component({ ... })
    export class ParentComponent {
        handleClassicOutput() {
            console.log('Child component emitted event');
        }
    }

    Und im Template des Parent-Components sollte irgendwo das Child-Component verwendet werden.

    parent.component.html
    <app-output-classic (childEventTriggered)="handleClassicOutput()"></app-output-classic>

    Angular Components - Output klassische Variante