DEV Community

Gergely Szerovay for This is Angular

Posted on • Edited on • Originally published at angularaddicts.com

Master Angular 16 (a study guide)

The latest version of Angular was released this week. Minko Gechev summarizes the most important new features in his release announcement.

This study guide helps you learn the new version of Angular.

🎯Changes and new features

In this article, I list out the most important changes and new features, also share resources that will teach you how these new Angular features work:

  • Signals
  • DestroyRef
  • takeUntilDestroyed
  • Required inputs
  • Bind Router information to component inputs
  • Node.js v14 support has been removed
  • TypeScript 5.0 Support
  • Angular Compatibility Compiler (ngcc) has been removed
  • Server Side Rendering: Non-destructive hydration + now the results of a request done on the server side can be reused on the client side
  • Strict type checking for ngTemplateOutlet
  • provideServiceWorker function to register service workers in standalone applications
  • Experimental Jest support

Next to these features, Angular 16 has a new Vite + Esbuild based dev server.

Naturally, reading articles about the changes is a good idea, additionally,

also provide great insights, you should check these out, too.


📌Signals

Official docs: Angular Signals
GitHub discussion: Angular Reactivity with Signals
RFC: RFC: Angular Signals
PR: feat(core): add Angular Signals to the public API
PR: Prototype of the RxJS interop layer for signals
PR: feat(core): Mark components for check if they read a Signal

A new primitive type called “Signal” has been introduced in Angular. It’s designed to store a value similar to a regular variable. When the Signal’s value changes, it notifies the interested consumers. Signals store both primitive data types and objects.

A “Computed Signal” derives its value from other Signals. To create a Computed Signal, we need to use the computed function and specify the derivation function.

In case we use the OnPush change detection in a component and it has a Signal in its template, Angular tracks the Signal as a dependency for the component and marks the component for check if the Signal’s value changes. In Angular 17 this will change, it will be possible to use Signal based per-component change detection without zone.js.

Pawel Kozlowski’s talk at the NgBe is a great introduction to Signals, what features are in Angular 16 and what’s expected in Angular 17:

Deborah Kurata wrote a detailed tutorial on Signals, she explained why we need Signals, what they are, and how to create, read and update them. She also demonstrates the concept of computed Signals and effects.

Manfred Steyer compares the current Zone.js based change detection with the new Signal based approach.

Stefan Haas explains how signal and RxJS interoperability works.

Tomas Trajan explains how Signals work using the push & pull concepts.


📌DestroyRef

Official docs: DestroyRef
Commit: feat(core): introduce concept of DestroyRef

DestroyRef lets you set callbacks to run for any cleanup or destruction behavior.

Example:

