Signal Queries sind die funktionalen Pendants zu den klassischen Decoratoren @ViewChild, @ViewChildren, @ContentChild und @ContentChildren. Statt eine Klassen-Property nach dem ersten Render zu befüllen, geben dir viewChild(), viewChildren(), contentChild() und contentChildren() ein Signal zurück — initial undefined oder ein leeres Array, danach automatisch der aktuelle Wert. Damit fällt der gesamte Lifecycle-Hook-Tanz um ngAfterViewInit weg, und Queries werden zu echten reaktiven Quellen, die sich nahtlos in computed() und effect() einklinken. Dieser Artikel zeigt mit Angular 21, wie die vier Funktionen aufgebaut sind, wie die required-Variante das | undefined aus dem Type schneidet, wann read den Token-Typ wechselt, warum descendants: true für Content-Queries oft entscheidend ist und wie eine vollständige Tabs-Component mit contentChildren + computed aussieht. Signal Queries sind seit v17.2 stable, in v19 wurde der Initializer-API-Stempel gesetzt — kein Devkit-Flag mehr nötig.

Funktionale Queries als Signal

Klassische Queries arbeiten mit einem Decorator auf einer Klassen-Property: @ViewChild('foo') foo!: ElementRef. Angular füllt diese Property erst im Lifecycle-Hook ngAfterViewInit (View-Queries) bzw. ngAfterContentInit (Content-Queries). Vor diesem Hook ist die Property undefined — was zu einer ganzen Klasse von Stolpersteinen führt: Konstruktoren, die zu früh greifen, effect()-Aufrufe, die das Resultat verfehlen, und BehaviorSubject-Brücken, die das Timing-Problem manuell überspielen.

Signal Queries lösen das mit einem anderen Ansatz: Statt einer Property liefert dir die Initializer-Funktion ein Signal. Der Wert ist anfänglich undefined (Single) oder [] (List) und wird von Angular automatisch aktualisiert, sobald das Element im View- oder Content-Baum auftaucht, verschwindet oder sich ändert.

TypeScript hello-signal-query.component.ts
import { Component, viewChild, effect } from '@angular/core';
import { AlertComponent } from './alert.component';

@Component({
    selector: 'app-dashboard',
    standalone: true,
    imports: [AlertComponent],
    template: `<app-alert />`,
})
export class DashboardComponent {
    // Signal<AlertComponent | undefined>
    alert = viewChild(AlertComponent);

    constructor() {
        effect(() => {
            const cmp = this.alert();
            if (cmp) {
                console.log('Alert ist da:', cmp);
            }
        });
    }
}

Drei Eigenschaften unterscheiden Signal-Queries grundsätzlich von Decorator-Queries:

  • Reaktiv von Anfang an. Du brauchst keinen Lifecycle-Hook mehr — effect() und computed() reagieren automatisch, sobald der Wert auflöst.
  • Type ist explizit. viewChild(X) returnt Signal<X | undefined>, viewChild.required(X) returnt Signal<X> — kein !-Hack auf der Property.
  • List statt QueryList. viewChildren() und contentChildren() geben Signal<readonly T[]> zurück. Kein .subscribe() auf QueryList.changes, kein Cleanup.

viewChild, viewChildren, contentChild, contentChildren

Alle vier Funktionen folgen demselben Muster: Sie nehmen einen Locator (Component-Klasse, Template-Reference-String oder DI-Token) und optional ein Options-Object. Sie unterscheiden sich nur in zwei Achsen — Single vs. List und View vs. Content.

FunktionSuchraumRückgabeDecorator-Pendant
viewChild()ViewSignal<T | undefined>@ViewChild
viewChild.required()ViewSignal<T>@ViewChild + !
viewChildren()ViewSignal<readonly T[]>@ViewChildren
contentChild()ProjektionSignal<T | undefined>@ContentChild
contentChild.required()ProjektionSignal<T>@ContentChild + !
contentChildren()ProjektionSignal<readonly T[]>@ContentChildren

View meint hier den eigenen Template-Block der Component. Content meint Markup, das von außen über <ng-content> projiziert wurde. Ein und dieselbe Komponente kann beide Arten gleichzeitig haben — eine Tabs-Komponente etwa hat ein eigenes Header-Template (View) und nimmt einzelne Tab-Elemente projiziert auf (Content).

Grundsyntax mit drei Locator-Arten

