Manchmal reicht es nicht aus, Daten nur über Inputs in eine Child-Komponente zu geben. Wenn du Methoden einer Child-Komponente direkt aufrufen oder ein nacktes DOM-Element (wie ein <canvas> oder <input>) manipulieren musst, verwendest du in Angular Queries. Mit dem Einzug der Signals gibt es nun eine moderne, reaktive API dafür.

View Queries vs. Content Queries

Bevor wir in den Code schauen, ist es wichtig, die zwei Arten von Queries in Angular zu verstehen:

  1. View Queries (viewChild / viewChildren): Suchen nach Elementen, die direkt im Template (der View) der aktuellen Komponente definiert sind.
  2. Content Queries (contentChild / contentChildren): Suchen nach Elementen, die von außen über Content Projection (<ng-content>) in die Komponente projiziert wurden.

Die moderne Signal-API

Angular bietet die Funktionen viewChild(), viewChildren(), contentChild() und contentChildren(). Sie alle geben ein reaktives Signal zurück. Das bedeutet, ihr Wert aktualisiert sich automatisch (z.B. wenn ein Element durch @if plötzlich erscheint), und du kannst sie problemlos in computed() oder effect() verwenden.

Ein Element abfragen (viewChild)

Du kannst nach dem Typ einer Child-Komponente suchen oder eine sogenannte Template Reference Variable (ein String, beginnend mit # im HTML) verwenden.

TypeScript view-child.ts
import { Component, viewChild, ElementRef, effect } from '@angular/core';
import { AlertComponent } from './alert.component';

@Component({
    selector: 'app-dashboard',
    template: `
        <!-- 1. Suche über den Typen -->
        <app-alert></app-alert>

        <!-- 2. Suche über eine Template Reference Variable (#myCanvas) -->
        <canvas #myCanvas></canvas>
    `,
    standalone: true,
    imports: [AlertComponent]
})
export class DashboardComponent {
    // Sucht nach der ersten Komponente vom Typ "AlertComponent"
    // Typ: Signal<AlertComponent | undefined>
    alertCmp = viewChild(AlertComponent);

    // Sucht nach dem Element mit "#myCanvas"
    canvasEl = viewChild<ElementRef<HTMLCanvasElement>>('myCanvas');

    constructor() {
        // Ein effect führt sich aus, sobald das Signal einen Wert hat
        effect(() => {
            const canvas = this.canvasEl()?.nativeElement;
            if (canvas) {
                const ctx = canvas.getContext('2d');
                // ... zeichnen
            }
        });
    }
    
    closeAlert() {
        // Direkter Aufruf einer Methode auf der Child-Komponente
        this.alertCmp()?.close();
    }
}

Erzwungene Queries (.required)

Da ein Element durch ein @if versteckt sein könnte, ist der Rückgabetyp eines normalen viewChild immer optional (undefined). Weißt du jedoch sicher, dass das Element immer da ist (da es z.B. nicht in einem @if liegt), kannst du .required() anhängen. Angular wirft dann einen Fehler, falls es fehlt, und du sparst dir ständige ?. Checks.

TypeScript required-query.ts
// Typ: Signal<AlertComponent> (kein undefined mehr!)
alertCmp = viewChild.required(AlertComponent);

Mehrere Elemente abfragen (viewChildren)

Sucht nach allen passenden Elementen und gibt ein Signal zurück, das ein Array enthält.

TypeScript view-children.ts
import { Component, viewChildren, computed } from '@angular/core';

@Component({
    selector: 'app-list',
    template: `
        <app-item text="Item 1" />
        <app-item text="Item 2" />
    `
})
export class ListComponent {
    // Signal<readonly ItemComponent[]>
    items = viewChildren(ItemComponent);

    // Reagiert automatisch, wenn Items hinzukommen oder verschwinden
    itemCount = computed(() => this.items().length);
}

Content Queries (contentChild)

Content Queries greifen auf Elemente zu, die via <ng-content> eingebunden werden. Beachte, dass View Queries nicht in projizierten Inhalten suchen können!

TypeScript content-child.ts
import { Component, contentChild, contentChildren } from '@angular/core';

@Component({
    selector: 'app-accordion',
    template: `
        <div class="accordion-wrapper">
            <ng-content></ng-content>
        </div>
    `
})
export class AccordionComponent {
    // Greift auf die projizierten Elemente zu,
    // z.B. <app-accordion> <app-panel /> <app-panel /> </app-accordion>
    
    firstPanel = contentChild(PanelComponent);
    allPanels = contentChildren(PanelComponent);
}

Standardmäßig suchen Content Queries nur nach direkten Kind-Elementen. Willst du auch tiefere Verschachtelungen (Descendants) durchsuchen, musst du dies in den Optionen aktivieren: contentChildren(PanelComponent, { descendants: true })

Klassische Dekorator-Queries (Legacy)

In älteren Codebases begegnen dir die Dekoratoren @ViewChild, @ViewChildren, @ContentChild und @ContentChildren.

Der größte Unterschied zu Signalen: Du musst auf den Lifecycle-Hook ngAfterViewInit (für View Queries) oder ngAfterContentInit (für Content Queries) warten, bevor die Eigenschaften mit Daten gefüllt sind. Vorher sind sie undefined.

TypeScript legacy-queries.ts
import { Component, ViewChild, ViewChildren, QueryList, AfterViewInit } from '@angular/core';

@Component({
    selector: 'app-legacy',
    template: `<div #myDiv>Hallo</div> <app-item></app-item>`
})
export class LegacyComponent implements AfterViewInit {
    @ViewChild('myDiv') myDivEl!: ElementRef<HTMLDivElement>;
    
    // QueryList statt Array bei Children-Queries
    @ViewChildren(ItemComponent) items!: QueryList<ItemComponent>;

    ngAfterViewInit() {
        // ERST HIER sind die Queries garantiert verfügbar
        console.log(this.myDivEl.nativeElement.innerHTML);
        console.log('Anzahl Items:', this.items.length);
        
        // Auf Änderungen lauschen (Reaktivität)
        this.items.changes.subscribe(() => {
            console.log('Items haben sich geändert');
        });
    }
}

Häufige Stolperfallen

static: true friert das Ergebnis ein

Bei Dekorator-Queries macht static: true das Ergebnis zwar schon in ngOnInit verfügbar – aktualisiert es danach aber nie wieder. Sobald das Ziel in @if/@for liegt oder dynamisch erscheint, brauchst du zwingend static: false (Default).

Signal-Queries lösen das Timing-Problem

viewChild() und contentChild() (seit v17.2) sind reaktive Signals und brauchen kein ngAfterViewInit mehr. Sie liefern undefined bis das Element existiert und aktualisieren sich von selbst – ideal für computed() und effect().

contentChildren findet nur direkte Kinder

Standardmäßig durchsuchen Content-Queries nicht den ganzen projizierten Baum. Für tiefere Verschachtelung musst du { descendants: true } setzen – sonst bleiben Listen unerwartet leer, sobald jemand einen Wrapper drumherum baut.

required: true statt ?.-Ketten

Wenn du sicher weißt, dass das Ziel immer im DOM ist, nimm viewChild.required(…). Der Rückgabetyp verliert das | undefined, und Angular wirft beim Fehlen sofort einen klaren Runtime-Error – besser als stille ?.-Kaskaden.

read-Option für anderes Token

Mit { read: ElementRef } oder { read: TemplateRef } bekommst du nicht die Komponenten-Instanz, sondern ein anderes DI-Token desselben Knotens. Klassischer Fall: eine Komponente abfragen, aber nur ihr nacktes ElementRef für DOM-Messungen ziehen.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Queries & Projection

Zur Übersicht