Angular 17: A New Era
November 8th marked a significant milestone in the world of Angular with the release of Angular 17. This wasn't just any ordinary update; it was a leap forward, signifying a new chapter for the popular framework. But what made this release truly stand out was the unveiling of Angular's revamped website, complete with a fresh brand identity and a new logo. This significant transformation represents the evolving nature of Angular, aligning with the modern demands of web development.
To commemorate this launch, we also hosted a release afterparty, where we went deep into its new features with Minko Gechev from the Angular core team, and Google Developer Experts (GDEs) Brandon Roberts, Deborah Kurata, and Enea Jahollari.
But what exactly are these notable new features in the latest version? Let's dive in and explore.
The Angular Renaissance
Angular has been undergoing a significant revival, often referred to as Angular's renaissance, a term coined by Sarah Drasner, the Director of Engineering at Google, earlier this year. This revival has been particularly evident in its recent versions. The Angular team has worked hard to introduce many new improvements, focusing on signal-based reactivity, hydration, server-side rendering, standalone components, and migrating to esbuild and Vite for a better and faster developer experience. This latest release, in particular, marks many of these features as production-ready.
Standalone Components
About a year ago, Angular began a journey toward modernity with the introduction of standalone components. This move significantly enhanced the developer experience, making Angular more contemporary and user-friendly.
In Angular's context, a standalone component is a self-sufficient, reusable code unit that combines logic, data, and user interface elements. What sets these components apart is their independence from Angular's NgModule system, meaning they do not rely on it for configuration or dependencies. By setting a standalone: true
flag, you no longer need to embed your component in an NgModule and you can bootstrap directly off that component:
// ./app/app.component.ts
@Component({ selector: 'app', template: 'hello', standalone: true })
export class AppComponent {}
// ./main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent).catch(e => console.error(e));
Compared to the NgModules way of adding components, as shown below, you can immediately see how standalone components make things much simpler.
// ./app/app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
title = 'CodeSandbox';
}
// ./app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
// .main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch((err) => console.error(err));
In this latest release, the Angular CLI now defaults to generating standalone components, directives, and pipes. This default setting underscores the shift towards a standalone-centric development approach in Angular.
New Syntax for Enhanced Control Flow
Angular 17 introduces a new syntax for control flow, replacing traditional structural directives like ngIf
or ngFor
, which have been part of Angular since version 2. This new syntax is designed for fine-grained change detection and eventual zone-less operation when Angular completely migrates to signals. It's more streamlined and performance-efficient, making handling conditional or list content in templates easier.
The @if
block replaces *ngIf
for expressing conditional parts of the UI.
@if (a > b) {
{{a}} is greater than {{b}}
} @else if (b > a) {
{{a}} is less than {{b}}
} @else {
{{a}} is equal to {{b}}
}
The @switch
block replaces ngSwitch
, offering benefits such as not requiring a container element to hold the condition expression or each conditional template. It also supports template type-checking, including type narrowing within each branch.
@switch (condition) {
@case (caseA) {
Case A.
}
@case (caseB) {
Case B.
}
@default {
Default case.
}
}
The @for
block replaces *ngFor
for iteration and presents several differences compared to its structural directive predecessor, ngFor
. For example, the tracking expression (calculating keys corresponding to object identities) is mandatory but offers better ergonomics. Additionally, it supports @empty
blocks.
@for (item of items; track item.id) {
{{ item.name }}
}
Defer Block for Lazy Loading
Angular 17 introduces the @defer
block, a dramatically improving lazy loading of content within Angular applications. Within the @defer
block framework, several sub-blocks are designed to elegantly manage different phases of the deferred loading process. The main content within the @defer
block is the segment designated for lazy loading. Initially, this content is not rendered, becoming visible only when specific triggers are activated or conditions are met, and after the required dependencies have been loaded. By default, the trigger for a @defer
block is the browser reaching an idle state.
For instance, take the following block: it delays the loading of the calendar-imp
component until it comes into the viewport. Until that happens, a placeholder is shown. This placeholder displays a loading message when the calendar-imp
component begins to load, and an error message if, for some reason, the component fails to load.
@defer (on viewport) {
<calendar-cmp />
} @placeholder {
<div>Calendar placeholder</div>
} @loading {
<div>Loading calendar</div>
} @error {
<div>Error loading calendar</div>
}
The on
keyword supports a wide a variety of other conditions, such as:
idle
(when the browser has reached an idle state)interaction
(when the user interacts with a specified element)hover
(when the mouse has hovered over a trigger area)timer(x)
(triggers after a specified duration)immediate
(triggers the deferred load immediately)
The second option of configuring when deferring happens is by using the when
keyword. For example:
@defer (when isVisible) {
<calendar-cmp />
}
Server-Side Rendering (SSR)
Angular 17 has made server-side rendering (SSR) much more straightforward. Now, a --ssr
option is included in the ng new
command, removing the need for additional setup or configurations. When creating a new project with the ng new
command, the CLI inquires if SSR should be enabled. As of version 17, the default response is set to 'No'. However, for version 18 and beyond, the plan is to enable SSR by default in newly generated applications. If you prefer to start with SSR right away, you can do so by initializing your project with the --ssr
flag:
ng new --ssr
For adding SSR to an already existing project, utilize the ng add
command of the Angular CLI:
ng add @angular/ssr
Hydration
In Angular 17, the process of hydration, which is essential for reviving a server-side rendered application on the client-side, has reached a stable, production-ready status. Hydration involves reusing the DOM structures rendered on the server, preserving the application's state, and transferring data retrieved from the server, among other crucial tasks. This functionality is automatically activated when server-side rendering (SSR) is used. It offers a more efficient approach than the previous method, where the server-rendered tree was completely replaced, often causing visible UI flickers. Such re-rendering can adversely affect Core Web Vitals, including Largest Contentful Paint (LCP), leading to layout shifts. By enabling hydration, Angular 17 allows for the reuse of the existing DOM, effectively preventing these flickers.
Support for View Transitions
The new View Transitions API, supported by some browsers, is now integrated into the Angular router. This feature, which must be activated using the withViewTransitions
function, allows for CSS-based animations during route transitions, adding a layer of visual appeal to applications.
To use it, first you need to import withViewTransitions
:
import { provideRouter, withViewTransitions } from '@angular/router';
Then, you need to add it to the provideRouter
configuration:
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes, withViewTransitions())
]
})
Other Notable Changes
- Angular 17 has stabilized signals, initially introduced in Angular 16, providing a new method for state management in Angular apps.
- Angular 17 no longer supports Node 16. The minimal Node version required is now 18.13.
- TypeScript version 5.2 is the least supported version starting from this release of Angular.
- The
@Component
decorator now supports astyleUrl
attribute. This allows for specifying a single stylesheet path as a string, simplifying the process of linking a component to a specific style sheet. Previously, even for a single stylesheet, an array was required understyleUrls
.
Conclusion
With the launch of Angular 17, the Angular Renaissance is now in full swing. This release has garnered such positive feedback that developers are showing renewed interest in the framework and are looking forward to leveraging it in upcoming projects. However, it's important to note that it might take some time for IDEs to adapt to the new templating syntax fully. While this transition is underway, rest assured that you can still write perfectly valid code using the old templating syntax, as all the changes in Angular 17 are backward compatible.
Looking ahead, the future of Angular appears brighter than ever, and we can't wait to see what the next release has in store!