Querying Made Easy: Exploring Angular’s Query Signals

Netanel Basal
Netanel Basal
Published in
3 min readFeb 9, 2024

--

The release of version v17.2.0-rc.0 marks another significant step in the journey of signals within Angular with the introduction of Queries Signals. This feature brings a set of new functions to perform queries: viewChild, viewChildren, contentChild, and contentChildren.

View Child and View Children API

import { viewChild, viewChildren } from '@angular/core';

@Component({
standalone: true,
imports: [FooComponent],
template: `
<div #el></div>
<ng-template #tpl />
<app-foo />
`,
})
export class UserProfileComponent {
// Signal<ElementRef<HTMLDivElement>>
divEl = viewChild<ElementRef<HTMLDivElement>>('el');

// Signal<FooComponent>
fooComp = viewChild.required(FooComponent);

// Signal<ViewContainerRef>
vcr = viewChild.required('tpl', { read: ViewContainerRef );

// Signal<readonly ElementRef<HTMLDivElement>[]>
divEls = viewChildren<ElementRef<HTMLDivElement>>('el');
}

This API supports everything that the decorator supports, with the added feature of defining the query input as required, which works only with single elements. This eliminates the need for QueryList or the AfterContentInit and AfterViewInit hooks as we can now use computed or effect as we do with any signal.

It also enables communication with a child component using signals, opening up possibilities for performing various interactions and implementing intriguing functionalities.

Content Child and Content Children API

The content child and content children APIs mirror their counterparts for view queries.

import { contentChild, contentChildren } from '@angular/core';

@Component({
standalone: true,
template: `<ng-content />`,
})
export class UserProfileComponent {
// Signal<ElementRef<HTMLDivElement>>
divEl = contentChild<ElementRef<HTMLDivElement>>('el');

// Signal<FooComponent>
fooComp = contentChild.required(FooComponent);

// Signal<ViewContainerRef>
vcr = contentChild.required('tpl', { read: ViewContainerRef );

// Signal<readonly ElementRef<HTMLDivElement>[]>
divEls = contentChildren<ElementRef<HTMLDivElement>>('el');
}

Quick Tabs Implementation using the New API

@Component({
selector: 'app-tabs',
standalone: true,
imports: [NgTemplateOutlet],
template: `
@for(tab of tabs(); track tab.title()) {
<button (click)="activeTabId.set(tab.title())">
{{ tab.title() }}
</button>
}

<ng-template [ngTemplateOutlet]="selectedTabTpl()" />
`,
})
export class TabsComponent {
tabs = contentChildren(TabDirective);
activeTabId = signal<string | null>(null);

selectedTabTpl = computed(() => {
const tabs = this.tabs();
if (!tabs.length) return null;

const selected = this.activeTabId();

if (!selected) return tabs[0].tpl;

return tabs.find((tab) => tab.title() === selected)!.tpl;
});
}

Tab Directive

@Directive({
selector: '[appTab]',
standalone: true,
})
export class TabDirective {
tpl = inject(TemplateRef);
title = input.required<string>({ alias: 'appTab' });
}

Usage in the Component:

<app-tabs>
<div *appTab="'A'">Tab one content</div>

<div *appTab="'B'">Tab two content</div>

<div *appTab="'C'">Tab three content</div>
</app-tabs>

This streamlined approach to querying within Angular provides more flexibility and simplicity, making it easier to manage component interactions and dynamic content rendering. With Queries Signals, Angular developers can enhance their applications with more expressive code.

Comparing Signal-Based and Decorator-Based Query Methods

  • Enhanced timing predictability: Query results become accessible promptly upon availability
  • Streamlined API interface: Every query yields a signal, and multiple-result queries facilitate interaction through a conventional array
  • Enhanced type safety: A reduction in query scenarios involving undefined results leads to heightened type safety.
  • Refined type inference: TypeScript demonstrates improved capability in deducing precise types with the utilization of type predicates or explicit read options
  • Efficient update handling: Angular adopts a lazier approach to updating signal-based query results, refraining from unnecessary work unless the query results are explicitly accessed in your code

🙏 Support ngneat & Netanel Basal: Get Featured!

Are you passionate about the ngneat open source libraries for Angular or find Netanel Basal’s blog posts invaluable for your learning journey? Show your support by sponsoring my work!

Follow me on Medium or Twitter to read more about Angular and JS!

--

--

A FrontEnd Tech Lead, blogger, and open source maintainer. The founder of ngneat, husband and father.