Episode 24/07: Angular 17.2, optional RxJs
ng-news
Posted on February 19, 2024
We got Angular 17.2 and a written confirmation along with an explanation that the Angular team wants to make RxJs optional in the upcoming years.
Angular 17.2
Angular 17.2 is out. Due to new features, a component is now almost a Signal Component. We still require zone.js or an EventEmitter, but quite a lot of the "Component API' works now with a Signal.
model()
The model function creates a writable Signal, presenting itself to the parent component as two-way binding.
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);
}
}
After:
@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
Separated from the new model function, the banana box syntax now supports also writable Signals:
Before:
@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
}
}
After:
@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
As planned, we also have signal-based alternatives for ViewChild and ContentChild. They are also simple functions and go without a decorator.
These features are still in developer preview, but as soon as they go production-ready, they're no longer an alternative but the preferred way to write modern Angular applications.
The model and the query come in a variation with a required function. The required function is something we already saw with the Signal Input in 17.1.
Before:
@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' };
}
After:
@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' };
}
Required removes the undefined from the Signal's underlying type. We are switching from a potential compilation to a runtime error.
It is our responsibility that we don't directly access it too early. For the queries, we can use afterNextRender.
In general, we are safe if we access the Signal in the template or via a computed or effect.
Other features in 17.2 are the support for Bun, an alternative to Node.js, and the NgOptimizedImage, which supports loading images from Netlify. And the DevTools also received improvements for SSR. We have experimental support for Material 3.
Further Reading
Optional RxJs
Next to Angular 17.2, we have, for the first time, written confirmation that the Angular team will make RxJs optional. That will require huge changes to the API, which will gradually happen over multiple major releases.
Posted on February 19, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.