Angular 17: Rebirth of a framework
⏱ Estimated reading time: 6 minutes
By Arthur Lannelucq, Senior Frontend developer @ Mesetys
Table of Contents
Angular went on a long journey to refresh its core components over the last versions. This path culminated in Angular 17, when the team decided to refresh the branding, their website and update the most developer-critical parts of the framework: compilation, SSR, CLI and control structures. This is a dive into the history of what led to Angular 17.
Angular 14
Angular 14 introduced Standalone components in replacement for ngComponents. Over time, features and patterns have developed around the concept such as lazy-loading and encapsulation, but this approach was dependency-heavy, hard to test, reason about and consequentially degraded user experience. SCAM, which stands for “Single Component Angular Module” developed as the easier path to develop shared modules for better reusability and easier maintenance at the risk of making the project file structure harder to grasp. This approach has been described in this blog post.
Standalone components are the official answer to this problem: every component —and this includes directives, pipes and so on— is its own module.
Also introduced in this function is the inject()
method which
can be used to inject dependencies outside the constructor()
method. This unlocked new patterns for simplifying Route
guards as demonstrated in this blog post.
Angular 15
Since Angular 15, developers can drop classes for Guards
and Route Resolvers as Angular’s 14 inject()
method enables
them to be expressed in functional style for less code and overhead.
Another change is Directive Composition API which can be used to gather multiple Angular Directives defined as standalone into one that is the sum of their behaviors! Composing directives makes for more reusable and maintainable code.
Angular 16
This version brought many critical changes to the framework, particularly when it comes to detecting changes. Until that
version, naive change detection was implemented in zone.js.
This implementation was naive as any interaction would trigger a complete refresh of the app! To optimize rendering
updates, a system that could better track interaction and changes was needed: Signal
s.
Signals notify about changes to the values they are observing and only update components that depend on them.
What is the consequence of Signals for RxJS and Observables?
Observable data types are often misunderstood and misused by developers, yet Signals are not meant to replace RxJS
except in very specific cases that deal with synchronous changes. There are ways to convert between Observables and
Signals using the toObservable()
and toSignal()
methods. Today, there is no other way to pass data to Signals than inputs, but their roadmap is promising with features
such as using Signals as Input and Output.
On to Server Side Rendering, where hydration was destructive and caused re-renders that made the webpage blink during client-side takeover. SSR hydration could be configured as non-destructive in this version. A bigger feature concerns compilation with the introduction of Vite and esbuild that can save up to 80% of the compilation time on larger codebases! This can save massive amounts of CI/CD and developer time!
Angular Input
s can now be declared as required and their
contents can be transformed automatically into booleans or numbers thanks to the booleanAttribute
and numberAttribute
transforms. Router parameters can
also be passed directly to the components with the bindToComponentInputs
option or the withComponentInputBinding
RouterFeature
. And we’ll finish this part on Angular 16 with self-closing tags, which has actually been introduced in
15.1 according to this changelog.
Angular 17
New branding, new logo, new documentation website with interactive components and tutorials with in-browser code experiments. This version is where high-value preview features go Generally available such as the CLI generating standalone components by default, Vite and esbuild compilations by default and a simpler SSR application creation flow. But that’s not all.
The biggest change has been introduced in the view language: developers won’t need to use the *ngIf
,
*ngFor
and *ngSwitch
directives and ng-container
elements to avoid leaving
traces on the DOM. To replace them, Angular introduced a new control flow
in templates with @if
, @for
and @switch
. Not only these directives don’t need to be attached to be components, but
they also improve the Developer eXperience with their former alternatives: expressions are not constrained by the DOM
attribute limitations, performance improved dramatically thanks to the integration with the templating language (a
performance uplift of up to 90% can be observed for @for
vs *ngFor
!) and @for
made reference tracking mandatory to
track changes in lists and that is a good thing!
If lazy loading was great, its logical evolution is Deferrable Views
within templates. This feature can defer the loading of dependencies like pipes, directives and CSS to further reduce
initial bundle sizes and improve user experience. It requires using @defer
template blocks which support conditions,
placeholder contents with @placeholder
, while-loading contents with @loading
and on-error contents with @error
.
Deferrable Views support multiple triggers in an “any of” fashion and additional options for placeholder and
while-loading contents.
Questions and Answers
Can @defer
triggers be used to implement “all of” conditions?
This may be possible using nested @defer
blocks or by using when
conditions that implement that logic. Stay tuned,
this feature may appear on the roadmap if it’s requested enough.
What is inject()
for? Where can it be used?
inject()
is to be used in injection contexts
such as within constructors and field initializers. Dependency Injection allows the framework to do the dependency
wiring for the developer and efficiently manage resource initialization.
Why use Standalone components?
Standalone components are a view into more composable and reusable components such that a button, an app screen and the entire application are each a component.
Signal
s resemble React states. What are notable differences?
While reactivity exists in Angular with Reactive Forms, they
are constrained to form inputs and driven by Observable
s. Signal
s offer more control over the data and update flows.
Signal updates can invoke one another and don’t significantly hamper performance.