Angular v16 : Game Changer

Sagarnath S
3 min readMay 7, 2023
Angular v16

Angular Signals: Signals are the new reactive primitive provided by angular, which will help framework track changes to its model.

A signal is a value with explicit change semantics. In Angular a signal is represented by a zero argument getter function returning the current signal value

interface Signal<T> {
(): T;
[SIGNAL]: unknown;
}

The getter function is marked with the SIGNAL symbol so the framework can recognize signals and apply internal optimizations.

Signals are fundamentally read-only: we can ask for the current value and observe change notification.

The Angular signals library will provide a default implementation of the writable signal that can be changed through the built-in modification methods.

  1. set(value: T) : void; — Directly set the signal to a new value, and notify any dependents.
    Useful for changing primitive values or replacing data structures when the new value is independent of the old one.
  2. update(updateFn : (value : T) => T): void; — Update the value of the signal based on its current value, and notify any dependents.
    Useful for making internal changes to the signal’s value without changing its identity, such as pushing to an array stored in the signal.
  3. mutate(mutatorFn : (value: T) => void): void; — Return a non-writable `Signal` which accesses this `WritableSignal` but does not allow mutation.
@Component({
selector: 'app-signal',
templateUrl: './signal.component.html',
standalone: true,
imports: [CommonModule]
})
export class SignalComponent {
actions = signal<string[]>([]);
counter = signal(0);
increment(){
this.counter.set(this.counter +1);
this.actions.mutate((oldValue) => oldValue.push('Increment'));
}
decrement() {
this.actions.update((oldValue) => oldValue - 1);
this.actions.mutate((oldValue) => oldValue.push('Increment'));
}
}
<h1>Signals</h1>
<div id="counter">
<div id="counter-btns">
<button (click)="decrement ()">Decrement</button>
<button (click)="increment()">Increment</button>
</div>
<div>
<h2>Active Logs</h2>
<ol id="log">
<li *ngFor="let action of actions()">{{action}}</li>
</ol>

Advancing developer tooling: In the next release of Angular, Esbuild can also be used during development.
To enable this we need to change "@angular-devkit/build-angular:browser" to "@angular-devkit/build-angular:browser-esbuild"

...
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser-esbuild",
...

Required inputs: A much-awaited feature for the community was the ability to make certain inputs required.
it has not been possible to get a compile-time error if you don’t specify a value for a specific input. The change adds zero overhead at runtime since the Angular compiler performs the check at build time.

In v16 now you can mark an input as required:

@Component(...)
export class App {
@Input({ required: true }) content: string = '';
}

Passing router data as component inputs: When routing to a component, one must inject the ActivatedRoute service to retrieve route data for use inside the component.

export const routes: Routes = [
{ path: 'home',
component: SearchComponent,
resolve: { address: () => address()}
}
]
//Currently Implemention
@Component({...})
export class HomeComponent {
readonly #activateRoute = inject(ActivatedRoute);
readonly address$ = this.#activatedRoute.data.(map(({ address}) => address)

When defining routes, specific params could be used to provide values for @inputs within the component.

Now you can pass the following data to a routing component’s inputs:

  • Route data — resolvers and data properties
  • Path parameters
  • Query parameters
@Component({...})
export class HomeComponent {
@Input() address!: AddressDetails
}

Self-closing tags : It’s a small developer experience improvement that could save you some typing.

//Now 
<my-self-closing-component [input]="data"></my-self-closing-component>
//New
<my-self-closing-component [input]="data"/>

DestroyRef injector: This new feature allows you to inject DestroyRef corresponding to a component, directive, service or a pipe and register the onDestroy lifecycle hook. The DestroyRef can be injected anywhere within an injection context, including outside of your component in such case the onDestroy hook is executed when a corresponding injector is destroyed.

@Component({...})
export class AppComponent {
constructor() {
inject(DestroyRef).onDestroy(() => {
// Writte your cleanup logic
})
}
}

Thank you for reading if you have any doubts. drop the message in comments.

Follow me on Medium or LinkedIn to read more about Angular and TS!

--

--

Sagarnath S

Software Development Engineer - I @CareStack || Web Developer || Angular || ASP.Net Core || C# || TypeScript || HTML || CSS