Angular @ViewChild: Zugriff auf Child Components, Direktiven und DOM-Elemente
@ViewChild ist ein Angular-Decorator, mit dem sich das Template einer Komponente durchsuchen und die erste passende Child Component, Direktive oder ein natives DOM-Element ermitteln lässt. Dadurch kann die Klasse einer Parent Component programmatisch auf Elemente ihrer eigenen View zugreifen, ohne klassische DOM-Abfragen zu verwenden oder Ereignisse manuell weiterzugeben.
Dieser Leitfaden zeigt, wie @ViewChild innerhalb einer Parent Component verwendet werden kann, um auf eine Child Component, eine Direktive oder ein DOM-Element zuzugreifen. Zusätzlich werden die Optionen static und read, @ViewChildren zusammen mit QueryList sowie die mit Angular 17 eingeführten signalbasierten View Queries behandelt.
Die wichtigsten Punkte zu Angular @ViewChild
@ViewChildliefert die erste passende Child Component, Direktive oder das erste entsprechende DOM-Element aus dem eigenen Template einer Komponente.- Standardmäßig steht das Ergebnis in
ngAfterViewInitzur Verfügung.static: truesollte nur verwendet werden, wenn sich ein Element niemals innerhalb einer strukturellen Direktive befindet und bereits inngOnInitbenötigt wird. @ViewChildrenstellt eineQueryListmit allen passenden Elementen bereit. ÜberQueryList.changeskann auf dynamisch hinzugefügte oder entfernte Elemente reagiert werden.- Mit der Option
readlässt sich festlegen, welcher Wert von einer Query zurückgegeben wird. Dadurch können beispielsweise einElementRefoder eine bestimmte Direktiveninstanz eines gefundenen Elements abgerufen werden. - Die signalbasierten Funktionen
viewChild()undviewChildren()wurden mit Angular 17 als modernere Alternative eingeführt. Für neue Projekte mit Angular 17 oder einer neueren Version sind sie in der Regel vorzuziehen. - Direkte DOM-Manipulationen über
ElementRef.nativeElementsollten vermieden werden, wenn sich dieselbe Aufgabe über Angular-Bindings oderRenderer2umsetzen lässt.
Voraussetzungen
Bevor die folgenden Beispiele umgesetzt werden, sollte die Entwicklungsumgebung folgende Voraussetzungen erfüllen:
- Node.js 18.x oder neuer sowie eine installierte npm-Version
- Eine global installierte Angular CLI:
npm install -g @angular/cli
- Grundkenntnisse zu Angular Components und TypeScript-Decorators
Die Beispiele in diesem Leitfaden wurden mit @angular/core v17 und @angular/cli v17 überprüft.
Was ist @ViewChild und wofür wird es verwendet?
Angular Components kommunizieren in den meisten Fällen über @Input– und @Output-Bindings. Es gibt jedoch Situationen, in denen eine Parent Component eine Methode einer Child Component aufrufen, eine Eigenschaft einer Direktive an einem Template-Element auslesen oder direkt auf einen nativen DOM-Knoten zugreifen muss. Für solche Anwendungsfälle ist @ViewChild vorgesehen.
Ohne @ViewChild könnte ein vergleichbarer Zugriff DOM-Abfragen wie document.querySelector erforderlich machen. Solche direkten Abfragen umgehen die üblichen Change-Detection-Mechanismen von Angular und können die serverseitige Darstellung beeinträchtigen. @ViewChild ist dagegen in den Initialisierungsprozess der Angular View integriert und liefert eine typisierte Referenz, wodurch unsichere direkte DOM-Zugriffe reduziert werden.
So funktioniert das View-Query-System von Angular
Angular erstellt eine Komponente im Wesentlichen in zwei Phasen. In der ersten Phase wird die Komponentenklasse instanziiert und ngOnInit ausgeführt. Die im Template definierten Child-Elemente wurden zu diesem Zeitpunkt jedoch noch nicht vollständig erstellt. In der zweiten Phase initialisiert Angular die View, erzeugt Child Components, bindet Direktiven ein und fügt DOM-Knoten ein. Am Ende dieser zweiten Phase wird ngAfterViewInit ausgeführt. Deshalb stehen Ergebnisse von @ViewChild normalerweise in ngAfterViewInit und nicht bereits in ngOnInit zur Verfügung.
Bei static: false, dem Standardverhalten, wartet Angular bis zu dieser zweiten Phase, bevor die Query aufgelöst wird. Bei static: true wird die Query synchron nach dem ersten Change-Detection-Durchlauf aufgelöst. Dieser Zeitpunkt liegt vor ngAfterViewInit, nachdem das Template bereits kompiliert wurde. Praktische Beispiele für die Option static folgen in einem späteren Abschnitt.
Angular-Beispielprojekt einrichten
Angular-Anwendung erstellen
Erstelle zunächst einen neuen Angular-Workspace:
ng new viewchild-demo –no-standalone –routing=false –style=css
Output:
CREATE viewchild-demo/src/app/app.component.ts (219 bytes)
CREATE viewchild-demo/src/app/app.module.ts (314 bytes)
CREATE viewchild-demo/src/app/app.component.html (23115 bytes)
…
Wechsle anschließend in das neu angelegte Projektverzeichnis:
cd viewchild-demo
Child Component für die Beispiele erstellen
Erstelle eine Child Component, auf die in den folgenden Beispielen zugegriffen werden kann:
ng generate component pup –flat –skip-tests
Output:
CREATE src/app/pup.component.css (0 bytes)
CREATE src/app/pup.component.html (19 bytes)
CREATE src/app/pup.component.ts (188 bytes)
UPDATE src/app/app.module.ts (467 bytes)
Angular ViewChild mit Direktiven verwenden
Wird eine Direktive auf ein Element innerhalb eines Templates angewendet, erhält die Parent Component standardmäßig keine direkte Referenz auf die jeweilige Direktiveninstanz. Mit @ViewChild lässt sich gezielt nach der Direktivenklasse suchen und eine typisierte Referenz auf die Instanz innerhalb der aktuellen View zurückgeben.
Im folgenden Beispiel wird eine SharkDirective erstellt. Sie verwendet ein appShark-Attribut und fügt dem Text des Host-Elements das Wort "Shark" voran. Anschließend nutzt die Parent Component @ViewChild, um eine Eigenschaft dieser Direktiveninstanz auszulesen.
Erstelle die Direktive über @angular/cli:
ng generate directive shark –skip-tests
Der Befehl erstellt die Datei shark.directive.ts und registriert die Direktive in app.module.ts:
app.module.ts
import { SharkDirective } from ‚./shark.directive‘;
…
@NgModule({
declarations: [
AppComponent,
SharkDirective
],
…
})
Anschließend werden ElementRef und Renderer2 verwendet, um den angezeigten Text zu verändern. Ersetze den Inhalt von shark.directive.ts durch folgenden Code:
shark.directive.ts
import {
Directive,
ElementRef,
Renderer2
} from ‚@angular/core‘;
@Directive(
{ selector: ‚[appShark]‘ } // selects elements containing the appShark attribute
)
export class SharkDirective {
creature = ‚Dolphin‘; // property that can be read through @ViewChild
constructor(elem: ElementRef, renderer: Renderer2) {
let shark = renderer.createText(‚Shark ‚); // create the text node
renderer.appendChild(elem.nativeElement, shark); // attach it to the host element
}
}
Füge nun das Attribut appShark zu einem span-Element mit Textinhalt hinzu. Ersetze den Inhalt von app.component.html entsprechend:
app.component.html
Fin!
Wird die Anwendung im Browser geöffnet, erscheint "Shark" vor dem vorhandenen Inhalt des Elements:
Output
Shark Fin!
Zusätzlich kann die Eigenschaft creature aus SharkDirective ausgelesen und ihrem Wert eine Eigenschaft namens extraCreature zugewiesen werden. Ersetze dazu den Inhalt von app.component.ts durch folgenden Code:
app.component.ts
import {
Component,
ViewChild,
AfterViewInit
} from ‚@angular/core‘;
import { SharkDirective } from ‚./shark.directive‘;
@Component({
selector: ‚app-root‘,
templateUrl: ‚./app.component.html‘,
styleUrls: [‚./app.component.css‘]
})
export class AppComponent implements AfterViewInit {
extraCreature!: string;
@ViewChild(SharkDirective)
set appShark(directive: SharkDirective) {
// this setter runs when Angular resolves the @ViewChild query
this.extraCreature = directive.creature;
};
ngAfterViewInit() {
// the @ViewChild result is guaranteed to be available at this point
console.log(this.extraCreature); // Dolphin
}
}
In diesem Beispiel wird ein Setter verwendet, um die Eigenschaft extraCreature festzulegen. Auf den Wert wird erst im Lifecycle Hook AfterViewInit zugegriffen, da Child Components und Direktiven zu diesem Zeitpunkt verfügbar sind. Anstelle einer direkten Property-Deklaration kommt hier ein Setter zum Einsatz, weil er synchron ausgeführt wird, sobald Angular ein Query-Ergebnis zuweist. Das gilt auch dann, wenn die Direktiveninstanz während der Laufzeit ersetzt wird.
Im Browser wird weiterhin die Meldung "Shark Fin!" angezeigt. In der Konsole erscheint dagegen:
Output
Dolphin
Angular ViewChild mit DOM-Elementen verwenden
Damit ein natives DOM-Element über @ViewChild abgefragt werden kann, muss es zunächst über eine Template-Referenzvariable im Template gekennzeichnet werden. Eine solche lokale Referenz beginnt mit #. Angular verwendet diesen Namen anschließend als Selektor. Ohne eine entsprechende Referenz kann @ViewChild nicht bestimmen, welches Element aus dem Template zurückgegeben werden soll.
Im folgenden Beispiel wird einem <input>-Element die Referenz #someInput zugeordnet. Die Parent Component greift anschließend darauf zu und setzt den Wert des Elements programmatisch:
app.component.html
<input #someInput placeholder=“Your favorite sea creature“>
Das <input>-Element kann nun über ViewChild angesprochen und sein value geändert werden. Ersetze den Inhalt von app.component.ts durch folgenden Code:
app.component.ts
import {
Component,
ViewChild,
AfterViewInit,
ElementRef
} from ‚@angular/core‘;
@Component({
selector: ‚app-root‘,
templateUrl: ‚./app.component.html‘,
styleUrls: [‚./app.component.css‘]
})
export class AppComponent implements AfterViewInit {
@ViewChild(’someInput‘) someInput!: ElementRef;
ngAfterViewInit() {
// nativeElement provides the underlying HTMLInputElement
this.someInput.nativeElement.value = ‚Whale!‘;
}
}
Hinweis: Der direkte Zugriff auf nativeElement umgeht das Sicherheitsmodell von Angular und kann bei serverseitigem Rendering Probleme verursachen. Für DOM-Operationen in produktiven Anwendungen sollte Renderer2 injiziert und über dessen Methoden gearbeitet werden. Muss lediglich eine Eigenschaft gesetzt werden, sind Angular-Template-Bindings wie [value] oder [style] vorzuziehen.
Sobald ngAfterViewInit ausgeführt wird, erhält das <input>-Element folgenden Wert:
Output
Whale!
Angular ViewChild mit Child Components verwenden
@ViewChild kann einer Parent Component eine typisierte Referenz auf die Instanz einer Child Component bereitstellen. Über diese Referenz lassen sich öffentliche Methoden der Child Component aufrufen oder öffentliche Eigenschaften direkt auslesen, ohne dafür einen Event Emitter oder einen gemeinsamen Service einzusetzen. Dieses Muster eignet sich, wenn die Kommunikation ausschließlich von der Parent Component zur Child Component erfolgt und programmatisch statt durch ein Benutzerereignis ausgelöst wird.
Dieser Abschnitt verwendet die bereits zuvor erstellte PupComponent. Falls dieser Einrichtungsschritt übersprungen wurde, kann die Komponente jetzt erstellt werden:
ng generate component pup –flat –skip-tests
Der Befehl erstellt pup.component.ts, pup.component.css und pup.component.html und registriert die Komponente anschließend in app.module.ts:
app.module.ts
import { PupComponent } from ‚./pup.component‘;
…
@NgModule({
declarations: [
AppComponent,
PupComponent
],
…
})
Füge anschließend der PupComponent eine Methode namens whoAmI hinzu, die eine Nachricht zurückgibt:
pup.component.ts
import { Component, OnInit } from ‚@angular/core‘;
@Component({
selector: ‚app-pup‘,
templateUrl: ‚./pup.component.html‘,
styleUrls: [‚./pup.component.css‘]
})
export class PupComponent implements OnInit {
constructor() { }
whoAmI() {
return ‚I am a pup component!‘;
}
ngOnInit(): void {
}
}
Binde danach die Child Component in das Anwendungstemplate ein. Ersetze dazu den Inhalt von app.component.html durch:
app.component.html
pup works!
Die Parent Component kann nun die Methode whoAmI aufrufen, indem sie über ViewChild die Instanz der Child Component erhält. Ersetze den Inhalt von app.component.ts durch:
app.component.ts
import {
Component,
ViewChild,
AfterViewInit
} from ‚@angular/core‘;
import { PupComponent } from ‚./pup.component‘;
@Component({
selector: ‚app-root‘,
templateUrl: ‚./app.component.html‘,
styleUrls: [‚./app.component.css‘],
})
export class AppComponent implements AfterViewInit {
// typed reference pointing to the child component instance
@ViewChild(PupComponent) pup!: PupComponent;
ngAfterViewInit() {
// invoke a public method directly on the child component
console.log(this.pup.whoAmI()); // I am a pup component!
}
}
Wird die Anwendung ausgeführt, erscheint in der Browser-Konsole:
Output
I am a pup component!
Die static-Option von Angular @ViewChild verstehen
static: true im Vergleich zu static: false
Die Einstellung static legt fest, wann Angular eine @ViewChild-Query in Bezug auf die Change Detection auflöst.
| Option | Auflösung | Verfügbar in | Geeignet, wenn |
|---|---|---|---|
static: false (Standard) |
Nach dem ersten Change-Detection-Durchlauf | ngAfterViewInit |
Das Element befindet sich innerhalb von *ngIf, *ngFor oder einer anderen strukturellen Direktive |
static: true |
Vor dem ersten Change-Detection-Durchlauf | ngOnInit |
Das Element ist dauerhaft vorhanden und nicht von einer strukturellen Direktive umgeben |
Praktisches Beispiel für den Unterschied
Bei static: false, dem Standardwert, wird das Query-Ergebnis in ngAfterViewInit verwendet:
// app.component.ts
@ViewChild(‚myElement‘) myElement!: ElementRef; // static: false is used by default
ngOnInit() {
console.log(this.myElement); // undefined: the query has not been resolved yet
}
ngAfterViewInit() {
console.log(this.myElement); // ElementRef: resolved after view initialization
}
Wird static: true gesetzt, ist die Referenz bereits innerhalb von ngOnInit verfügbar:
// app.component.ts
@ViewChild(‚myElement‘, { static: true }) myElement!: ElementRef;
ngOnInit() {
console.log(this.myElement); // ElementRef: resolved before change detection
}
Für ein Element innerhalb einer strukturellen Direktive sollte static: true nicht verwendet werden. Angular kann eine solche Query nicht vor der Change Detection auflösen, wenn das entsprechende Element zu diesem Zeitpunkt möglicherweise noch nicht im DOM vorhanden ist.
Mit der read-Option den Rückgabetyp von @ViewChild ändern
Der standardmäßige Rückgabetyp einer @ViewChild-Query richtet sich danach, was Angular am gefundenen Element erkennt. Entspricht der Selektor einer Komponente, wird die Komponenteninstanz zurückgegeben. Wird eine Direktive gefunden, liefert Angular die Direktiveninstanz. Mit der Option read lässt sich dieses Verhalten überschreiben. Typische Einsatzfälle sind der Zugriff auf das zugrunde liegende ElementRef eines Elements, auf dem eine Komponente liegt, oder die Auswahl einer bestimmten Direktive, wenn mehrere Direktiven auf dasselbe Element angewendet werden.
Ein ElementRef über einen Komponenten-Selektor auslesen
Dieser Ansatz eignet sich, wenn anstelle der Komponenteninstanz das Host-DOM-Element benötigt wird, beispielsweise um dessen Abmessungen zu ermitteln oder einen nativen DOM-Knoten an eine externe Bibliothek zu übergeben.
// app.component.ts
// Without read, the query returns a PupComponent instance.
// With read: ElementRef, the query returns the component’s host DOM element.
@ViewChild(PupComponent, { read: ElementRef }) pupElement!: ElementRef;
ngAfterViewInit() {
console.log(this.pupElement.nativeElement.tagName); // APP-PUP
}
Eine Direktive von einem Element auslesen
Wenn auf demselben Element mehrere Direktiven verwendet werden, kann mit read bestimmt werden, welche Direktiveninstanz zurückgegeben werden soll.
Füge #myRef und appShark demselben Element in app.component.html hinzu:
app.component.html
Fin!
Anschließend lässt sich die auf diesem Element vorhandene Instanz von SharkDirective abfragen:
// app.component.ts
@ViewChild(‚myRef‘, { read: SharkDirective }) sharkDir!: SharkDirective;
ngAfterViewInit() {
console.log(this.sharkDir.creature); // Dolphin
}
Mehrere Elemente mit @ViewChildren und QueryList abfragen
@ViewChild liefert lediglich das erste passende Ergebnis. Wenn mehrere Elemente denselben Selektor verwenden und Referenzen auf alle Treffer benötigt werden, kommt @ViewChildren zum Einsatz.
Eine @ViewChildren-Query definieren
Platziere zwei <app-pup>-Komponenten im Template und frage beide gemeinsam ab:
app.component.html
<app-pup></app-pup>
<app-pup></app-pup>
app.component.ts
import {
Component,
ViewChildren,
AfterViewInit,
QueryList
} from ‚@angular/core‘;
import { PupComponent } from ‚./pup.component‘;
@Component({
selector: ‚app-root‘,
templateUrl: ‚./app.component.html‘,
styleUrls: [‚./app.component.css‘]
})
export class AppComponent implements AfterViewInit {
// contains every PupComponent instance found in the template
@ViewChildren(PupComponent) pups!: QueryList;
ngAfterViewInit() {
console.log(this.pups.length); // 2
}
}
Über eine QueryList iterieren
Mit .forEach() kann nach der Initialisierung synchron auf jede gefundene Instanz zugegriffen werden. Dies lässt sich beispielsweise nutzen, um für jedes Child eine Initialisierungsmethode aufzurufen oder einen anfänglichen Eigenschaftswert auszulesen:
ngAfterViewInit() {
this.pups.forEach((pup, index) => {
console.log(`Pup ${index}:`, pup.whoAmI());
});
}
Änderungen einer QueryList abonnieren
QueryList stellt ein Observable namens changes bereit. Dieses gibt eine neue QueryList aus, sobald Elemente zur gefundenen Menge hinzugefügt oder daraus entfernt werden. Das kann beispielsweise geschehen, wenn eine *ngIf-Bedingung eine Child Component in die View einfügt oder wieder entfernt. Beim ersten Rendern wird changes nicht ausgelöst; Ausgaben erfolgen erst bei späteren Änderungen.
ngAfterViewInit() {
// runs whenever a PupComponent enters or leaves the view
this.pups.changes.subscribe((list: QueryList) => {
console.log(‚Pup count:‘, list.length);
});
}
Kann die Komponente zerstört werden, während das Abonnement noch aktiv ist, sollte die Subscription gespeichert und in ngOnDestroy beendet werden, um ein Memory Leak zu vermeiden:
import { Subscription } from ‚rxjs‘;
private pupSub!: Subscription;
ngAfterViewInit() {
this.pupSub = this.pups.changes.subscribe((list: QueryList) => {
console.log(‚Pup count:‘, list.length);
});
}
ngOnDestroy() {
this.pupSub.unsubscribe();
}
Signalbasierte View Queries in Angular 17+
Mit Angular 17 wurden viewChild() und viewChildren() als signalbasierte Alternativen zu @ViewChild und @ViewChildren eingeführt.
viewChild() im Vergleich zu @ViewChild
| Merkmal | @ViewChild |
viewChild() |
|---|---|---|
| Rückgabetyp | Direkte Referenz | Signal<T | undefined> |
ngAfterViewInit erforderlich |
Ja | Nein |
| Unterstützung für zoneless Anwendungen | Eingeschränkt | Ja |
| Verfügbar seit | Angular 2 | Angular 17 |
@ViewChild kann auch in zoneless Anwendungen eingesetzt werden. Ändert sich jedoch eine abgefragte Referenz, muss die Change Detection über ChangeDetectorRef.markForCheck() manuell benachrichtigt werden, weil ohne Zone.js keine automatische Erkennung ausgelöst wird. Das signalbasierte viewChild() ist direkt mit dem reaktiven Graphen von Angular verbunden und kann ohne diesen manuellen Schritt aktualisiert werden.
Grundlegendes Beispiel für eine Signal Query
Das folgende Beispiel verwendet eine Standalone Component. Die signalbasierte API ist für das Standalone-Komponentenmodell vorgesehen und sollte nicht in app.module.ts deklariert werden.
Zusätzlich wird afterNextRender verwendet. Dabei handelt es sich um eine mit Angular 17 eingeführte Lifecycle-Funktion, die einen Callback einmal nach Abschluss des nächsten DOM-Renderzyklus ausführt. In Umgebungen ohne klassenbasierten Lifecycle, beispielsweise bei signalbasierten Standalone Components, kann sie anstelle von ngAfterViewInit eingesetzt werden. afterNextRender eignet sich, wenn nach dem ersten Rendern einmalig DOM-Daten gelesen oder verändert werden müssen. Während des serverseitigen Renderings wird die Funktion nicht ausgeführt.
app.component.ts
import { Component, viewChild, ElementRef, afterNextRender } from ‚@angular/core‘;
@Component({
selector: ‚app-root‘,
template: „,
standalone: true
})
export class AppComponent {
// viewChild() provides a Signal
nameInput = viewChild(’nameInput‘);
constructor() {
afterNextRender(() => {
// retrieve the signal value after the view has rendered
console.log(this.nameInput()?.nativeElement.value);
});
}
}
Ist garantiert, dass ein Element vorhanden ist, kann mit viewChild.required() die undefined-Alternative entfernt werden:
// Signal: throws an error when the query has no match
nameInput = viewChild.required(’nameInput‘);
Wann signalbasierte Queries sinnvoll sind
Bei neuen Anwendungen mit Angular 17 oder einer neueren Version können viewChild() und viewChildren() bevorzugt werden, insbesondere bei zoneless Change Detection oder beim Einsatz von Standalone Components. Projekte mit Angular 16 oder älter sollten weiterhin @ViewChild und @ViewChildren verwenden.
Das folgende Beispiel zeigt, wie viewChildren() ein Signal mit mehreren gefundenen Elementen bereitstellt. In signalbasierten Komponenten kann es @ViewChildren ersetzen. Vor der Ausführung dieses Beispiels wird eine ChildComponent mit ng generate component child --flat --skip-tests --standalone erzeugt:
app.component.ts
import { Component, viewChildren, afterNextRender } from ‚@angular/core‘;
import { ChildComponent } from ‚./child.component‘;
@Component({
selector: ‚app-root‘,
template: `
`,
standalone: true,
imports: [ChildComponent]
})
export class AppComponent {
// viewChildren() provides Signal<readonlyarray>
children = viewChildren(ChildComponent);
constructor() {
afterNextRender(() => {
console.log(this.children().length); // 2
});
}
}
@ViewChild vs. @ContentChild vs. @ViewChildren
| Decorator / Funktion | Abfragebereich | Rückgabe | Anzahl | Verfügbar in |
|---|---|---|---|---|
@ViewChild |
Eigenes Template der Komponente | Erster passender Treffer | Einzelnes Element | ngAfterViewInit oder ngOnInit mit static: true |
@ViewChildren |
Eigenes Template der Komponente | QueryList<T> |
Mehrere Elemente | ngAfterViewInit |
@ContentChild |
Über <ng-content> projizierter Inhalt |
Erster passender Treffer | Einzelnes Element | ngAfterContentInit |
viewChild() |
Eigenes Template der Komponente | Signal<T> |
Einzelnes Element | Beim Auslesen in Angular 17+ |
viewChildren() |
Eigenes Template der Komponente | Signal<ReadonlyArray<T>> |
Mehrere Elemente | Beim Auslesen in Angular 17+ |
@ContentChild eignet sich für wiederverwendbare Wrapper Components, die projizierte Inhalte aufnehmen. Für Elemente, die direkt im eigenen Template einer Komponente definiert sind, werden @ViewChild oder viewChild() verwendet.
Häufige Fehler bei Angular @ViewChild und wie sie vermieden werden
Auf @ViewChild vor ngAfterViewInit zugreifen
Wird eine @ViewChild-Eigenschaft bereits innerhalb von ngOnInit verwendet, ist ihr Wert normalerweise undefined, da Angular die View zu diesem Zeitpunkt noch nicht initialisiert hat.
Falsch:
// app.component.ts
ngOnInit() {
// TypeError: properties of undefined cannot be read
console.log(this.someInput.nativeElement.value);
}
Richtig:
// app.component.ts
ngAfterViewInit() {
console.log(this.someInput.nativeElement.value); // behaves as expected
}
Die Zugriffslogik sollte nach ngAfterViewInit verschoben werden. Zu diesem Zeitpunkt garantiert Angular, dass die View initialisiert und die Query aufgelöst wurde.
Elemente innerhalb von *ngIf oder *ngFor abfragen
Wird für eine Query auf ein Element innerhalb einer strukturellen Direktive static: true gesetzt, versucht Angular die Referenz bereits aufzulösen, obwohl das Element möglicherweise noch gar nicht vorhanden ist.
Falsch:
// app.component.ts
@ViewChild(‚conditionalEl‘, { static: true }) el!: ElementRef;
// static: true cannot resolve an element before *ngIf has potentially rendered it
Richtig:
// app.component.ts
@ViewChild(‚conditionalEl‘) el: ElementRef | undefined;
// static: false is the default and resolves after change detection
ngAfterViewInit() {
if (this.el) {
this.el.nativeElement.focus();
}
}
Die Kombination aus static: false und einer Prüfung auf undefined stellt sicher, dass die Referenz erst verwendet wird, wenn das entsprechende Element tatsächlich im DOM vorhanden ist.
ElementRef nicht verwenden, wenn @Input oder @Output ausreichen
Eine Child Component über read: ElementRef direkt zu verändern, ist unnötig, wenn dasselbe Ergebnis über ein @Input-Binding erreicht werden kann.
Falsch:
// app.component.ts
// Retrieves the host through read: ElementRef and modifies its style directly.
@ViewChild(PupComponent, { read: ElementRef }) pupEl!: ElementRef;
ngAfterViewInit() {
this.pupEl.nativeElement.style.color = ‚red‘;
}
Richtig:
// pup.component.ts
import { Input } from ‚@angular/core‘;
export class PupComponent {
@Input() highlightColor: string = “;
}
// pup.component.html
// {{ message }}
<!– app.component.html –>
<app-pup [highlightColor]=“‚red’“></app-pup>
Wird der Wert über @Input übergeben, bleiben Parent Component und Child Component voneinander entkoppelt. Gleichzeitig bleiben die Change-Detection- und Sicherheitsmechanismen von Angular erhalten.
Häufig gestellte Fragen zu Angular @ViewChild
Was ist der Unterschied zwischen @ViewChild und @ContentChild in Angular?
@ViewChild durchsucht Elemente, die direkt im eigenen Template einer Komponente definiert sind. @ContentChild durchsucht dagegen Elemente, die über <ng-content> in eine Komponente projiziert werden. @ContentChild ist daher für wiederverwendbare Wrapper Components geeignet, die projizierte Inhalte von einer Parent Component erhalten.
Warum ist meine @ViewChild-Eigenschaft in ngOnInit undefined?
Standardmäßig wird @ViewChild erst nach der Initialisierung der View aufgelöst. Dieser Vorgang findet nach ngOnInit statt. Die Logik, die auf die Referenz zugreift, sollte deshalb nach ngAfterViewInit verschoben werden. Wird der Zugriff ausdrücklich bereits in ngOnInit benötigt, kann static: true gesetzt werden. Das gilt jedoch nur, wenn sich das abgefragte Element nicht innerhalb einer strukturellen Direktive befindet.
Was bewirkt die Option static bei @ViewChild?
static: true weist Angular an, die Query vor der Change Detection aufzulösen. Dadurch steht das Ergebnis bereits in ngOnInit zur Verfügung. static: false ist der Standardwert und führt dazu, dass die Query erst nach der Change Detection aufgelöst wird und damit in ngAfterViewInit verfügbar ist. Für Elemente innerhalb von *ngIf oder *ngFor sollte static: false eingesetzt werden.
Wie kann ich auf mehrere Child-Elemente mit demselben Selektor zugreifen?
Verwende @ViewChildren anstelle von @ViewChild. Dadurch wird eine QueryList mit allen passenden Elementen zurückgegeben. Über .forEach() können die Elemente durchlaufen werden, während ein Abonnement von .changes auf spätere Änderungen der Liste reagieren kann.
Ist die direkte DOM-Manipulation über ElementRef aus @ViewChild sicher?
Direkte DOM-Manipulationen über ElementRef.nativeElement umgehen das Sicherheitsmodell von Angular und können serverseitiges Rendering beeinträchtigen. Für DOM-Operationen sollte deshalb der Angular-Service Renderer2 verwendet werden. Alternativ sind Angular-Bindings wie [style], [class] oder @HostBinding vorzuziehen, wenn sich die gewünschte Funktion damit umsetzen lässt.
Was ist die signalbasierte Alternative zu @ViewChild in Angular 17+?
Angular 17 führte viewChild() als reaktive Alternative zu @ViewChild ein. Die Funktion gibt ein Signal zurück, das Änderungen des abgefragten Elements automatisch widerspiegelt und direkt mit dem signalbasierten Reaktivitätsmodell von Angular zusammenarbeitet. Für neue Projekte mit Angular 17 oder einer neueren Version kann diese Variante verwendet werden.
Kann @ViewChild ein Element innerhalb eines *ngIf-Blocks abfragen?
Ja. Die Query muss dafür static: false verwenden, was gleichzeitig der Standardwert ist. Ist die *ngIf-Bedingung false und wird das Element daher nicht gerendert, hat die @ViewChild-Eigenschaft den Wert undefined. Vor dem Zugriff sollte deshalb immer geprüft werden, ob die Referenz tatsächlich vorhanden ist.
Wofür wird die Option read bei @ViewChild verwendet?
Mit read wird festgelegt, welchen Typ Angular für das gefundene Element zurückgeben soll. So liefert beispielsweise @ViewChild('myRef', { read: ElementRef }) ein ElementRef, selbst wenn der Selektor eigentlich eine Komponente identifiziert. @ViewChild('myRef', { read: MyDirective }) gibt dagegen die Instanz der angegebenen Direktive zurück, die auf dem Element angewendet wurde.
Fazit zu Angular @ViewChild
Dieser Leitfaden hat gezeigt, wie @ViewChild innerhalb einer Parent Component eingesetzt werden kann, um auf eine Child Component, eine Direktive oder ein natives DOM-Element zuzugreifen. Außerdem wurden die Optionen static und read, die Abfrage mehrerer Elemente über @ViewChildren und QueryList sowie die mit Angular 17 verfügbare signalbasierte API viewChild() behandelt.
Mit diesen Techniken lassen sich typisierte Referenzen auf Elemente innerhalb eines Komponenten-Templates abrufen. Gleichzeitig kann gesteuert werden, wann Queries im Verhältnis zum Angular-Initialisierungszyklus aufgelöst werden. Abhängig von der verwendeten Angular-Version kann zwischen den decoratorbasierten und den signalbasierten Query-APIs gewählt werden.