@Component({
  selector: 'component-1',
  standalone: true,
  template: ``,
})
class Component1 {
  constructor(private destroyRef: DestroyRef) {
    destroyRef.onDestroy(() => {
      // run this cleanup code, when the component is destoryed
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

In his article, Netanel Basal shows an example for how to create a reusable logic that performs a cleanup task when a scope is destroyed.


📌takeUntilDestroyed

Commit: feat(core): implement takeUntilDestroyed in rxjs-interop

takeUntilDestroyed completes an observable in case the calling component (or directive, service or pipe) is destroyed.

In the following example, when the component is created, it starts to show ‘Tick’ messages on the browser’s console, using a subscription to a 2 second interval(). When the component is destroyed, it unsubscribes from the interval():

@Component({
  selector: 'component-1',
  standalone: true,
  template: ``,
})
class Component1 {
  constructor() {
    interval(2000).pipe(takeUntilDestroyed()).subscribe(() => console.log('Tick'));
  }
}
Enter fullscreen mode Exit fullscreen mode

It’s also possible to pass a specific DestroyRef as a parameter to takeUntilDestroyed, so we can create reusable functions with subscriptions that unsubscribe when the context is destroyed:

@Component({
  selector: 'component-1',
  standalone: true,
  template: ``,
})
class Component1 {
  constructor(private destroyRef: DestroyRef) {
    reusableTicks(destroyRef);
  }
}
Enter fullscreen mode Exit fullscreen mode

📌Required inputs

Official docs: Input
PR: feat(compiler): add support for compile-time required inputs

We can mark component- or directive inputs as required. The following example fails in compile time, as in the Component2’s template we didn’t provide the required text input:

@Component({
  selector: 'component-1',
  standalone: true,
  template: ``,
})
class Component1 {
  // ❗ this is a required input
  @Input({ required: true }) text: string;
}

@Component({
  selector: 'component-2',
  standalone: true,
  template: `
    <component-1></component-1>
  `,
  imports: [Component1]
})
class Component2 {
  constructor(private destroyRef: DestroyRef) {
    destroyRef.onDestroy(() => {
      // run this cleanup code, when the component is destoryed
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

📌Bind Router information to component inputs

Official docs: Router / Getting route information, withComponentInputBinding
PR: Feature to bind Router information to component inputs

This new feature simplifies the process of getting route information. Enea Jahollari explains how this new feature works by examples.


📌Node.js v14 support has been removed

Changelog entry: Node.js v14 support has been removed

Node.js v14 is planned to be End-of-Life on 2023–04–30. Angular will stop supporting Node.js v14 in Angular v16. Angular v16 will continue to officially support Node.js versions v16 and v18.


📌TypeScript 5

From Angular’s perspective, the two most important new features in Typescript 5 are:

  • the decorators
  • and the supporting of multiple configuration files in tsconfig.json‘s extends.

Decorators

TypeScript 5 supports a new version of the ECMAScript decorator proposal, referred to as ‘Standard decorators’.

Kevin Kreuzer explains us in his article, how these new decorators work.

Multiple configuration file support in tsconfig.json‘s extends

For example, both strict and noImplicitReturns are enabled in the final tsconfig.json in the following example:

// tsconfig-a.json
{
    "compilerOptions": {
        "strict": true
    }
}

// tsconfig2-b.json
{
    "compilerOptions": {
        "noImplicitReturns": true
    }
}

// tsconfig.json
{
    "extends": ["./tsconfig-a.json", "./tsconfig-b.json"],
    "files": [
      "src/main.ts",
      "src/polyfills.ts"
    ]
}
Enter fullscreen mode Exit fullscreen mode

You can find more details of the new Typescript features in their official announcement.


📌Angular Compatibility Compiler (ngcc) has been removed

Changelog entry: Angular Compatibility Compiler (ngcc) has been removed

Angular 9 transitioned its rendering architecture from the “ View Engine” to “Ivy”. However, to support libraries that were still dependent on the “View Engine”, Angular Compatibility Compiler (ngcc) was created. In Angular 16, ngcc and all other codes related to the old “View Engine” have been removed, so “View Engine” libraries can no longer be used in v16+.

To learn more about “Ivy”, read Maria Korneeva‘s article about how “View Engine” and “Ivy” works and what is new in “Ivy”.


📌NgZone is configurable in bootstrapApplication

Official docs: provideZoneChangeDetection
PR: Add ability to configure NgZone in bootstrapApplication

There are two zone configuration related changes in Angular 16:

class CustomZone extends NoopNgZone {}

bootstrapApplication(
  StandaloneCmp, { 
  providers: [
    // provide a custom zone implementation
    { provide: NgZone, useValue: new CustomZone() }
   ]
  }
);
Enter fullscreen mode Exit fullscreen mode
bootstrapApplication(
  StandaloneCmp, { 
  providers: [
    // enable eventCoalescing and runCoalescing
    provideZoneChangeDetection({
      eventCoalescing: true,
      runCoalescing: true
    })
  ]
});
Enter fullscreen mode Exit fullscreen mode

📌Server Side Rendering improvements

Non-destructive hydration

PR: feat(platform-browser): add a public API function to enable non-destructive hydration

With non-destructive hydration, Angular has the capability of reusing pre-existing DOM structures on the client-side that were initially rendered by the server. This prevents the flicker that occurs when the page is rendered and ready for interaction by users.

The results of the request done on the server side can be reused on the client side

PR: feat(platform-browser): enable HTTP request caching when using provideClientHydration

To avoid the need of refetching the same data on the client side, requests made on the server side will be cached and accessible on the client side.

Jessica Janiuk summarized the current state and future of the server side rendering in her blog post.


📌Strict type checking for ngTemplateOutlet

PR: fix(common): strict type checking for ngtemplateoutlet

Previously, when we passed a context to ngTemplateOutlet, it was passed as an Object, so in the following example there was no compilation error:

interface Context1 {
  prop: number;
}

@component({
  standalone: true,
  imports: [NgTemplateOutlet],
  selector: 'person',
  template: `
    <ng-container
      *ngTemplateOutlet="
        myTemplateRef;
        context: { prop: 1, missingProp: 2 }
      "></ng-container>
  `,
})
export class Component1 {
  templateRef!: TemplateRef<Context1>;
}
Enter fullscreen mode Exit fullscreen mode

Now in Angular 16 the code in the previous example fails with a compile time error, as missingProp is not defined in Context1. We can fix this by adding the missingProp property to Context1 or add $any(…) inside the template to preserve the previous behavior.


📌provideServiceWorker function to register service workers in standalone applications

Official docs: Service workers / provideServiceWorker
PR: feat(service-worker): add function to provide service worker

Previously to register a service worker in a standalone app, we had to use importProvidersFrom(ServiceWorkerModule.register(…)).

provideServiceWorker is a new way to register service workers in standalone apps, usage example:

bootstrapApplication(AppComponent, {
 providers: [
   provideServiceWorker('my-service--worker.js')
 ],
});
Enter fullscreen mode Exit fullscreen mode

📌Experimental Jest support

The Angular Team added initial experimental support for Jest in Angular 16. In Angular 17, they’ll replace the deprecated Karma test runner with the Web Test Runner.


➕ Have you seen any other resources I should add to this Angular 16 Study Guide? Please send it to me so that I can feature it in the article!


👨‍💻About the author

My name is Gergely Szerovay, I work as a frontend development chapter lead. Teaching (and learning) Angular is one of my passions. I consume content related to Angular on a daily basis — articles, podcasts, conference talks, you name it.

I created the Angular Addict Newsletter so that I can send you the best resources I come across each month. Whether you are a seasoned Angular Addict or a beginner, I got you covered.

Next to the newsletter, I also have a publication called — you guessed it — Angular Addicts. It is a collection of the resources I find most informative and interesting. Let me know if you would like to be included as a writer.

Let’s learn Angular together! Subscribe here 🔥

Follow me on Medium, Twitter or LinkedIn to learn more about Angular!

Top comments (0)