Last week (May, 22nd) the Angular team released Angular 18. The most important change is the stabilizing new reactivity system for at least the next 10 years of Angular.
So lets take a step back and look at the previous year of releases:
Angular 16 - May 3, 2023
- 3 new reactive primitives, they will be the heart of the new reactivity model
- Signal → Writable signals provide an API for updating their values directly
- Computed → Computed signal are read-only signals that derive their value from other signals
- Effect → An effect is an operation that runs whenever one or more signal values change
Angular 17 - November 8, 2023
- Signal and Computed became stable APIs
Angular 18 - May 22, 2024
- Hybrid change detection (turned on by default for new v18 apps)
- Experimental API to fully disable zonejs, powered by the reactive APIs!
- note: not all api’s work (yet) with this e.g. forms
- SSR dev tool hydration
- Angular Material 3
- Components support hydration
- Theming Api
- SSR Event Replay → Events will be captured and replayed at the right time once the app is hydrated → Powered by JsAction
- Typescript 5.4 support
As you can see the Angular team has put great effort into making the new zoneless APIs more stable and future ready. So how does Angular knows when to run the next digest cycle? The funny thing is that's when you communicate state changes to Angular. For example by setting a signal that’s used in a template. The framework will schedule change detection automatically, without the need of zonejs.
I hear you think but what about major existing applications that consist of thousands of components that are not yet using signals, right? For these applications the goal of the Angular team was to make it possible to run zoneless as well without needing to migrate everything to signals. And of course, don't forget the rich ecosystem of existing Angular libraries that need to be zoneless-compatible without having to make major changes as well.
Well they made it via the hybrid change detection that will stitch zonejs and zoneless seamlessly together!
Lets deep dive a little bit into the new signal based API’s
Signal input API:
The new way to pass data to child components is via the input api. It's signal based and whenever the input value changes, it will run change detection if needed. You can also make an input required, which means that the input value must be set before the component is created.
import { Component, input } from '@angular/core'; @Component({ selector: 'fv-app', template: ` <h1>Hello, {{ name() }}</h1> <h2>The current registered users are: {{ totalUsers() }}</h2> `, }) export class TimelineGroupComponent { name = input<string>(); totalUsers = input.required<number>(0); }
Model input API
The model input api is the new way of two-way binding in Angular. It will not only update the inner state of the component it will also notify the parent if it's listening for updates.
<fv-custom-checkbox [(checked)]="isChecked" (checkedChanged)="runSomeUpdate($event)"> </fv-custom-checkbox>
import { Component, model } from '@angular/core'; @Component({ selector: 'fv-custom-checkbox', template: ` <div class="fv-custom-checkbox"> <input type="checkbox" id="custom-checkbox" (click)="toggle()" [value]="checked()" /> <label for="custom-checkbox">Checkbox with model!</label> </div> `, }) export class CustomCheckboxComponent { protected checked = model(false); toggle() { this.checked.set(!this.checked()); } }
Signal query API
A new way to type-safe interact in a reactive way with content children and view children in templates. This new approach exposes query results as signals, which mean that query results can be composed with other signals using computed or effect and drive change detection. This new signal based query system offers other benefits, like more predictable timing, simpler API, improved type safety, more accurate type inference, and lazier updates.
import { Component, effect, viewChildren } from '@angular/core'; @Component({ selector: 'fv-app', template: ` @for (todo of todos; track todo.id) { <todo [item]="todo" /> } `, }) export class App { children = viewChildren(Todo); constructor() { effect(() => { console.log(this.children().length); }); } }
Small updates: ng-content
Since v18 You can now give a default value to ng-content. This allows you to have placeholder text for empty list or other components with no current content.
Coming up
- Fully support zoneless apps
- Extend the signal support for example on forms and the router
- Full signal components
- SSR rendering mode selection for routes (eg serverside, client side or build side)