Angular 17.2 shipped, and with it came official confirmation and reasoning that the team intends to phase out RxJs as a mandatory dependency down the line.
Angular 17.2
With the release of Angular 17.2, components are nearing Signal Component status. The need for zone.js or an EventEmitter hasn't disappeared entirely, yet a substantial portion of the "Component API" is now Signal-compatible.
model()
The model() function yields a writable Signal that appears to the parent component as a two-way binding mechanism.
Before:
@Component({
selector: 'app-tags',
template: `<h3>Select Tags</h3>
@for (tag of existingTags; track tag) {
<button class="p-4 border-2 cursor-pointer" (click)="toggleTag(tag)">
{{ tag }}
</button>
}`,
standalone: true,
})
export class TagsComponent {
existingTags = getTags();
@Input() tags: string[] = [];
@Output() tagsChange = new EventEmitter<string[]>();
toggleTag(tag: string) {
if (this.tags.includes(tag)) {
this.tags = this.tags.filter((value) => value !== tag);
} else {
this.tags = [...this.tags, tag];
}
this.tagsChange.emit(this.tags);
}
}
Following that:
@Component({
selector: 'app-tags',
template: `<h3>Select Tags</h3>
@for (tag of existingTags; track tag) {
<button class="p-4 border-2 cursor-pointer" (click)="toggleTag(tag)">
{{ tag }}
</button>
}`,
standalone: true,
})
export class TagsComponent {
existingTags = getTags();
tags = model<string[]>([]);
toggleTag(tag: string) {
const tags = this.tags();
if (tags.includes(tag)) {
this.tags.update((tags) => tags.filter((value) => value !== tag));
} else {
this.tags.update((tags) => [...tags, tag]);
}
}
}
"Banana Box" and Writable Signal
Beyond the new model function, the banana box syntax now extends its support to writable Signals:
Earlier:
@Component({
selector: 'app-tags-selector',
template: `
<app-tags [(tags)]="tags"></app-tags>
<p>Selected Tags: {{ prettyTags }}</p>
`,
standalone: true,
imports: [TagsComponent, JsonPipe],
})
export class TagsSelectorComponent {
tags: string[] = [];
get prettyTags() {
return this.tags.join(', '); // performance issue
}
}
Following that:
@Component({
selector: 'app-tags-selector',
template: `
<app-tags [(tags)]="tags"></app-tags>
<p>Selected Tags: {{ prettyTags() }}</p>
`,
standalone: true,
imports: [TagsComponent, JsonPipe],
})
export class TagsSelectorComponent {
tags = signal<string[]>([]);
prettyTags = computed(() => this.tags().join(', '));
}
Queries
Just as anticipated, signal-based counterparts for ViewChild and ContentChild are here too. These take the form of plain functions, requiring no decorator at all.
They remain in developer preview at the moment, yet once they reach production stability, they shift from being a mere option to becoming the standard approach for crafting state-of-the-art Angular applications.
The model and the query each offer a variant built around a required function—a concept first introduced alongside the Signal Input in 17.1.
Previously:
@Component({
template: `
<form>
<input [(ngModel)]="user.firstname" name="firstname" />
<input [(ngModel)]="user.lastname" name="lastname" />
</form>
`,
standalone: true,
imports: [FormsModule],
})
export class IntroductionComponent implements AfterViewInit {
@ViewChild(NgForm) ngForm: NgForm | undefined;
ngAfterViewInit(): void {
if (!this.ngForm) {
console.error('form is not available');
return;
}
this.ngForm.form.valueChanges.subscribe(console.log);
}
user = { firstname: 'Konrad', lastname: 'Huber' };
}
Following that:
@Component({
template: `
<form>
<input [(ngModel)]="user.firstname" name="firstname" />
<input [(ngModel)]="user.lastname" name="lastname" />
</form>
`,
standalone: true,
imports: [FormsModule],
})
export class IntroductionComponent {
ngForm = viewChild.required(NgForm);
constructor() {
effect(() => this.ngForm().form.valueChanges.subscribe(console.log));
}
user = { firstname: 'Konrad', lastname: 'Huber' };
}
With required, the undefined is stripped from the Signal's underlying type. This shifts the failure mode from something that could occur at compile time to an error thrown at runtime.
The onus is on us to avoid reading it prematurely. For queries, afterNextRender is the place to handle it.
As a rule, accessing the Signal in the template, inside a computed, or within an effect keeps us out of trouble.
The 17.2 release also brings support for Bun as an alternative to Node.js, along with NgOptimizedImage additions for loading images from Netlify. SSR support in DevTools received upgrades too. Material 3 is available experimentally.
Further Reading
Angular 17.2 novelties - Ninja Squad
The release of Angular 17.2 has arrived.
Signals for Component Interaction: Inputs, Bidirectional Bindings, plus Content and View Queries - ANGULARarchitects
Angular 17.2 Release: Key Updates
This walkthrough covers everything introduced in Angular 17.2—from the signal-driven view queries (viewChild, viewChildren, contentChild, contentChildren) to the fresh model() two-way binding syntax.
Optional RxJs
Alongside the release of Angular 17.2, we now have the first concrete commitment from the Angular team that RxJs will become optional. This shift will necessitate significant modifications to the library's interface, which the team plans to roll out gradually across several major version updates.