viewChild() akzeptiert drei Arten von Locator: eine Component-Klasse, einen Template-Reference-Variablen-String (wie 'myInput' für #myInput) oder ein DI-Token wie ElementRef oder TemplateRef.

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

@Component({
    selector: 'app-locators',
    standalone: true,
    imports: [AlertComponent],
    template: `
        <app-alert />
        <input #email type="email" />
        <ng-template #row let-name>
            <p>Hallo, {{ name }}</p>
        </ng-template>
    `,
})
export class LocatorsComponent {
    // 1) Component-Klasse als Locator: Type wird automatisch inferiert
    alert = viewChild(AlertComponent);
    // Signal<AlertComponent | undefined>

    // 2) Template-Reference-String + explizites Generic
    email = viewChild<ElementRef<HTMLInputElement>>('email');
    // Signal<ElementRef<HTMLInputElement> | undefined>

    // 3) Template-Reference auf <ng-template> mit TemplateRef-Type
    row = viewChild<TemplateRef<{ $implicit: string }>>('row');
    // Signal<TemplateRef<...> | undefined>
}

Der Wert wird durch Aufruf der Signal-Funktion gelesen: this.alert() returnt AlertComponent | undefined. Ohne die Klammern bekommst du die Signal-Funktion selbst — ein häufiger Fehler in Templates und Effects, der zu still falschen Wahrheits-Checks führt (eine Funktion ist immer truthy).

Wenn das Element garantiert existiert

Liegt das Ziel nicht hinter einem @if oder *ngIf und ist es kein konditional projiziertes Element, kannst du viewChild.required() (oder contentChild.required()) verwenden. Der Type verliert dann das | undefined, und Angular wirft einen klaren Runtime-Error, falls das Element doch fehlt.

TypeScript required-query.component.ts
import { Component, viewChild, ElementRef } from '@angular/core';

@Component({
    selector: 'app-canvas-host',
    standalone: true,
    template: `<canvas #board width="400" height="200"></canvas>`,
})
export class CanvasHostComponent {
    // Signal<ElementRef<HTMLCanvasElement>> — kein undefined!
    board = viewChild.required<ElementRef<HTMLCanvasElement>>('board');

    draw() {
        // Direkter Zugriff ohne Optional-Chaining
        const ctx = this.board().nativeElement.getContext('2d')!;
        ctx.fillRect(10, 10, 80, 40);
    }
}

required heißt im TypeScript-Type non-nullable, nicht „Angular garantiert die Existenz”. Wenn das Element zur Laufzeit doch fehlt (z. B. weil ein dynamisch eingehängtes Template es weglässt), wirft Angular beim ersten ()-Aufruf einen NG0951-Error. Der Schutz ist also Type-Schutz plus Fail-Fast — kein magischer DOM-Zwang.

Reactivity bei Add, Remove, Move

viewChildren() returnt Signal<readonly T[]>. Das Array aktualisiert sich automatisch, wenn neue Elemente erscheinen, alte verschwinden oder die Reihenfolge sich ändert — alles, ohne dass du ein QueryList.changes-Subscribe verdrahten musst.

TypeScript dynamic-list.component.ts
import { Component, viewChildren, computed, signal } from '@angular/core';
import { ItemComponent } from './item.component';

@Component({
    selector: 'app-dynamic-list',
    standalone: true,
    imports: [ItemComponent],
    template: `
        <button (click)="add()">Add</button>
        <button (click)="remove()">Remove</button>

        @for (id of ids(); track id) {
            <app-item [id]="id" />
        }

        <p>Aktuell: {{ count() }} Items, erstes id = {{ firstId() }}</p>
    `,
})
export class DynamicListComponent {
    ids = signal<number[]>([1, 2, 3]);

    // Signal<readonly ItemComponent[]>
    items = viewChildren(ItemComponent);

    // Reaktive Ableitungen ohne Lifecycle-Hook
    count = computed(() => this.items().length);
    firstId = computed(() => this.items()[0]?.id ?? null);

    add() {
        this.ids.update((arr) => [...arr, arr.length + 1]);
    }
    remove() {
        this.ids.update((arr) => arr.slice(0, -1));
    }
}

Beachte zwei Details: Das Array ist als readonly markiert — du darfst es nicht direkt mutieren (kein push/splice). Und computed() kann das Signal direkt konsumieren; Angular trackt die Abhängigkeit automatisch und re-evaluiert, sobald sich die Liste ändert.

Container-Komponenten und ng-content

Content-Queries durchsuchen das Markup, das von außen in deine Component projiziert wurde — typisch über <ng-content>. View-Queries finden diese Elemente nicht; sie liegen logisch im Parent-Template, nicht in deinem.

TypeScript accordion.component.ts
import { Component, contentChild, contentChildren } from '@angular/core';
import { PanelComponent } from './panel.component';

@Component({
    selector: 'app-accordion',
    standalone: true,
    template: `
        <div class="accordion">
            <ng-content />
        </div>
        <p>Insgesamt {{ panels().length }} Panel(s)</p>
    `,
})
export class AccordionComponent {
    // Erstes projiziertes Panel — durchsucht ALLE Descendants per Default
    firstPanel = contentChild(PanelComponent);

    // Alle direkten Panels; tiefer geschachtelt nur mit descendants: true
    panels = contentChildren(PanelComponent, { descendants: true });
}

Verwendet wird die Component dann von außen so:

HTML parent.template.html
<app-accordion>
    <app-panel title="Eins">…</app-panel>
    <app-panel title="Zwei">…</app-panel>
    <app-panel title="Drei">…</app-panel>
</app-accordion>

Der entscheidende Unterschied zu View-Queries: Eine Container-Component kennt ihre Inhalte erst zur Laufzeit, weil der Parent sie projiziert. Genau hier glänzt die Signal-Variante — du brauchst keinen ngAfterContentInit-Hook, keinen QueryList.changes.subscribe(). Stattdessen reagiert ein effect() oder computed() automatisch auf jede Änderung.

Vom Component-Tag zum nackten ElementRef

Standardmäßig liefert eine Query auf einem Component-Tag die Component-Instanz. Manchmal willst du aber nur das nackte DOM-Element, den ViewContainerRef oder den TemplateRef desselben Knotens. Die read-Option wechselt das DI-Token:

TypeScript read-option.component.ts
import {
    Component,
    viewChild,
    ElementRef,
    ViewContainerRef,
} from '@angular/core';
import { AlertComponent } from './alert.component';

@Component({
    selector: 'app-read-demo',
    standalone: true,
    imports: [AlertComponent],
    template: `
        <app-alert #alert />
        <ng-container #host></ng-container>
    `,
})
export class ReadDemoComponent {
    // Component-Instanz (Default)
    alertCmp = viewChild.required(AlertComponent);

    // Selbe DOM-Stelle, aber als nacktes ElementRef
    alertEl = viewChild.required('alert', { read: ElementRef });

    // ng-container als programmatischer Insertion-Point
    host = viewChild.required('host', { read: ViewContainerRef });

    highlight() {
        (this.alertEl().nativeElement as HTMLElement).classList.add('on');
    }
}

Der Type des Signals folgt der read-Angabe: viewChild.required('alert', { read: ElementRef }) gibt Signal<ElementRef>, viewChild('foo', { read: TemplateRef }) gibt Signal<TemplateRef<unknown> | undefined>. Bei der Tabelle weiter unten findest du die häufigsten Read-Token-Wechsel im Praxis-Einsatz.

read-TokenWann sinnvoll
ElementRefDOM-Operationen wie focus(), getBoundingClientRect()
ViewContainerRefProgrammatisches Einhängen von Templates oder Components
TemplateRef<ng-template> als Variable für *ngTemplateOutlet
Eigenes DI-TokenService, der über providers an die Component gehängt ist

computed() statt ngAfterViewInit

Der eigentliche Mehrwert von Signal-Queries zeigt sich erst, wenn du sie in einer reaktiven Pipeline weiterverarbeitest. Klassische Decorator-Queries sind keine Observables und lassen sich nicht direkt in computed() füttern — du brauchst eine Brücke (z. B. ein BehaviorSubject, das du im ngAfterViewInit befüllst). Signal-Queries sind diese Brücke ab Werk.

TypeScript reactive-measure.component.ts
import { Component, viewChildren, computed, ElementRef } from '@angular/core';

@Component({
    selector: 'app-measure',
    standalone: true,
    template: `
        @for (h of [40, 60, 80]; track h) {
            <div #row [style.height.px]="h">Row</div>
        }
        <p>Erste Höhe: {{ firstHeight() ?? 'n/a' }}</p>
        <p>Summe: {{ totalHeight() }}</p>
    `,
})
export class MeasureComponent {
    rows = viewChildren<ElementRef<HTMLDivElement>>('row');

    firstHeight = computed(
        () => this.rows()[0]?.nativeElement.clientHeight ?? null,
    );

    totalHeight = computed(() =>
        this.rows().reduce(
            (sum, r) => sum + r.nativeElement.clientHeight,
            0,
        ),
    );
}

Mit klassischen Queries müsstest du diese Berechnungen manuell in ngAfterViewInit anstoßen, in ngAfterViewChecked periodisch refreshen und auf QueryList.changes lauschen. Der Signal-Ansatz reduziert das auf zwei computed()-Definitionen — Reactivity ist ein Sprachmittel, nicht ein Lifecycle-Choreographie-Problem.

Side-by-side mit gleichem Use-Case

Die Migration ist mechanisch einfach, aber ein paar Konventionen ändern sich. Hier dasselbe Beispiel zweimal: links der klassische Decorator-Stil, rechts die Signal-Variante.

TypeScript migration-before.component.ts
// VORHER: Decorator + ngAfterViewInit
import {
    Component,
    ViewChild,
    ViewChildren,
    QueryList,
    ElementRef,
    AfterViewInit,
} from '@angular/core';
import { ItemComponent } from './item.component';

@Component({
    selector: 'app-old',
    standalone: true,
    imports: [ItemComponent],
    template: `
        <input #email />
        <app-item *ngFor="let i of ids" [id]="i" />
    `,
})
export class OldComponent implements AfterViewInit {
    ids = [1, 2, 3];

    @ViewChild('email') emailEl!: ElementRef<HTMLInputElement>;
    @ViewChildren(ItemComponent) items!: QueryList<ItemComponent>;

    ngAfterViewInit() {
        this.emailEl.nativeElement.focus();
        this.items.changes.subscribe((q: QueryList<ItemComponent>) => {
            console.log('Now', q.length);
        });
        console.log('Initial', this.items.length);
    }
}
TypeScript migration-after.component.ts
// NACHHER: Signal-Queries + effect()
import {
    Component,
    viewChild,
    viewChildren,
    ElementRef,
    effect,
} from '@angular/core';
import { ItemComponent } from './item.component';

@Component({
    selector: 'app-new',
    standalone: true,
    imports: [ItemComponent],
    template: `
        <input #email />
        @for (i of ids; track i) {
            <app-item [id]="i" />
        }
    `,
})
export class NewComponent {
    ids = [1, 2, 3];

    emailEl = viewChild.required<ElementRef<HTMLInputElement>>('email');
    items = viewChildren(ItemComponent);

    constructor() {
        // Auto-Focus, sobald das Input im DOM ist
        effect(() => this.emailEl().nativeElement.focus());

        // Reagiert auf jede Listen-Änderung (Add/Remove/Move)
        effect(() => console.log('Now', this.items().length));
    }
}

Drei Konventions-Änderungen lohnt sich zu merken: QueryList<T> wird zu Signal<readonly T[]>, !-Definite-Assertion fällt zu viewChild.required(...), und ngAfterViewInit plus QueryList.changes.subscribe() werden zu effect()-Aufrufen im Konstruktor. Die offiziellen Migration-Schematics laufen über ng generate @angular/core:signal-queries-migration (ab v18) oder das übergreifende signals-Schematic — falls verfügbar; ansonsten ist die manuelle Umstellung in den meisten Komponenten ein Sache von Minuten.

Vollständiges Tabs-Pattern mit contentChildren

Ein kanonischer Anwendungsfall für contentChildren() ist eine Tabs-Komponente: Der Parent projiziert beliebig viele <app-tab>-Elemente; der Wrapper sammelt sie ein und kontrolliert, welcher Tab gerade sichtbar ist. Die ganze State-Logik passt in eine Komponente mit signal() + computed() ohne einen einzigen Lifecycle-Hook.

TypeScript tab.component.ts
import { Component, input, TemplateRef, viewChild } from '@angular/core';

@Component({
    selector: 'app-tab',
    standalone: true,
    template: `
        <ng-template #content>
            <ng-content />
        </ng-template>
    `,
})
export class TabComponent {
    label = input.required<string>();
    content = viewChild.required<TemplateRef<unknown>>('content');
}
TypeScript tabs.component.ts
import {
    Component,
    contentChildren,
    signal,
    computed,
} from '@angular/core';
import { NgTemplateOutlet } from '@angular/common';
import { TabComponent } from './tab.component';

@Component({
    selector: 'app-tabs',
    standalone: true,
    imports: [NgTemplateOutlet],
    template: `
        <div class="tabs__bar" role="tablist">
            @for (tab of tabs(); track tab; let i = $index) {
                <button
                    type="button"
                    role="tab"
                    [attr.aria-selected]="i === selectedIndex()"
                    (click)="selectedIndex.set(i)"
                >
                    {{ tab.label() }}
                </button>
            }
        </div>

        <div class="tabs__panel" role="tabpanel">
            @if (currentTab(); as tab) {
                <ng-container [ngTemplateOutlet]="tab.content()" />
            }
        </div>
    `,
})
export class TabsComponent {
    tabs = contentChildren(TabComponent);
    selectedIndex = signal(0);

    currentTab = computed(() => {
        const list = this.tabs();
        const idx = this.selectedIndex();
        return list[idx] ?? list[0] ?? null;
    });
}
HTML parent-usage.template.html
<app-tabs>
    <app-tab label="Profil">
        <p>Profil-Daten…</p>
    </app-tab>
    <app-tab label="Einstellungen">
        <p>Einstellungen…</p>
    </app-tab>
    <app-tab label="Verlauf">
        <p>Letzte Aktionen…</p>
    </app-tab>
</app-tabs>

Drei Beobachtungen: Erstens — tabs() aktualisiert sich automatisch, wenn der Parent dynamisch Tabs hinzufügt oder entfernt (z. B. mit @for). Zweitens — der Tabs-Container braucht keinerlei Lifecycle-Code; alle Beziehungen sind als Signals/Computed ausgedrückt. Drittens — der currentTab-Fallback fängt den Edge-Case ab, dass der selectedIndex über das aktuelle Array hinausläuft, was bei dynamischen Tabs leicht passiert.

viewChild + effect statt ngAfterViewInit

Ein Klassiker, den viele Codebases mit ngAfterViewInit lösen, ist Auto-Focus beim Erscheinen eines Inputs. Mit Signal-Queries reduziert sich das auf einen einzigen effect()-Aufruf:

TypeScript auto-focus.component.ts
import { Component, viewChild, ElementRef, effect, signal } from '@angular/core';

@Component({
    selector: 'app-auto-focus',
    standalone: true,
    template: `
        <button (click)="show.set(!show())">{{ show() ? 'Hide' : 'Show' }}</button>
        @if (show()) {
            <input #firstInput placeholder="Auto-fokussiert" />
        }
    `,
})
export class AutoFocusComponent {
    show = signal(false);

    // Optional, weil das Input nur bei show() === true im DOM ist
    firstInput = viewChild<ElementRef<HTMLInputElement>>('firstInput');

    constructor() {
        effect(() => {
            // Liest beide Signals: das Query-Signal UND show()
            const el = this.firstInput()?.nativeElement;
            if (el) {
                // afterNextRender wäre eine Alternative — hier aber ausreichend
                queueMicrotask(() => el.focus());
            }
        });
    }
}

Klassisch bräuchtest du @ViewChild('firstInput'), einen AfterViewInit-Hook plus eine separate Reaktion auf jedes Toggle des @if — denn ngAfterViewInit feuert nur einmal beim ersten Render, nicht bei jeder Wiedereinblendung. Der effect() löst beides in einem Schritt: Er feuert sowohl beim ersten Mount als auch bei jedem späteren Re-Mount des Inputs, weil das Query-Signal jeweils neu auflöst.

Was passiert, wenn das Element nur konditional existiert?

Strukturdirektiven (@if, @for, *ngIf, *ngFor und auch eigene *appWhen-Direktiven) hängen ihre Inhalte dynamisch ein und aus. Signal-Queries gehen damit automatisch um:

  • Einblenden → das Signal wechselt von undefined zu einem gefüllten Wert (Single) oder von [] zu [item] (List).
  • Ausblenden → das Signal wechselt zurück zu undefined bzw. [].
  • Reihenfolge ändern → bei viewChildren()/contentChildren() wird das Array neu sortiert; jede Identitäts-vergleichende Logik (z. B. === auf das alte erste Item) muss damit umgehen.
TypeScript conditional-query.component.ts
import { Component, viewChild, ElementRef, signal, effect } from '@angular/core';

@Component({
    selector: 'app-conditional',
    standalone: true,
    template: `
        <button (click)="visible.set(!visible())">Toggle</button>
        @if (visible()) {
            <p #para>Hallo</p>
        }
    `,
})
export class ConditionalComponent {
    visible = signal(true);
    para = viewChild<ElementRef<HTMLParagraphElement>>('para');

    constructor() {
        effect(() => {
            const el = this.para();
            console.log(el ? 'sichtbar:' : 'weg —', el?.nativeElement);
        });
    }
}

viewChild.required('para') wäre hier falsch: Sobald visible auf false springt, wirft Angular beim Lesen einen NG0951-Error. Optional + Null-Check ist die saubere Wahl, sobald ein @if/*ngIf im Spiel ist. Bei @for mit potenziell leerer Liste gilt analog: viewChildren() returnt [], was [0]?.nativeElement korrekt zu undefined auflöst.

Wichtige Details, die in Code-Reviews auffallen

viewChild() ist ein Signal — vergessenes () gibt die Funktion-Reference

Lese-Aufrufe brauchen die Klammern: this.alert(), nicht this.alert. Der zweite Ausdruck gibt die Signal-Funktion selbst — und eine Funktion ist immer truthy, was zu still falschen if (this.alert)-Checks führt. Auch im Template gilt: {{ alert()?.title }}, nicht {{ alert?.title }}.

viewChild.required() wirft RUNTIME-Error, kein Compile-Schutz

Der Type verliert das | undefined, aber Angular garantiert nicht die Existenz im DOM. Ist das Element wegen @if false nicht da, fliegt beim ersten ()-Aufruf ein NG0951. required heißt also: TS-Type non-nullable, plus Fail-Fast. Für konditionale Elemente bleibt die optionale Variante richtig.

Signal-Queries lösen den ngAfterViewInit-Stolperstein

Lese-Aufruf vor dem ersten Render gibt undefined, danach automatisch das Element — kein Hook nötig. Damit darfst du im Konstruktor schon effect(() => …) registrieren, der erst feuert, wenn das Query auflöst. Klassische Decorator-Queries verlangten dafür AfterViewInit plus Brückenlogik.

read-Option ändert den Type des Signals

viewChild<ElementRef>(‘x’, { read: ElementRef }) returnt Signal<ElementRef | undefined>. TypeScript kann den Read-Type aus dem Options-Object inferieren, oder du gibst ihn als zweites Generic an. Häufiger Fehler: Component-Klasse als Locator + read: ElementRef ohne Generic — der Type stimmt dann nicht mit dem tatsächlichen Wert überein.

contentChildren() updated bei dynamischer Projektion

Wenn der Parent mit @for verschiedene Tabs projiziert, aktualisiert sich das Array automatisch. Das alte QueryList.changes.subscribe()-Pattern fällt komplett weg — die Reactivity ist Teil des Signal-Werts. Beachte aber: descendants: false (Default) findet nur direkte Kinder; verschachtelte Wrapper machen die Liste leer.

Signal-Queries arbeiten gut mit OnPush und Zoneless

Reactivity ist Teil der Signal-Welt — kein Zone.js-Trigger nötig, keine manuelle markForCheck()-Aufrufe. Genau richtig für die OnPush-Default-Welt ab v22 und für Zoneless-Apps. Klassische Decorator-Queries spielen hier weniger sauber; du brauchst oft ChangeDetectorRef.markForCheck() nach QueryList.changes.

Generic-Typing: explizit bei Template-References

viewChild(MyComp) inferiert den Type automatisch aus der Klasse. Bei String-Locatoren kann TypeScript nicht raten — gib das Generic explizit an: viewChild<ElementRef<HTMLInputElement>>(‘email’). Ohne Generic bekommst du Signal<unknown | undefined>, was in computed() sofort zu Type-Errors führt.

Klassische Decorator-Queries dürfen daneben bestehen

Du kannst @ViewChild und viewChild() in derselben Component mischen — Angular behandelt beide Stile parallel. Praktisch bei schrittweiser Migration eines großen Trees: ein Element pro Sitzung umstellen, der Rest läuft unverändert weiter. Auch @ContentChild + contentChild() sind nebeneinander erlaubt.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Queries & Projection

Zur Übersicht