The New Angular: Maximizing Performance and Productivity (2024)

Table of Contents
The New Angular Maximizing Performance and Productivity Christian Liebel @christianliebel Hello, it’s me. The New Angular Maximizing Performance and Productivity The New Angular Maximizing Performance and Productivity “Angular Renaissance” Angular 16+ – Signals, Signal-based inputs (…and a Zone.js-less future) Angular 16+ But: The shape of Angular apps changes significantly. Signals Standalone Components and APIs Built-in Control Flow and Deferrable Overview – A signal is a reactive primitive: a wrapper API – Writable Signals const count = signal(0); count.set(3); – RxJS Interop – Convert observable to signal counter$ = interval(1000); OLD Zone.js-based Change Detection The New Angular Maximizing Performance and POSSIBLE Future Change Detection The New Angular Maximizing Performance and Signal-based Components (since Angular 17.1) @Component({ selector: "counter", standalone: true, Signal-based Components (since Angular 17.2) @Component({ selector: "counter", standalone: true, Signal-based Components (since Angular 17.3) @Component({ selector: "counter", standalone: true, Signal-based Components (since Angular 17.2) @ViewChild(MyComponent) a!: MyComponent; @ViewChilren(MyComponent) b!: Zoneless Change Detection (since Angular 18.0) bootstrapApplication(AppComponent, { providers: [ NgRx Signal Store – Reactive state management solution based on Maturity – effect(), RxJS interop, Signal-based components, and zoneless change Migration Strategy – Why? You should migrate to Signals, as Signals Standalone Components and APIs Built-in Control Flow and Deferrable The New Angular Maximizing Performance and Productivity NgModules UI-related components @NgModule({ declarations: [AppComponent, HomeComponent, MenuComponent], imports: [BrowserModule], providers: [DataService], bootstrap: The problem with modules… @Component({ selector: 'app-my', template: `<a *ngFor="let @Component({ selector: 'app-my', template: `<a *ngFor="let item of items" [appTooltip]="item.tooltip">{{ Interoperability with NgModules – Standalone components/directives/pipes can’t be part of Interoperability with NgModules @Component({ selector: 'app-my', template: `<a *ngFor="let item Web Components Streamlined standalone API simplifies the creation of web The New Angular Maximizing Performance and Productivity Folder Structure Modules main.ts app/app-routing.module.ts app/app.module.ts Standalone Apps main.ts app/app.routes.ts Bootstrap API Modules import { platformBrowser } from '@angular/platform-browser'; import Providers Modules @NgModule({ imports: [ BrowserModule, RouterModule.forRoot(routes), HttpClientModule ] }) Environment Injectors Modules @NgModule({ providers: [{ provide: PhotosService, useClass: CustomPhotosService, Migration Strategy – Maturity: Standalone components and APIs are fully How? – Migration can be partly automated – Manual changes Migration ng g @angular/core:standalone The New Angular Maximizing Performance and Signals Standalone Components and APIs Built-in Control Flow and Deferrable If <ng-container *ngIf="item; else notFound"> <app-component [item]="item" /> </ng-container> <ng-template Motivation – Control flow is built into the renderer, so Switch <ng-container [ngSwitch]="condition"> <ng-container *ngSwitchCase="caseA"> Case A. </ng-container> <ng-container *ngSwitchDefault> For <li *ngFor="let item of items; trackBy: trackByFn"> {{ item.name Migration Strategy – Maturity: The built-in control flow is fully How? – Migration can be automated – Target path can How? – ⚠ @for tracks the count variable by default Migration ng g @angular/core:control-flow The New Angular Maximizing Performance and Motivation – Takes lazy loading a step further by bringing Examples @defer (on viewport) { <calendar-cmp /> } @placeholder { Signals Standalone Components and APIs Built-in Control Flow and Deferrable Motivation – Server-side rendering (SSR) renders the application on the Motivation <html> <body> <app-root></app-root> </body> </html> <html> <body> <app-root _nghost-ng-c1888335296="" In Angular 17 – SSR is now fully integrated with Caveats – ⚠ Setting up SSR with authentication is hard, Motivation The JavaScript ecosystem has advanced and new (and significantly Motivation – Significantly faster build times (initial/incremental) – Compatibility with Motivation – Maturity: New apps created from Angular 17 onwards Migration ng update @angular/cli --name use-application-builder The New Angular Maximizing The “Angular Renaissance” is real Notable advancements while being backwards Thank you for your kind attention! Christian Liebel @christianliebel [emailprotected]
  • The New Angular Maximizing Performance and Productivity Christian Liebel @christianliebel

    Consultant

  • Hello, it’s me. The New Angular Maximizing Performance and Productivity

    Christian Liebel X: @christianliebel Email: christian.liebel @thinktecture.com Angular & PWA Slides: thinktecture.com /christian-liebel

  • The New Angular Maximizing Performance and Productivity “Angular Renaissance”

  • Angular 16+ – Signals, Signal-based inputs (…and a Zone.js-less future)

    – Standalone Components/APIs – Built-in Control Flow/Deferrable Views – Server-Side Rendering/Hydration (…and new build tools: esbuild & Vite) Goals: Streamline & modernize APIs & improve performance The New Angular Maximizing Performance and Productivity “Angular Renaissance”

  • Angular 16+ But: The shape of Angular apps changes significantly.

    You can tell the difference between an Angular 2 and an Angular 18 app. No worries: All new features are backwards compatible & interoperable. Not another AngularJS scenario. Still: You should migrate your applications as soon as possible. In this session, we talk about why, when and how you should migrate. The New Angular Maximizing Performance and Productivity “Angular Renaissance”

  • Signals Standalone Components and APIs Built-in Control Flow and Deferrable

    Views Server-Side Rendering, esbuild and Vite The New Angular Maximizing Performance and Productivity Agenda

  • Overview – A signal is a reactive primitive: a wrapper

    around a value that notifies interested consumers when that value changes – The signal concept is a lot simpler than RxJS (= streamlined APIs) – Signals form the basis for a new, more effective change detection system in Angular that could eventually replace Zone.js (= performance improvement) – Works great with ChangeDetectionStrategy.OnPush The New Angular Maximizing Performance and Productivity Signals

  • API – Writable Signals const count = signal(0); count.set(3); –

    Computed Signals const doubleCount = computed(() => count() * 2); – Effects effect(() => console.log(`The count is: ${doubleCount()}`)); The New Angular Maximizing Performance and Productivity Signals https://angular.dev/guide/signals

  • RxJS Interop – Convert observable to signal counter$ = interval(1000);

    counter = toSignal(this.counter$, {initialValue: 0}); – Convert signal to observable counter = signal(0); counter$ = toObservable(this.counter); – takeUntilDestroyed() The New Angular Maximizing Performance and Productivity Signals https://angular.dev/guide/signals/rxjs-interop

  • OLD Zone.js-based Change Detection The New Angular Maximizing Performance and

    Productivity Signals current (global) zone NgZone Angular boot onclick setTimeout Detect changes Detect changes <app-root> <app-nav> <app-content> <app-list> CHANGE {{ binding }} {{ binding }} {{ binding }} {{ binding }}

  • POSSIBLE Future Change Detection The New Angular Maximizing Performance and

    Productivity Signals CHANGE <app-root> <app-nav> <app-content> <app-list> {{ binding }} {{ signal() }} {{ binding }} {{ binding }}

  • Signal-based Components (since Angular 17.1) @Component({ selector: "counter", standalone: true,

    template: "{{ value }}", }) export class CounterComponent { @Input() value = 0; @Input({ required: true }) value2!: number; } @Component({ selector: "counter", standalone: true, template: "{{ value() }}", }) export class CounterComponent { value = input(0); value2 = input.required<number>(); } The New Angular Maximizing Performance and Productivity Signals https://blog.angular-university.io/angular-signal-inputs/ OLD NEW

  • Signal-based Components (since Angular 17.2) @Component({ selector: "counter", standalone: true,

    template: "{{ value }}", }) export class CounterComponent { @Input({ required: true }) value = 0; @Output() valueChange = new EventEmitter<number>(); } @Component({ selector: "counter", standalone: true, template: "{{ value() }}", }) export class CounterComponent { value = model.required<number>(); } The New Angular Maximizing Performance and Productivity Signals https://blog.angular-university.io/angular-signal-inputs/ OLD NEW

  • Signal-based Components (since Angular 17.3) @Component({ selector: "counter", standalone: true,

    template: "", }) export class CounterComponent { @Output() count = new EventEmitter<number>(); } @Component({ selector: "counter", standalone: true, template: "", }) export class CounterComponent { count = output<number>(); } The New Angular Maximizing Performance and Productivity Signals OLD NEW

  • Signal-based Components (since Angular 17.2) @ViewChild(MyComponent) a!: MyComponent; @ViewChilren(MyComponent) b!:

    QueryList<MyComponent>; @ContentChild(MyComponent) c!: MyComponent; @ContentChildren(MyComponent) d!: QueryList<MyComponent>; a = viewChild(MyComponent); b = viewChildren(MyComponent); c = contentChild(MyComponent); d = contentChildren(MyComponent); The New Angular Maximizing Performance and Productivity Signals OLD NEW

  • Zoneless Change Detection (since Angular 18.0) bootstrapApplication(AppComponent, { providers: [

    provideExperimentalZonelessChangeDetection(), ], }); The New Angular Maximizing Performance and Productivity Signals NEW

  • NgRx Signal Store – Reactive state management solution based on

    Signals – Simple and lightweight – Package: @ngrx/signals – Replacement for component store and entity adapter – Function-based, but can be class-based The New Angular Maximizing Performance and Productivity Signals https://ngrx.io/guide/signals

  • Maturity – effect(), RxJS interop, Signal-based components, and zoneless change

    detection are still in developer preview (as of Angular 18.0.0) – @ngrx/signals is still in developer preview (as of version 17.2.0) The New Angular Maximizing Performance and Productivity Signals

  • Migration Strategy – Why? You should migrate to Signals, as

    they improve performance and API simplicity. – When? You should write new components based on Signals and migrate older components if time permits. Currently, there’s no hurry: RxJS and the traditional ways in Angular won’t go away anytime soon, and some Signal APIs and frameworks are still in developer preview. – How? Manual effort required, no automated migration available. (Could probably be handled by GenAI.) The New Angular Maximizing Performance and Productivity Signals

  • Signals Standalone Components and APIs Built-in Control Flow and Deferrable

    Views Server-Side Rendering, esbuild and Vite The New Angular Maximizing Performance and Productivity Agenda

  • The New Angular Maximizing Performance and Productivity NgModules UI-related components

    (BookModule) UI-related components (TodoModule) Logic/ infrastructure components (BookModule) Logic/ infrastructure components (TodoModule) Components Directives Pipes High-Level Services Low-Level Services

  • @NgModule({ declarations: [AppComponent, HomeComponent, MenuComponent], imports: [BrowserModule], providers: [DataService], bootstrap:

    [AppComponent], }) export class AppModule { } The New Angular Maximizing Performance and Productivity NgModules

  • The problem with modules… @Component({ selector: 'app-my', template: `<a *ngFor="let

    item of items" [appTooltip]="item.tooltip">{{ item.name | myPipe }}</a>`, }) export class MyComponent { @Input({ required: true }) items!: Item[]; } Question: Where does appTooltip and myPipe come from? The New Angular Maximizing Performance and Productivity NgModules OLD

  • @Component({ selector: 'app-my', template: `<a *ngFor="let item of items" [appTooltip]="item.tooltip">{{

    item.name | myPipe }}</a>`, standalone: true, imports: [NgFor, TooltipDirective, MyPipe] }) export class MyComponent { @Input({ required: true }) items!: Item[]; } The New Angular Maximizing Performance and Productivity Standalone Components NEW

  • Interoperability with NgModules – Standalone components/directives/pipes can’t be part of

    NgModules – When used in non-standalone components, it needs to be imported @NgModule({ declarations: [MyStandaloneComponent], imports: [MyStandaloneComponent], }) export const MyModule {} The New Angular Maximizing Performance and Productivity Standalone Components

  • Interoperability with NgModules @Component({ selector: 'app-my', template: `<a *ngFor="let item

    of items" [appTooltip]="item.tooltip">{{ item.name | myPipe }}</a>`, standalone: true, imports: [CommonModule, TooltipModule, PipeModule] }) export class MyComponent { @Input({ required: true }) items!: Item[]; } The New Angular Maximizing Performance and Productivity Standalone Components

  • Web Components Streamlined standalone API simplifies the creation of web

    components from Angular components Web components can be used in any context (not only Angular apps) ⚠ Problem: Zone.js is still used for change detection, the Angular team is actively working on making this optional The New Angular Maximizing Performance and Productivity Standalone Components

  • The New Angular Maximizing Performance and Productivity

  • Folder Structure Modules main.ts app/app-routing.module.ts app/app.module.ts Standalone Apps main.ts app/app.routes.ts

    app/app.config.ts The New Angular Maximizing Performance and Productivity Standalone Applications OLD NEW

  • Bootstrap API Modules import { platformBrowser } from '@angular/platform-browser'; import

    { AppModule } from './app/app.module'; platformBrowser() .bootstrapModule(AppModule) .catch(e => console.error(e)); Standalone Apps import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; bootstrapApplication(AppComponent) .catch(e => console.error(e)); The New Angular Maximizing Performance and Productivity Standalone Applications OLD NEW

  • Providers Modules @NgModule({ imports: [ BrowserModule, RouterModule.forRoot(routes), HttpClientModule ] })

    export class AppModule { } Standalone Apps export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes), provideHttpClient(), // fallback for modules: importProvidersFrom (TranslateModule) ] }; The New Angular Maximizing Performance and Productivity Standalone Applications OLD NEW

  • Environment Injectors Modules @NgModule({ providers: [{ provide: PhotosService, useClass: CustomPhotosService,

    }] }) export class MyModule { constructor() { // My logic } } Standalone Apps createEnvironmentInjector([ { provide: PhotosService, useClass: CustomPhotosService }, { provide: ENVIRONMENT_INITIALIZER, useValue: () => {/* My logic */} } ]); The New Angular Maximizing Performance and Productivity Standalone Applications OLD NEW

  • Migration Strategy – Maturity: Standalone components and APIs are fully

    stable. – Why? Standalone components and APIs improve developer experience and prepare your codebase for upcoming Angular features. – When? New projects should be started using standalone APIs, new components should be added as standalone components (default since version 17), NgModules and standalone are interoperable. – Existing projects should be migrated as soon as possible, new parts should be added using standalone components/APIs. – ⚠ Some ng add/ng update integrations may not work with standalone workspaces. The New Angular Maximizing Performance and Productivity Standalone Components & APIs

  • How? – Migration can be partly automated – Manual changes

    will be required – Effort for mid-size projects: 4–8 hours – No functional difference – ⚠ Changes to the project setup may lead to the migration to break – ⚠ Replacements in unit tests may be incorrect – ⚠ Migration does not introduce the new folder structure (app config) The New Angular Maximizing Performance and Productivity Standalone Components & APIs

  • Migration ng g @angular/core:standalone The New Angular Maximizing Performance and

    Productivity Standalone Components & APIs

  • Signals Standalone Components and APIs Built-in Control Flow and Deferrable

    Views Server-Side Rendering, esbuild and Vite The New Angular Maximizing Performance and Productivity Agenda

  • If <ng-container *ngIf="item; else notFound"> <app-component [item]="item" /> </ng-container> <ng-template

    #notFound> Item not found! </ng-template> @if (item) { <app-component [item]="item" /> } @else { Item not found! } The New Angular Maximizing Performance and Productivity Built-in Control Flow OLD NEW https://angular.dev/essentials/conditionals-and-loops#if-block

  • Motivation – Control flow is built into the renderer, so

    you don’t have to import the structural directives (e.g., CommonModule, NgIf). – Dummy DOM elements such as <ng-container> are no longer needed. – The structural directive syntax (e.g., *ngIf) is not very common. – Better performance (up to 90% for @for), no longer tied to Zone.js. – Improve developer experience when dealing with common scenarios, such as “else” blocks, empty lists or deferring loading certain content. – ⚠ But: Built-in control flow is not extensible, in contrast to structural directives (e.g., *ngxPermission). The New Angular Maximizing Performance and Productivity Built-in Control Flow https://angular.dev/guide/templates/control-flow https://blog.angular.io/introducing-angular-v17-4d7033312e4b

  • Switch <ng-container [ngSwitch]="condition"> <ng-container *ngSwitchCase="caseA"> Case A. </ng-container> <ng-container *ngSwitchDefault>

    Default case. </ng-container> </ng-container> @switch (condition) { @case (caseA) { Case A. } @default { Default case. } } The New Angular Maximizing Performance and Productivity Built-in Control Flow OLD NEW https://angular.dev/guide/templates/control-flow#switch-block---selection

  • For <li *ngFor="let item of items; trackBy: trackByFn"> {{ item.name

    }} </li> <li *ngIf="items.length===0"> No items. </li> @for (item of items; track item.name) { <li> {{ item.name }} </li> } @empty { <li> No items. </li> } The New Angular Maximizing Performance and Productivity Built-in Control Flow OLD NEW https://angular.dev/essentials/conditionals-and-loops#for-block

  • Migration Strategy – Maturity: The built-in control flow is fully

    stable. – Why? Optimizes developer experience for common scenarios and improves performance (Zone.js, mandatory tracking in for loops) – When? Start using control flow now and migrate your codebase as soon as possible. Otherwise, keep using structural directives and migrate later. – Supported by current versions of WebStorm, VS Code and Prettier. – ⚠ But: The syntax does not match the HTML standard and may lead to problems with older IDE versions or tooling that is not aware of it. The New Angular Maximizing Performance and Productivity Built-in Control Flow

  • How? – Migration can be automated – Target path can

    be specified (allowing a step-by-step migration) – Allows formatting affected files – Removes CommonModule/structural directive imports from component class – Manual changes likely required – Effort for mid-size projects: < 1 hour – No functional changes The New Angular Maximizing Performance and Productivity Built-in Control Flow

  • How? – ⚠ @for tracks the count variable by default

    (not necessarily best for performance) – ⚠ The built-in control flow does not support some rarely used features that were previously possible (e.g., *ngFor collection aliasing, multiple *ngIf aliases, …). These usages must be changed before running the migration. – ⚠ Template reformatting does not use Prettier The New Angular Maximizing Performance and Productivity Built-in Control Flow

  • Migration ng g @angular/core:control-flow The New Angular Maximizing Performance and

    Productivity Built-in Control Flow

  • Motivation – Takes lazy loading a step further by bringing

    it to the template level, which can improve the load time performance of your application. – Loads a (large/rarely used) view only based on a certain trigger (e.g., when the template enters the viewport, the device is idle, …). – Everything uses template magic, enabled by the new @defer block. – Offers helpers like @placeholder (while the content is being loaded), or @error when the content fails to load. – An example for a feature that is only accessible via the new syntax. The New Angular Maximizing Performance and Productivity Deferrable Views

  • Examples @defer (on viewport) { <calendar-cmp /> } @placeholder {

    <div>Calendar placeholder</div> } @error { Failed to load the calendar! } The New Angular Maximizing Performance and Productivity Deferrable Views https://angular.dev/guide/defer

  • Signals Standalone Components and APIs Built-in Control Flow and Deferrable

    Views Server-Side Rendering, esbuild and Vite The New Angular Maximizing Performance and Productivity Agenda

  • Motivation – Server-side rendering (SSR) renders the application on the

    server, significantly improving load time performance – Prerendering/static site generation (SSG) renders all static HTML files during build time, improving server response time – Hydration allows client-side Angular to reattach to the server- rendered DOM nodes during launch, improving client-side load time – Partial Hydration (implementation in progress) renders the content of a @defer block on the server and reattaches on the client. The New Angular Maximizing Performance and Productivity Server-Side Rendering

  • Motivation <html> <body> <app-root></app-root> </body> </html> <html> <body> <app-root _nghost-ng-c1888335296=""

    ng- version="17.1.3" ngh="0" ng-server- context="ssr"><main _ngcontent-ng- c1888335296="" class="main"><div _ngcontent- ng-c1888335296="" class="content"><div _ngcontent-ng-c1888335296="" class="left- side"><svg _ngcontent-ng-c1888335296="" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 982 239" fill="none" class="angular-logo"><!-- -- ></svg></a></div></div></div></main><router- outlet _ngcontent-ng- c1888335296=""></router-outlet><!-- container--></app-root> </body> </html> The New Angular Maximizing Performance and Productivity Server-Side Rendering No SSR SSR

  • In Angular 17 – SSR is now fully integrated with

    Angular and includes SSR, prerendering, and hydration – Angular Universal is no longer offered as a separate library – For new projects, SSR can be easily enabled (e.g., ng new <name> --ssr) – Existing projects first have to switch to the new application builder and update their configuration The New Angular Maximizing Performance and Productivity Server-Side Rendering

  • Caveats – ⚠ Setting up SSR with authentication is hard,

    SSG may be impossible. – ⚠ Custom or Noop Zone.js are not yet supported. – ⚠ Angular i18n with hydration is not yet supported. – ⚠ When the Angular Service Worker is used, SSR is only used on the first visit. – ⚠ All components must be built for hydration. – ⚠ Libraries that depend on DOM manipulation (D3 charts, grids, …) may break hydration. Skipping hydration is possible via ngSkipHydration, but you’ll lose the advantages of hydration. The New Angular Maximizing Performance and Productivity Server-Side Rendering

  • Motivation The JavaScript ecosystem has advanced and new (and significantly

    faster) build tools have arrived. The New Angular Maximizing Performance and Productivity esbuild and Vite https://esbuild.github.io/

  • Motivation – Significantly faster build times (initial/incremental) – Compatibility with

    native ECMAScript Modules (ESM) and the modern JavaScript ecosystem – Hot module replacement (HMR) support for styles (performance improvement) The New Angular Maximizing Performance and Productivity esbuild and Vite https://blog.angular.io/introducing-angular-v17-4d7033312e4b

  • Motivation – Maturity: New apps created from Angular 17 onwards

    will automatically use the application builder – Why? Significantly faster build times, modern JavaScript – When? The existing Webpack-based build system is still stable. Old apps will not be migrated automatically. As Angular will move on, you should migrate as time permits. – How? Aim for application; if that is not possible, try browser-esbuild. Just replace the builders in angular.json and see if the build works. If not, investigate and estimate the effort. The New Angular Maximizing Performance and Productivity esbuild and Vite

  • Migration ng update @angular/cli --name use-application-builder The New Angular Maximizing

    Performance and Productivity esbuild and Vite

  • The “Angular Renaissance” is real Notable advancements while being backwards

    compatible Increased build/runtime performance and developer experience Keep your users and developers happy Angular 17.1+ apps will look significantly different to previous versions Avoid technical debt, migrate now The New Angular Maximizing Performance and Productivity Summary

  • Thank you for your kind attention! Christian Liebel @christianliebel [emailprotected]

  • The New Angular: Maximizing Performance and Productivity (2024)
    Top Articles
    Latest Posts
    Article information

    Author: Dong Thiel

    Last Updated:

    Views: 6420

    Rating: 4.9 / 5 (59 voted)

    Reviews: 90% of readers found this page helpful

    Author information

    Name: Dong Thiel

    Birthday: 2001-07-14

    Address: 2865 Kasha Unions, West Corrinne, AK 05708-1071

    Phone: +3512198379449

    Job: Design Planner

    Hobby: Graffiti, Foreign language learning, Gambling, Metalworking, Rowing, Sculling, Sewing

    Introduction: My name is Dong Thiel, I am a brainy, happy, tasty, lively, splendid, talented, cooperative person who loves writing and wants to share my knowledge and understanding with you.