0% нашли этот документ полезным (0 голосов)
107 просмотров182 страницы

The Ultimate Guide To Angular Evolution Angular 19

Загружено:

stefanobelfiore
Авторское право
© © All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате PDF, TXT или читать онлайн в Scribd
0% нашли этот документ полезным (0 голосов)
107 просмотров182 страницы

The Ultimate Guide To Angular Evolution Angular 19

Загружено:

stefanobelfiore
Авторское право
© © All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате PDF, TXT или читать онлайн в Scribd
Вы находитесь на странице: 1/ 182

2025 edition with Angular 19

The Ultimate
Guide to Angular
Evolution
How Each Angular Version Impacts Efficiency,
DX, UX and App Performance
Table of Contents
Preface������������������������������������������������������������������������������������������������������������������������������������������ 5
Introduction��������������������������������������������������������������������������������������������������������������������������������� 6
How this book is organized������������������������������������������������������������������������������������������������������������������������������������������������������������ 7
Help us improve this ebook�����������������������������������������������������������������������������������������������������������������������������������������������������������9
Spread the word���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������9
Join Angular community and level up your skills���������������������������������������������������������������������������������������������������������������9
Angular v14��������������������������������������������������������������������������������������������������������������������������������� 10
Standalone API (developer preview)��������������������������������������������������������������������������������������������������������������������������������������� 11
Typed forms���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 15
Inject function�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������17
CDK Dialog and Menu�������������������������������������������������������������������������������������������������������������������������������������������������������������������� 19
Setting the page title��������������������������������������������������������������������������������������������������������������������������������������������������������������������� 20
ENVIRONMENT_INITIALIZER Injection Token���������������������������������������������������������������������������������������������������������������������������22
Binding to protected component members����������������������������������������������������������������������������������������������������������������������� 24
Angular extended diagnostics�������������������������������������������������������������������������������������������������������������������������������������������������� 25
ESM Application Build (experimental)������������������������������������������������������������������������������������������������������������������������������������27
Typescript/Node.js support���������������������������������������������������������������������������������������������������������������������������������������������������������27
Angular v15�������������������������������������������������������������������������������������������������������������������������������� 32
Standalone API (Stable)�������������������������������������������������������������������������������������������������������������������������������������������������������������� 33
Directive composition API������������������������������������������������������������������������������������������������������������������������������������������������������������ 39
Image directive��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 41
MDC-based components������������������������������������������������������������������������������������������������������������������������������������������������������������ 42
CDK Listbox����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 43
Improved stack traces������������������������������������������������������������������������������������������������������������������������������������������������������������������ 44
Auto-imports in language service������������������������������������������������������������������������������������������������������������������������������������������ 46
Typescript/Node.js support�������������������������������������������������������������������������������������������������������������������������������������������������������� 47
Angular v16�������������������������������������������������������������������������������������������������������������������������������� 50
Signals library (developer preview)���������������������������������������������������������������������������������������������������������������������������������������� 51
SSR Hydration (developer preview)���������������������������������������������������������������������������������������������������������������������������������������� 54
Vite-powered dev server������������������������������������������������������������������������������������������������������������������������������������������������������������� 56
Required inputs�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 57
Input transform function�������������������������������������������������������������������������������������������������������������������������������������������������������������� 60
Router data input bindings���������������������������������������������������������������������������������������������������������������������������������������������������������� 61
Injectable DestroyRef and takeUntilDestroyed������������������������������������������������������������������������������������������������������������������ 63
Self-closing tags����������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 65
runInInjectionContext�������������������������������������������������������������������������������������������������������������������������������������������������������������������� 66
Standalone API CLI support�������������������������������������������������������������������������������������������������������������������������������������������������������� 67
Typescript/Node.js support�������������������������������������������������������������������������������������������������������������������������������������������������������� 67
Angular v17����������������������������������������������������������������������������������������������������������������������������������73
Signals library (stable)������������������������������������������������������������������������������������������������������������������������������������������������������������������74
Signal inputs���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������76
New control flow (Developer preview)�����������������������������������������������������������������������������������������������������������������������������������78
Deferred loading (developer preview)������������������������������������������������������������������������������������������������������������������������������������81
Inputs Binding with NgComponentOutlet ��������������������������������������������������������������������������������������������������������������������������� 85
Animation lazy loading���������������������������������������������������������������������������������������������������������������������������������������������������������������� 86
View Transitions ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 88
Esbuild + Vite (stable)������������������������������������������������������������������������������������������������������������������������������������������������������������������ 90
SSR Hydration (stable)�������������������������������������������������������������������������������������������������������������������������������������������������������������������91
CLI improvements����������������������������������������������������������������������������������������������������������������������������������������������������������������������������92
Devtools Dependency Graph������������������������������������������������������������������������������������������������������������������������������������������������������93
Rebranding and introduction of angular.dev�������������������������������������������������������������������������������������������������������������������� 94
Typescript/Node.js support�������������������������������������������������������������������������������������������������������������������������������������������������������� 95
Angular v18���������������������������������������������������������������������������������������������������������������������������������99
Hybrid Change Detection�����������������������������������������������������������������������������������������������������������������������������������������������������������100
Signal inputs������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 103
Model inputs�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������105
Signal queries���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 107
Output syntax���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 109
Collaboration Between Angular and Wiz������������������������������������������������������������������������������������������������������������������������������ 111
Ng-content fallback����������������������������������������������������������������������������������������������������������������������������������������������������������������������� 113
Route Redirects as Functions���������������������������������������������������������������������������������������������������������������������������������������������������� 114
New RedirectCommand��������������������������������������������������������������������������������������������������������������������������������������������������������������� 116
Improved Hydration Debugging Experience����������������������������������������������������������������������������������������������������������������������� 117
New Observables in Forms��������������������������������������������������������������������������������������������������������������������������������������������������������� 119
New Documentation��������������������������������������������������������������������������������������������������������������������������������������������������������������������� 120
Hydration Support in CDK and Material������������������������������������������������������������������������������������������������������������������������������� 122
Material 3������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 123
Typescript/Node.js support������������������������������������������������������������������������������������������������������������������������������������������������������� 124
Angular v19������������������������������������������������������������������������������������������������������������������������������� 132
New reactive primitive - linkedSignal (experimental)������������������������������������������������������������������������������������������������� 133
Resource API (experimental)���������������������������������������������������������������������������������������������������������������������������������������������������� 136
AfterRenderEffect Function (experimental)�����������������������������������������������������������������������������������������������������������������������140
Minor signal improvements������������������������������������������������������������������������������������������������������������������������������������������������������� 141
@let template variable syntax������������������������������������������������������������������������������������������������������������������������������������������������� 143
Incremental Hydration (experimental)��������������������������������������������������������������������������������������������������������������������������������144
Server Route Configuration (experimental)���������������������������������������������������������������������������������������������������������������������� 147
RouterOutlet data input��������������������������������������������������������������������������������������������������������������������������������������������������������������148
RouterLink directive enhancements��������������������������������������������������������������������������������������������������������������������������������������150
Default query params handling strategy���������������������������������������������������������������������������������������������������������������������������� 151
Components become standalone by default������������������������������������������������������������������������������������������������������������������ 152
New useful migrations (injections, standalone API)������������������������������������������������������������������������������������������������������ 153
Strict standalone flag������������������������������������������������������������������������������������������������������������������������������������������������������������������� 155
Initializer provider functions������������������������������������������������������������������������������������������������������������������������������������������������������156
New angular diagnostics������������������������������������������������������������������������������������������������������������������������������������������������������������ 158
Hot module replacement for ng serve��������������������������������������������������������������������������������������������������������������������������������� 159
New features in Angular Language Service�����������������������������������������������������������������������������������������������������������������������160
Typescript/Node.js support������������������������������������������������������������������������������������������������������������������������������������������������������� 162
The future���������������������������������������������������������������������������������������������������������������������������������� 164
Signal Forms�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������164
Selectorless���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������166
Zoneless Angular����������������������������������������������������������������������������������������������������������������������������������������������������������������������������166
Streamed server-side rendering��������������������������������������������������������������������������������������������������������������������������������������������166
Overall������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 167
What the experts are saying ������������������������������������������������������������������������������������������������ 168
How important is it to stay up-to-date with all the latest changes in Angular, given the current pace
of changes?��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������168
How do the overall changes affect the learning curve? Is it easier to learn Angular in its latest form? ������� 171
What are your expectations for the direction and pace of change in Angular in the future?��������������� 173
What is your most anticipated feature in the Angular Roadmap?������������������������������������������������������������������������� 174
Thank You��������������������������������������������������������������������������������������������������������������������������������� 175
Consulting and audit�������������������������������������������������������������������������������������������������������������� 176
Media partners���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������177
Bibliography����������������������������������������������������������������������������������������������������������������������������� 178
About authors�������������������������������������������������������������������������������������������������������������������������� 178
Main Authors������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 178
Special thanks to���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 178
Invited experts��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 179
Preface
This ebook is a combination of Angular roadmap and changelog, supplemented with
clear explanations and use cases of all relevant changes in recent years. It brings
together all the new features in one place and puts them in a broader context of
Angular’s evolution. Regardless of whether you are interested in learning about
a specific functionality or want to stay up to date with all the changes and their impact
on the framework, this text has got you covered.

The Ultimate Guide to Angular Evolution: version 14-19 5


Introduction
Angular is gaining momentum we’ve never seen before. Since 2021, we’ve been
getting a lot of new features on a regular basis, and the pace of change is not
slowing down. This is a positive for Angular apps, but it also means you need to
dedicate an amount of time every year to keep track of these changes. It’s crucial to
stay up to date with Angular’s changes and new features in order to provide your
users with a quality, high-performing application and in-house developers with a good
developer experience.

Regularly updating the Angular version will make your application easier to maintain
thanks to features and tools that make it easier to develop, test, and improve your
application. This translates directly into several benefits for your business, including
improved app performance, better security, increased developer efficiency, and better
user experience.

This ebook will serve as your guide to previous Angular versions, the changes and new
features they brought. As a bonus it provides expert insights and predictions of what
the framework will look like in the future.

We will be focusing on versions 14 and up. This is because the earlier period of Angular
development was marked by significant internal changes (related to the migration of
the compiler and rendering engine to Ivy), which, at the same time, meant much less
new functionality and changes to the framework’s API. To visualize this, we can try to
divide the lifespan of Angular so far into three phases:

*Now - the time of publication, Q1 2025

Angular along with other frameworks like React and Vue, popularized sin-
gle-page applications and built a strong community around them. The first
versions of Angular were very thoroughly battle-tested. During that period the
limitations of the legacy build and rendering pipeline called “View Engine” were
learned. After that Angular core developers decided to completely redesign it
and gave it a new code name called “Ivy”. Complete migration took a few years
to finally abandon the previous solution.

Ivy engine introduced a number of new opportunities and brought us into the 3rd
Phase, a kind of “boom” of new functionalities and long-term shifts in Angular mental
models. In the following pages, we will look at all of these in detail.

The Ultimate Guide to Angular Evolution: version 14-19 6


How this book is organized
The technical content is grouped by major release versions of Angular (starting
with version 14, up to version 19, and beyond). Sometimes, however, functionalities
were introduced between major releases or introduced gradually and improved
over several versions. While we wanted to follow the chronological order as closely
as possible, there may be some deviations that do not disrupt the broader context of
changes to the framework.

Our approach to presenting this content is systematic and reader-friendly, ensuring that
you can both grasp the technicalities and see the broader picture of each feature’s
impact. We structured all functionalities into three distinct sections:

Challenge - This section outlines the specific problem or need that the new feature
addresses. Understanding the challenge provides context and highlights the
significance of the feature in real-world scenarios.

Solution - Here, we delve into the technical details of the feature. We describe how it
works, its implementation, and any variations or configurations that are relevant.

Benefits - This part showcases the advantages of using the feature. We focus on how it
contributes to business values.

To further enhance your reading and learning experience, each feature is categorized
using labels. That way, you have the option of reading this ebook cover-to-cover to get
a full picture of Angular’s evolution or jump to specific sections that are most relevant to
your immediate needs. The labels and structured format make it easy to find and focus
on the features that are most pertinent to your current projects or interests.

The four labels are:

Performance enhancements in frontend development are crucial


Performance
for both the end-user experience and the efficiency of development
processes. These enhancements focus on optimized JavaScript
execution, efficient resource loading, and minimized browser
reflow and repaint, resulting in quicker page loads and smoother
web element interactions. Additionally, these improvements can
streamline the development workflow and CI/CD pipeline,
enabling quicker builds, more efficient testing, and faster
deployment cycles. We use this label to mark such beneficial
changes in performance.

Improving developer experience can increase productivity and


Dev Experience code quality. When developers have better tools, processes, and
documentation, they can implement features more quickly, reliably
and in a more maintainable way. They are more motivated to work
on your project and stay in your team for a longer time. This label

The Ultimate Guide to Angular Evolution: version 14-19 7


marks changes that have a positive impact on the developer’s
experience.

Focusing on user experience is crucial, because it leads to products


UX that are more intuitive, accessible and enjoyable to use. This can
increase user engagement, satisfaction and loyalty, which are
important factors for the success of any software application.
This label marks changes that improve user experience in Angular
projects.

By increasing the speed at which new features and fixes


Efficiency are delivered, companies can respond more quickly to market
changes and user feedback, leading to a competitive edge and
improved customer satisfaction. This label marks changes that
speed up the developer’s work and increase his/her efficiency.

We hope this book serves as a valuable resource in your jour-


ney with Angular, whether you’re a seasoned developer or just
starting out. Happy reading, and happy coding!

The Ultimate Guide to Angular Evolution: version 14-19 8


Help us improve this ebook
As new Angular version is coming, we will be happy to prepare the next edition of the
ebook and share insights about the latest version, but…
we need your help.

You, as a reader, would know much better how to make the Guide more useful,
so it really serves your needs and solves your problems.

Please let us know how you we can improve the ebook:


Share your feedback in this short form

Spread the word


If you find the Guide valuable feel free to share it with your friends, collegues
or other Angular enthusiasts.

We’ve created this ebook because we believe that Angular has a great potential
and using the latest versions can boost efficiency, developer experience and per-
formance of your applications.

Help us spread this idea!

Share the information about the ebook via Twitter, LinkedIn, Facebook or Reddit.

Join Angular community and level up your skills


You can also find lots of useful information, tips, case studies at Angular.love com-
munity. Check the blog and take part in the Angular events to get knowledge from
the most experienced experts and Angular Enthusiasts.

Angular.love blog https://angular.love/


Angular.love meetups and camps https://meetup.angular.love/

Partner conferences:
WeAreDevelopers https://www.wearedevelopers.com/world-congress/
WAYS CONF https://www.waysconf.com/

The Ultimate Guide to Angular Evolution: version 14-19 9


Typed Forms

Standalone API

Angular v14
release date: 06.2022
Before version 14, a significant goal was accomplished with the final deprecation of the
legacy View Engine. This change, along with aligning all internal tools with Ivy, not only
simplified but often enabled the introduction of innovations in Angular. It also led to
easier framework maintenance, reduced codebase complexity, and smaller bundle
sizes.

This was the first wave of improvements made possible by Ivy. The best examples are the
Standalone API (in developer preview), Typed Forms and better developer experience
related to debugging applications, both in the CLI and in the browser.

The introduction of standalone components streamlines the development process,


reducing the need for additional configurations. Enhanced type safety in forms ensures
error-resistant applications, which is especially beneficial for complex use cases.
Features like streamlined page title accessibility improve user experience and SEO, while
extended developer diagnostics offer actionable performance insights. With support for
the latest TypeScript and ECMAScript standards, Angular 14 provides developers with
a more powerful, efficient and flexible toolset for building advanced web applications.

Standalone API (developer preview)

Performance Dev Experience Efficiency

Challenge:
Although ECMAScript has a native module system supported in all modern browsers,
Angular delivers its own module system, which is based on NgModules.

NgModule is a structure that describes how to create an injector and how to compile
a component’s template at runtime. It includes definitions of components, directives,
pipes, and service providers that will be added to the application dependency injectors.
The goals of NgModules are to organize the application and extend it with capabilities
from external libraries.

This solution was met with some criticism from the beginning, because it was considered
too complicated and illegible. The complex connections between modules and their
providers, unclear dependencies between components, and unclear NullInjectorErrors
were some of the reasons a simplified alternative needed to be provided.

Solution:
In version 14 (developer preview), the Angular Team made NgModules optional with
full backward compatibility. Creating components, directives and pipes as standalone
is possible by setting the “standalone” flag to true in their decorators.

Angular v14 | The Ultimate Guide to Angular Evolution 11


@Component({
selector: ‘footer’,
template: ‘<ng-content></ng-content>’,
standalone: true,
})
export class FooterComponent {}

Standalone components can be imported by another standalone component or


module or used within a routing declaration.

//in module
@NgModule({
imports: [FooterComponent],
})
export class Module {}

//in component
@Component({
selector: main,
template: ‘<ng-content></ng-content>’,
standalone: true,
imports: [FooterComponent ]
})
export class Component {}

//routing
export const ADMIN_ROUTES: Route[] = [
{path: ‘footer’, component: FooterComponent}
];

This transition makes components, directives and pipes self-contained. NgModule is no


longer the smallest building reusable block. This results in many positive outcomes,
including:
• a component no longer needs to be defined in its own NgModule and can be
reused independently,
• reading components is much easier, as the reader doesn’t have to track
a component’s module to understand its dependencies,
• tracking implicit dependencies on NgModules context is very costly for tools and
makes it difficult to optimize generated code. With the standalone API the tools can
be optimized,

Angular v14 | The Ultimate Guide to Angular Evolution 12


• the API for dynamic loading and rendering components is simpler, since we
no longer have to care about a component’s module when using ViewContainer-
Ref.createComponent(...).

Angular’s main ApplicationModule is no exception. It also became optional, making it


possible to bootstrap application directly with a standalone component using the
bootstrapComponent function:

import { Component, bootstrapComponent } from ‘@angular/core’;

@Component({
selector: ‘my-app’,
standalone: true,
template: ‘<h1>Hi there!</h1>’,
})
export class AppComponent {}

bootstrapComponent(AppComponent);

How does it affect the routing? The new API allows to lazy load standalone paths
directly, with no need for NgModule:

RouterModule.forRoot([
{
path: ‘/path/to/standalone/component’,
loadComponent: () => import(‘./default-standalone.cmp’)
}
]);

There is also no need for NgModule while testing standalone components. This is how
the test case can look like

const fixture = TestBed.createStandaloneComponent(MyComponent, {...});

At this point, in developer preview mode, the change is not production-ready. It has some
problems to overcome. (Spoiler: Angular overcomes them in Angular version 15). Still, it
presents a major mental model change that turns standalone components into basic
building blocks of an application.

Angular v14 | The Ultimate Guide to Angular Evolution 13


The Angular Core Team also made sure that everything was compatible with the existing
module-based ecosystem, meaning all existing libraries should work as-is.

Benefits:
This revolution boosts the developer experience by:
• reducing boilerplate,
• making reading component code (especially dependencies) easier,
• removing the whole concept of an extra module system based on NgModules,
• improving compilation time, and
• lowering the entry barrier for novice developers.
Reduced bundle size and Tree-Shaking (thanks to function-based API) also improves
the performance. More details and benefits are described in chapter “Angular 15”, where
Standalone API becomes Stable.

Expert Opinion:

Standalone APIs will make the authoring and building of Angular apps much
simpler, especially for beginners to the Angular framework. The concept of Angular
modules had been complex to explain and many developers who wanted to learn
Angular were confused. Thankfully Standalone APIs will change that.

~ Aristeidis Bampakos
Google Developer Expert

From my perspective, the introduction of the Standalone API has significantly simplified
the learning curve for Angular. In the past, learners had to grapple with understanding
the complex NgModule relationships, including figuring out what to export, where to
import, and dealing with the intricacies of eager and lazy-loaded routing. Standalone API,
however, empowers developers to concentrate solely on the component they are
working on, without the need to concern themselves with the intricacies of the NgModule.
The extension of this approach to Pipes and Directives is like the icing on the cake.

Furthermore, Angular CLI provides support for creating projects with a focus on the
standalone component-based approach, which is a valuable feature. There are also
several open-source libraries available to facilitate the migration of applications from
NgModule-based architecture to a standalone component-based one, making
this transition smoother and more accessible.

~ Balram Chavan
Google Developer Expert

Standalone APIs introduced an important shift towards simplifying application


development in Angular. They are the basis for many powerful features that have
been introduced recently.

~ Marko Stanimirović
Google Developer Expert

Angular v14 | The Ultimate Guide to Angular Evolution 14


Typed forms

Dev Experience

Challenge:
Before Angular version 14, many APIs related to Reactive Forms (FormGroup,
FormControl, FormArray, etc.) included the usage of “any” type. This resulted in poor
type safety, bad support for coding tools, codebase inconsistency and problematic
refactoring.

Solution:
In response to the challenges mentioned above, the Angular Team introduced Typed
Forms. Existing reactive forms were extended to include generic types to enforce type
safety for form controls, groups, and arrays. By applying types, you can catch potential
errors at compile time, making your codebase more robust and maintainable.

Example of reactive control with a generic type:

const nameFormControl = new FormControl<string>(‘’);


// type of nameFormControl.value is string | null

The reason control values are nullable is because of the control.reset() method. If you
don’t pass an argument, it will change the value to null. However, it’s possible to change
this behavior by setting the new flag called ‘nonNullable’:

const nameFormControl = new FormControl<string>(‘’, { nonNullable: true });


// type of nameFormControl.value is string

nameFormControl.reset();
// reset method will change value to empty string

Things get more interesting when we inspect the behavior of complex controls, like
FormGroup:

Angular v14 | The Ultimate Guide to Angular Evolution 15


const myForm = new FormGroup({
name: new FormControl(‘’, { nonNullable: true }),
});
// type of myForm.value.name is string | undefined
// type of myForm.getRawValue().name is string

The possibly undefined type comes from the fact that name control might be
disabled (and not included in form value object).

For the use cases where we don’t know all control keys beforehand, like when controls are
added dynamically, the Angular Team added the new FormRecord class.

const myForm = new FormRecord<FormControl<string>>({ });


myForm.addControl(‘foo’, new FormControl<string>(‘bar’, { nonNullable: true
}));
// type of myForm.value is Partial<{[key: string]: string}>

Migrating to Angular 14 will not break any existing untyped forms, because all
occurrences of forms classes will be automatically replaced by their untyped versions:

const login = new UntypedFormGroup({


email: new UntypedFormControl(‘’),
password: new UntypedFormControl(‘’),
});

You can incrementally migrate to Typed Forms by removing the “Untyped” prefix
in your application.

Benefits:
Leveraging TypedForms in Angular:
• Leads to more readable, maintainable, and reliable code: this feature
enhances type safety and development efficiency by providing compile-time checking
and improved IDE support for autocompletion and error detection. Discrepancies in
data types or structures are caught early, and refactoring becomes safer and more
straightforward.

Angular v14 | The Ultimate Guide to Angular Evolution 16


• Facilitates cleaner, more concise code and streamline validation processes,
aligning well with Angular’s design philosophy for a more cohesive development experience.

Inject function

Dev Experience Efficiency

Challenge:
So far, when it comes to dependency injection, we have been limited to injections through
the constructor. This brings some limitations associated with reusability (as we could
not reuse the constructor definition), testing, and a general separation of concerns. For
example, classes required a knowledge of how to create their dependencies instead of
focusing purely on their main responsibilities.

Solution:
The ‘inject’ utility function allows us to retrieve an instance of dependency from the
Angular Dependency Injection System outside of a class constructor. This function
has to be called in an injection context, that is one of the following:
• a constructor of the component, directive, pipe, injectable service or NgModule,
• an initializer for fields of such classes,
• a factory function (in a ‘useFactory’ object of a provider or an injectable),
• an InjectionToken’s factory, or
• a function within a stack frame that is run in an injection context.
Let’s take a look at an example of usages inside a component:

@Component({...})
class HomeComponent {
private readonly dependencyA: DependencyA;
private readonly dependencyB: DependencyB = inject(DependencyB);

constructor() {
this.dependencyA = inject(DependencyA);
}
}

When you want to verify if you’re in an injection context, you can use a helper function
called assertInInjectionContext:

Angular v14 | The Ultimate Guide to Angular Evolution 17


function getService(): FooService {
assertInInjectionContext(getService);
return inject(FooService);
}

This feature allows us to create many reusable DI-dependent functions like the following:

function getProductId(): Observable<string> {


return inject(ActivatedRoute).paramMap.pipe(
map((params) => params.get(‘productId’))
);
}

@Component({...})
class ProductDetails {
private readonly productId$ = getProductId();
...
}

Benefits:
The inject function in Angular:
• Provides flexibility: allows developers to access service instances and other
dependencies outside of class constructors, making code more modular and testable.
• Streamlines the process of dependency retrieval: promotes cleaner and more
maintainable code by abstracting the complexity of dependency management and
injection.

Angular v14 | The Ultimate Guide to Angular Evolution 18


CDK Dialog and Menu

Dev Experience Efficiency UX

Challenge:
Before the introduction of the CDK, styling certain components available in Angular
Material was difficult due to their ready-made design.In such a situation, it was
necessary to overwrite the styles of a given component, which could be problematic.

Solution:
Thanks to the CDK, it is possible to create unstyled dialogs and menus and customize
them by ourselves.

Open dialog is called by an open method with a component or with a TemplateRef


representing the dialog content and returns a DialogRef instance.

const dialogRef = dialog.open(DialogComponent, {


height: ‘300px’,
width: ‘500px’,
panelClass: ‘empty-dialog’,
});

cdkMenu from CdkMenuModule provides directives to create custom menu interactions


based on the WAI ARIA specification. It’s possible to create your own design or use ready-
made classes from directives to make it easier to add custom styles.
A typical menu consists directives:
• cdkMenuTriggerFor - trigger element to open ng-template with menu
• cdkMenu - create menu content after click on the trigger
• cdkMenuItem - create and add item to menu

<button [cdkMenuTriggerFor]=”menu”>Open menu</button>

<ng-template #menu>
<div cdkMenu>
<button cdkMenuItem>Item 1</button>
<button cdkMenuItem>Item 2</button>
<button cdkMenuItem>Item 3</button>
</div>
</ng-template>

Angular v14 | The Ultimate Guide to Angular Evolution 19


Benefits:
Angular CDK:
• Provides a set of tools to build feature-packed and high-quality Angular components.
• Simplifies common pattern and behavior implementation.

Setting the page title

Dev Experience Efficiency

Challenge:
Angular provides a Title service that allows modifying the title of a current HTML document.
To use it, you have to inject it. It is completely independent from routing, so any linking to
the data in the routing configuration requires complex logic using Angular Router.

Solution:
Angular 14 offers a new feature - TitleStrategy - to set unique page titles using the new
Route.title property in the Angular Router. The title property takes over the title of the
page after routing navigation.

export const routes: Routes = [


{
path: ‘home’,
title: ‘Home Page’,
loadComponent: () =>
import(‘./home/home.component’).then((m) => m.HomeComponent),
}
];

It is also possible to provide a custom TitleStrategy to apply more complex logic behind
a page title.

Angular v14 | The Ultimate Guide to Angular Evolution 20


const routes: Routes = [{
path: ‘home’,
component: HomeComponent
}, {
path: ‘about’,
component: AboutComponent,
title: ‘About Me’ // <-- Page title
}];

@Injectable()

export class TemplatePageTitleStrategy extends TitleStrategy {


constructor(private readonly title: Title) {
super();
}
override updateTitle(routerState: RouterStateSnapshot) {
const title = this.buildTitle(routerState);
if (title !== undefined) {
this.title.setTitle(`My App - ${title}`);
} else {
this.title.setTitle(`My App - Home`);
}
}
}

@NgModule({

providers: [{provide: TitleStrategy, useClass: TemplatePageTitleStrategy}]

})
export class MainModule {}

Benefits:
This feature is:
• A new convenient way for manipulating a page title: with no dependency injection
and reactivity overhead.

Angular v14 | The Ultimate Guide to Angular Evolution 21


ENVIRONMENT_INITIALIZER Injection Token

Dev Experience Efficiency

Challenge:
It was possible to use a class with NgModule decorator to run initialization logic, i.e.:

@NgModule(...)
export class LazyModule {
constructor(configService: ConfigService) {
configService.init();
}
}

But in the absence of NgModules, some Standalone API counterpart was needed.

Solution:
The Environment injector is a more generalized version of the module injector, intro-
duced together with Standalone APIs in Angular v14. ENVIRONMENT_INITIALIZER is a to-
ken for initialization functions that will run during the construction time of an envi-
ronment injector.

When we navigate to a lazy loaded route, a new environment injector is also creat-
ed for that route. Then we can provide ENVIRONMENT_INITIALIZER functions that will
be executed upon such navigation. Inside the initialization function, you can use
the inject(...) function and perform any logic you need when the application is boot-
strapped or lazy loaded content is instantiated.

Angular v14 | The Ultimate Guide to Angular Evolution 22


Initialization functions on a standalone application bootstrap:

bootstrapApplication(AppComponent, {
providers: [
{
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useValue: () => inject(ConfigurationService).init(),
},
],
});

Initialization functions on lazy loaded routes:

export const lazyRoutes: Routes = [


{
path: ‘’,
component: FooComponent,
providers: [
{
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useValue: () => inject(BarService).activate(),
},
],
},
];

Benefits:
This Injection token:
• Increases efficiency: we can place our initialization logic for the entire appli-
cation or a specific lazily loaded route in a way that is compatible with Stan-
dalone APIs and NgModules.

• Improves developer experience: this part of the process is now more clear
and convenient to carry out.

Angular v14 | The Ultimate Guide to Angular Evolution 23


Binding to protected component members

Dev Experience

Challenge:
Only public members of a component were accessible in its component template.
It meant that every binding in the template was also a part of the component’s pub-
lic API. Component classes exposed too much and violated encapsulation.

Component’s public API is relevant when we reference components programmati-


cally, i.e. via ViewChild decorator:

@ViewChild(MyComponent) myComponent: MyComponent;

Solution:
In Angular v14, it is possible to use fields marked as protected in the template.

@Component({
selector: 'my-component',
template: '{{ message }}', // Now compiles!
})
export class MyComponent {
protected message: string = 'Hello world';
}

Benefits:
With this change, we get:
• An improved developer experience: the overall encapsulation is improved.
We can now encapsulate (hide) methods and properties that we use inside
a component’s template but don’t want to expose them anywhere else.

Angular v14 | The Ultimate Guide to Angular Evolution 24


Angular extended diagnostics

Dev Experience Efficiency

Challenge:
Sometimes in the Angular codebase, there are potential anomalies that are not
straightforward bugs. For example, when they don’t cause a compilation error and
meet all syntax requirements. At the same time, objections might occur, and they
might not necessarily reflect what the programmer had in mind.

Let’s take a look at the following example:

<component ([foo])=”bar”></component>

This is a valid Angular example, but it is not a standard two-way data binding. Instead,
it is a one-way data binding for an event (output) called “[foo]”. The valid two-way data
binding looks like this:

<component [(foo)]=”bar”></component>

Solution:
Angular 13.2.0 brought us a new functionality called Extended Diagnostics. It’s a tool
built into the compilation process of Angular view templates (it’s part of the compiler
itself), and it doesn’t require any additional infrastructure or scripts. – It simply works
out of the box, and with the ng serve during the transpilation process.

Its task is to detect potential anomalies just like the one mentioned above. It serves
as a kind of additional linter for angular view template syntax. We enable it inside
the tsconfig file in the angularCompilerOptions section.

Angular v14 | The Ultimate Guide to Angular Evolution 25


{
“angularCompilerOptions”: {
“strictTemplates”: true,
“extendedDiagnostics”: {
“checks”: {
“invalidBananaInBox”: “error”
},
“defaultCategory”: “error”
}
}

The list of currently available diagnostics is available in the official documentation:


https://angular.io/extended-diagnostics. The Angular Team plans to add new
diagnostics in minor releases of the framework. New diagnostics and new bugs may
appear along with version upgrades, so by default, detected anomalies are returned
as warnings. This can be controlled with the “angularCompilerOptions.extended-
Diagnostics.defaultCategory” field in the tsconfig file.

Ideas for new diagnostics can be submitted via the github feature requests:
https://github.com/angular/angular/issues/new?template=2-feature-request.yaml

Benefits:
These extended diagnostics result in:
• Improved code security and reliability: with no extra effort and/or cost.

Angular v14 | The Ultimate Guide to Angular Evolution 26


ESM Application Build (experimental)

Dev Experience Performance

Challenge:
The standard Angular bundler is considered quite slow by developers. The Angular Team
tested various other approaches that can speed up the package-building process.

Solution:
Angular version 14 introduces an experimental feature that leverages the esbuild-based
build system for the “ng build” command. This experimental build system compiles pure
ECMAScript Modules (ESM) output.

To enable it, use the following snippet in the angular.json config file:

“builder”: “@angular-devkit/build-angular:browser-esbuild”

Esbuild itself is an extremely fast JavaScript bundler and minifier written in the Go
language that’s designed for modern web development. It supports heavy par-
allel processing and might significantly outperform competitors such as Webpack.
Its support in Angular framework will be developed in the upcoming releases.

Benefits:
Introducing a new esbuild-based build system is:
• A step towards faster build-time performance: including both initial builds
and incremental builds.
• An opening to new tools: thanks to ESM, which includes dynamic import
expressions for lazy module loading support.

Typescript/Node.js support

Support for Node.js v12 and Typescript older than 4.6 has been removed. Angular 14
supports TS v4.7 and targets ES2020 by default, meaning initial bundle size is reduced.
Here are the new feature examples in the now-supported Typescript:

Angular v14 | The Ultimate Guide to Angular Evolution 27


Enhanced Awaited type (available in 4.5)

Dev Experience

Challenge:
Developers who frequently work with Promises, especially with async/await syntax,
sometimes want to explicitly describe the type of value returned by the resolved
Promise. For regular synchronous functions, there is the ReturnType<FnType> utility, but
before TypeScript 4.5, there was no counterpart for asynchronous functions.

Solution:
A new utility type called Awaited was introduced in TypeScript 4.5. It unwraps
promise-like “thenables” without relying on PromiseLike, and it does it recursively.

// Name = string
type Name = Awaited<Promise<string>>;

// Age = number
type Age = Awaited<Promise<Promise<number>>>;

// Foo = boolean | number


type Foo = Awaited<boolean | Promise<number>>;

Benefits:
Extra explicit typing of async functions:
• Positively affects the type-safeness, and
• Improves code readability.

Template string types as discriminants (available in 4.5)

Dev Experience

Challenge:
TypeScript could not correctly use template string types to narrow the type in
discriminated unions. In such cases, the exact type could not be inferred therefore,
typing support was limited.

Angular v14 | The Ultimate Guide to Angular Evolution 28


Solution:
With this feature, Typescript is now able to use template string literals as discriminants.
The following example used to fail, but now successfully type-checks:

export interface Success {


type: `${string}Success`;
body: string;
}

export interface Error {


type: `${string}Error`;
message: string
}

export function handler(r: Success | Error) {


if (r.type === “HttpSuccess”) {
const token = r.body; // correct!
}
}

Benefits:
This change brings:
• More flexibility: when defining types and more intelligent type inference by TypeScript
transpiler.

Control Flow Analysis for Destructured Discriminated Unions


(Available in 4.6)

Dev Experience

Challenge:
If we had a discriminated union and we tried to destructure it, we could no longer narrow
its members using discriminator property.

Solution:
Since TypeScript version 4.6, it is possible to narrow destructed discriminated object
properties. The following example explains such a case:

Angular v14 | The Ultimate Guide to Angular Evolution 29


type Action =
| { kind: “NumberContents”; payload: number }
| { kind: “StringContents”; payload: string };
function processAction(action: Action) {
const { kind, payload } = action;
if (kind === “NumberContents”) {
// payloadis narrowed to number
let num = payload * 2;
// ...
} else if (kind === “StringContents”) {
// payload is narrowed to string
const str = payload.trim();
// ...
}
}

Benefits:
We gain more flexibility when using and mixing discriminated unions and object
destruction.

Allows code in Constructors before super() (Available in 4.6)

Dev Experience

Challenge:
In JavaScript classes, it’s necessary to invoke super() before using “this” keyword.
TypeScript also has this rule, although it used to be excessively strict in ensuring it. In
TypeScript, it used to be considered an error to have any code at the start of a constructor
if the class containing it had any property initializers.

Solution:
From now on we can place a code inside the constructor, before calling “super()”. Note
that it is still mandatory to call super() before referring to the “this“ keyword.

Angular v14 | The Ultimate Guide to Angular Evolution 30


class Base {
// ...
}
class Derived extends Base {
someProperty = true;
constructor() {
doSomeStuff(); // do any logic, but don’t refer ‘this’ yet
super();
}
}

Benefits:
The unnecessary limitation has been removed and the transpiler still validates the code
correctly, giving more freedom to the programmer.

Angular v14 | The Ultimate Guide to Angular Evolution 31


Directive Composition API

Image Directive

Standalone API

Angular v15
release date: 11.2022
Angular v15 introduces significant improvements, phasing out legacy systems,
enhancing developer experience and optimizing performance. Standalone APIs are now
stable, supporting simpler development practices and ensuring compatibility with core
libraries. The release includes more efficient bundling with tree-shakable standalone
APIs for Router and HttpClient and an ngSrc image directive for smarter data fetching
and improved performance.

Standalone API (Stable)

Performance Dev Experience Efficiency

Challenge:
The problems with NgModules and the benefits of the Standalone API were presented
in the Angular 14 chapter. The solution at that time had disadvantages related to not
being in a stable version, as well as shortcomings regarding providers, adapting many
basic modules and benefiting from abandoning modules.

Solution:
Starting with version 15, the standalone APIs drop the developer preview label and
become stable. Thanks to this, we get the green light to safely utilize them in our
applications, including production.

Let’s explore the major changes around Angular Routing. First, we received a new type
of guard – canMatch. So what’s the difference between this new one, canLoad, which tells
us whether we can load a route that references a lazily loaded module, and canActivate/
canActivateChild, which tells us whether we can activate a child route/route? CanMatch
works, in a sense, at a different, “earlier” stage and decides whether the current url can
be matched against a given route. This means it can play a similar role to both canLoad
(which it will eventually replace) and canActivate.* However when it returns false,
subsequent routing configuration entries are processed.

This means we gain a new possibility – defining a route with the same path, but
navigating to different places based on the logic implemented by the guard. This is
useful, for example, when handling different user roles, or conditionally loading another
version of the feature based on feature flags. Here’s a usage example:

Angular v15 | The Ultimate Guide to Angular Evolution 33


class CanMatchSettings implements CanMatch {
constructor(private currentUser: User) {}

canMatch(route: Route, segments: UrlSegment[]): boolean {


return this.currentUser.isAdmin;
}
}

const routes: Routes = [


{
path: ‘settings’,
canMatch: [CanMatchSettings],
loadComponent: () =>
import(‘./admin-settings/admin-settings.component’).then(
(v) => v.AdminSettingsComponent
),
},
{
path: ‘settings’,
loadComponent: () =>
import(‘./user-settings/user-settings.component’).then(
(v) => v.UserSettingsComponent
),
},
];

The Router API has been fully adjusted to the standalone approach, so we no longer need
to use the Router Module. Instead, we get a whole set of alternative APIs, which also have
the advantage of being easily treeshakeable:

const routes: Routes = [...];

bootstrapApplication(AppComponent, {
providers: [
provideRouter(
routes,
withDebugTracing(),
withPreloading(PreloadAllModules)
),
],
});

Angular v15 | The Ultimate Guide to Angular Evolution 34


Another new feature is support for functional guards and resolvers. This means that it
is possible to implement them in the form of plain functions, so we can say goodbye to
the classes being the only option here. The introduction of this concept triggered
many reactions among the community, both positive and negative. Some developers
see this as a new direction of the framework. Personal preferences aside, one thing
cannot be denied – it requires much less boilerplate. What is more, it is now very easy
to create higher-order, parametrized functions returning a properly configured version
of the guard based on that. A usage example, which you can easily compare with the
earlier example, looks as follows:

const routes: Routes = [


{
path: ‘settings’,
canMatch: [() => inject(User).isAdmin],
loadComponent: () =>
import(‘./admin-settings/admin-settings.component’).then(
(v) => v.AdminSettingsComponent
),
},
{
path: ‘settings’,
loadComponent: () =>
import(‘./user-settings/user-settings.component’).then(
(v) => v.UserSettingsComponent
),
},
];

A small, but lovely, improvement also appeared in the syntax for importing lazy-loaded
paths. It is possible to omit the “.then(...)” part if we use the default export in the target file.

@Component({
standalone: true,
...
})
export default class MyComponent { ... }

{
path: ‘home’,
loadComponent: () => import(‘./my-component’),
}

Angular v15 | The Ultimate Guide to Angular Evolution 35


Every lazy-loaded route creates a separate injector, just like every lazy loaded ngModule
created its own injector. The counterpart of the “providers” array in the NgModule
configuration is now moved to the route configuration:

{
path: ‘admin’,
providers: [AdminService],
children: [
{path: ‘users’, component: AdminUsersCmp},
{path: ‘teams’, component: AdminTeamsCmp},
],
}

Providers declared in the route are available for the component declared at the same
level and for all its descendants.

Similarly to Router, HttpClient has also been adapted to the module-less approach:

bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(
withXsrfConfiguration({
cookieName: ‘MY-XSRF-TOKEN’,
headerName: ‘X-MY-XSRF-TOKEN’,
})
),
],
});

Angular v15 | The Ultimate Guide to Angular Evolution 36


The transition to Standalone API also affected Angular interceptors:

bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(
withInterceptors([
(request, next) => {
console.log(‘Url: ‘, request.urlWithParams);
return next(request);
},
])
),
],

During migration to functional interceptors, you can still use your class-based interceptors
configured using a multi-provider by adding withInterceptorsFromDi utility function as
follows:

bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(withInterceptorsFromDi()),
{
provide: HTTP_INTERCEPTORS,
useClass: YourClassBasedHttpInterceptor,
multi: true,
},
],
});

If in your standalone app, you need any providers from third-party libraries that are
available only inside NgModules, you can use the importProvidersFrom utility function:

bootstrapApplication(AppComponent, {
providers: [importProvidersFrom(MatDialogModule)],
});

Angular v15 | The Ultimate Guide to Angular Evolution 37


It is also worth mentioning that the shift towards a module-less approach simplifies the
creation of dynamic components that are self-contained and no longer need their
Ngmodules.

@Component({...})
export class MyComponent {
constructor(private readonly viewContainerRef: ViewContainerRef) {}
create(): void {
this.viewContainerRef.createComponent(OtherComponent);
}
}

Benefits:
The proposed module-less approach:
• Simplifies the process of application development: reduces the reliance on
NgModules and minimizes boilerplate code.
• Enables faster development cycles and a cleaner codebase: provides a more
direct and streamlined API for key aspects like bootstrapping, routing, and dynamic
component instantiation.
• Improves app performance: thanks to the shift towards a providers-first
approach, which enhances tree-shakability.

Expert Opinion:

The standalone API makes many things easier. Especially since you now have to provide
everything you need in one component, and it does not magically come from somewhere.
This makes the topic of DependencyInjection much easier to understand. This also made
the lazy loading of components possible. My tip for this: Declare the component class as
a default export so that you don’t have to resolve the promise yourself during lazy load-
ing. This makes the dynamic import shorter.

~ David Muellerchen
Google Developer Expert

Angular v15 | The Ultimate Guide to Angular Evolution 38


Directive composition API

Efficiency Dev Experience

Challenge:
One of the most wanted features in the framework was the ability to reuse directives
and apply their behavior to other directives or components.

Up to this point, there have been few possibilities to partially achieve a similar result,
such as the use of inheritance, where the main limitation is that only one base class
can be used.

Another idea used, for example, by Angular Material, is the use of TypeScript mixins. But
this forces a specific approach to the code shared this way, which heavily complicates
implementation and doesn’t allow for the use of Angular APIs in mixins.

Solution:
The Directive Composition API in Angular is a feature that allows directives to be
applied to a different directive or a component’s host element directly from within
the component’s TypeScript class. The only major restriction is that only standalone
directives can be applied to our directives/components, which on the other hand,
don’t need to be standalone.

@Component({
selector: ‘my-component’,
templateUrl: ‘./my-component.html’,
hostDirectives: [
{
directive: NgClass,
},
{
directive: CdkDrag,
inputs: [‘data’],
outputs: [‘moved: dragged’],
},
],
standalone: true,
})
export class MyComponent {}

Angular v15 | The Ultimate Guide to Angular Evolution 39


The above piece of code applies the NgClass and CdkDrag directives to our component.
The first one doesn’t expose any inputs or outputs, so you won’t be able to use them in
the template where our component is used. The second, on the other hand, exposes both
input and output, with an alias defined for output. Therefore, the use of our component
could look as follows:

<my-component [data]=”myData” (dragged)=onDragged($event)>


</my-component>

But can we control the behavior of applied directives from inside the component? This is
possible using the inject function, which allows us to inject the instance of the directive
into the component and manipulate its properties. It looks like this:

@Component({
selector: ‘my-component’,
templateUrl: ‘./my-component.html’,
hostDirectives: [
{
directive: NgClass,
},
],
standalone: true,
})
export class MyComponent {
private ngClassDirective = inject(NgClass);
someCallback(): void {
this.ngClassDirective.ngClass = ‘my-class’;
}
}

Benefits:
Directive Composition API:
• Improves the developer experience: enhances code modularity by allowing
developers to encapsulate and reuse behaviors across different components and
directives.
• Leads to a cleaner and more organized codebase
• Makes the maintenance and updating of the application more efficient.

Angular v15 | The Ultimate Guide to Angular Evolution 40


• Simplifies the implementation process: it applies directives directly to the host
element from within the TypeScript class, reducing the need for complex configurations
and boilerplate code in the templates.

Image directive

Efficiency Performance UX

Challenge:
The app may take a long time to load in the browser due to the way images are loaded.
This can be especially noticeable when the website contains a lot of multimedia.

Solution:
In collaboration with the Aurora team, the Angular team has introduced the
NgOptimizedImage to enhance image optimization and incorporate best practices
for image loading performance. It is a standalone directive designed to boost
image loading efficiency. It became a stable feature in Angular version 15.

To activate iNgOptimizedImage, simply replace the image’s src attribute with ngSrc.

<img ngSrc=”cat.jpg”>

If the LCP image is shown, a good way to prioritize its loading is to use property called
“priority”.

<img ngSrc=”cat.jpg” priority>

Thank for that ‘priority’ applies three optimizations:


• Sets fetchpriority=high - gets resources in the optimal order and prioritizes the image
• Sets loading=eager - lazy-loading images
• Automatically generates a preload link element if it uses SSR.
NgOptimizedImage requires us to specify a height and width for the image or attribute
‘fill’ to prevent image-related layout changes. But if the ‘fill’ attribute is used, it’s
necessary to set the parent element ‘position: relative/fixed/absolute’. When using CDN
images, it is possible to compress images and convert them on demand to formats such

Angular v15 | The Ultimate Guide to Angular Evolution 41


as WebP or AVIF.
The NgOptimizedImage image directive also offers other solutions to improve
application performance when loading images such as:
• supporting resource hints - preloading critical assets
• possibility to preload the resource for all routes instead of manual addition of
preload resource hint.

Benefits:
Using the NgOptimizedImage directive and ngSrc attribute is very simple, almost
cost-free, and can significantly improve an application’s performance, SEO and core
web vitals.

MDC-based components

Efficiency Dev Experience UX

Challenge:
The current version of the Angular Material library was loosely linked to the official
Material Design specification. All the styles and behaviors were reinterpreted and
reimplemented to Angular style. With the arrival of Material Design Components for Web
(MDC), the library became outdated It became clear that new Angular Material should
be strongly and directly based on official MDC design tokens. These include values like
colors, fonts and measurements.

Solution:
In Angular Material version 15, a significant migration took place. Many components
are now being refactored to be based on Material Design Components for Web
(MDC). Various components have been refactored, leading to changes in styles, APIs
and even complete rewrites for some. Components like form-field, chips, slider and
list have significant changes in their APIs to integrate with MDC. There are library-wide
changes affecting component size, color, spacing, shadows and animations to improve
spec-compliance and accessibility. Theming changes include updates to default
typography levels and themeable density. Each component has specific changes,
including style updates, element structure and API modifications.

Benefits:
These changes offer several benefits, such as:
• Improved accessibility
• Better adherence to the Material Design spec
• Faster adoption of future Material Design versions: due to shared infrastructure

Angular v15 | The Ultimate Guide to Angular Evolution 42


CDK Listbox

Efficiency Dev Experience UX

Challenge:
Creating a typical listbox with accessibility support, keyboard events support,
multiselection and correct scroll behavior, as well as satisfying WAI ARIA listbox pattern
requirements is a time-consuming task.

Solution:
The newly added component to the Angular CDK library meets all the above guidelines
while being a fully customizable solution.

Appointment Time

Fri, 20 Oct, 12:00

Fri, 20 Oct, 13:00

Fri, 20 Oct, 14:00

Fri, 20 Oct, 15:00

The code example:

<label class=”example-listbox-label” id=”example-appointment-label”>


Appointment Time
</label>
<ul cdkListbox
[cdkListboxValue]=”appointment”
[cdkListboxCompareWith]=”compareDate”
(cdkListboxValueChange)=”appointment = $event.value”
aria-labelledby=”example-appointment-label”
class=”example-listbox”>
<li *ngFor=”let time of slots”
[cdkOption]=”time”
class=”example-option”>
{{formatTime(time)}}
</li>

Angular v15 | The Ultimate Guide to Angular Evolution 43


Benefits:
Using a ready-to-use, safe, tested and trustworthy solution with WAI ARIA standards
directly from the creators of Angular saves developers a significant amount of time.

Improved stack traces

Dev Experience

Challenge:
Up until now, the stack traces presented in CLI and browser devtools were quite obscured
by external functions (i.e. from webpack or node_modules). It was hard to investigate
the execution order when the trace included many lines from outside the code written by
the programmer.

Solution:
Improvements in this area are possible thanks to the cooperation of the Angular Team
and the Chrome team. The goal was to mark scripts as “external” and thus exclude them
in the development tool functions(e.g. stack traces). Starting with Angular 14.1, the
contents of the node_modules and webpack folders are marked in this way. It is worth
mentioning that this mechanism is available to all developers, so authors of other
frameworks can use it as well.

The result is a stack trace that omits scripts that are probably not in the developer’s area
of interest when an error is shown in the console. Also, the code that belongs to excluded
scripts is skipped when you’re debugging and iterating over subsequent instructions in
the code.

GET http://localhost:4200/random-number 404 (Not Found) app.component . ts:27


(anonymous) @ app.component . ts:27
Zone - setTimeout (async)
(anonymous) @ app.component . ts:4
timeout @ app.component . ts:4
(anonymous) @ app.component . ts:22
Zone - Promise.then (async)
(anonymous) @ app.component . ts:22
increment @ app.component . ts:38
AppComponent_Template_app_button_handleClick_3_listener
@ app.component . ts:12
onClick
@ button.component . ts:18
ButtonComponent_Template_input_click_0_listener
@ button.component . html:1
Zone - HTMLInputElement.addEventListener : click (async)
ButtonComponent_Template
@ button.component . html:1
Promise.then (async)
4431
@ main . ts: 7
__webpack_exec__
@ main . ts: 8
(anonymous)
@ main . ts: 8
(anonymous)
(anonymous) @ main . ts: 8

Show 229 more frames @ main . ts: 2

Angular v15 | The Ultimate Guide to Angular Evolution 44


The second new feature worth mentioning is stack trace linking for asynchronous
operations. The code executed following the completion of an asynchronous action can
now be properly linked with the code initiating this action (e.g. a click and call to the
server). This is possible thanks to the introduction of the so-called Async Stack Tagging
API mechanism in Chrome.

Call Stack

Show ignore - listed frames

(anonymous) app.component.ts:36
Promise.then (async)

(anonymous) app.component.ts:27
Zen - setTimeout (async)

(anonymous) app.component.ts:4

timeout app.component.ts:4

(anonymous) app.component.ts:22
Zone - Promise.then (async)

(anonymous) app.component.ts:22

increment app.component.ts:38

AppComponent_Template_app_button_handleclick_3_listener app.component.ts:12

onClick app.component.ts:18

ButtonComponent_Template_input_click_0_listener app.component.ts:1
Zone - HTMLInputElement.addEventListener:click (async)

ButtonComponent_Template app.component.ts:1

Benefits:
Both sync and async traces have readable form and provide more information to the
programmer. Therefore these improvements:
• Boost developer experience
• Ease the debugging process

Angular v15 | The Ultimate Guide to Angular Evolution 45


Auto-imports in language service

Efficiency Dev Experience

Challenge:
With Standalone API, we frequently need to add extra imports to the Component
metadata, because the only components, directives and pipes available in the
template are ones we explicitly imported.

Solution:
The Angular Language Service is a tool utilized by all code editors to enhance errors, hints
and navigation between Angular templates. The new DX-related improvement,
a part of language service, allows us to automatically import components whose
selectors were used in another component’s template. This applies to both
standalone and module-based components.

import { Component, NgModule } from ‘@angular/core’;


import { BrowserModule } from ‘@angular/platform-browser’;

@Component ({
selector: ‘app-root’,
template: ‘<app-foo></app-foo>’,
styleUrls: [‘./app Quick Fix...
}) Import FooModule from ‘./foo/foo.module’ on AppModule
export class AppComp
Extract...
title = ‘tour - of - k
Extract to readonly field in class “AppComponent”
}

Benefits:
This tool is utilized by IDEs, and it significantly simplifies and speeds up the import
of components, directives and pipes using keyboard shortcuts, enabling the
programmer to focus on other tasks.

Angular v15 | The Ultimate Guide to Angular Evolution 46


Typescript/Node.js support

Angular 14.2 added support for TypeSript 4.8, Node.js v18 and Angular 15.1 added
support for Typescript 4.9. According to the official documentation, it includes, among
other things, a number of performance improvements that can reduce build and hot
reload times of TypeScript projects by up to 40%. Let’s take a look at other new features
worth mentioning.

The satisfies Operator (available in 4.9)

Dev Experience

Challenge:
When we define an expression, TypeScript infers the most general type for it:

const person = { name: ‘John’ };


// typeof person: { name: string }

In the example above, the typeof “name” property is not narrowed to const literal ”John”
but generalized to any string value. In some cases, we would like to ensure that
the expression matches a certain type while also ensuring the most specific type at the
same time.

Solution:
TypeScript version 4.9 introduced a new operator called “satisfies.” It validates whether
the type of an expression matches a type without changing the resulting type of that
expression.

Angular v15 | The Ultimate Guide to Angular Evolution 47


type Colors = ‘red’ | ‘green’ | ‘blue’;
// Ensure that we have exactly the keys from ‘Colors’.
const myColors = {
red: ‘yes’,
green: false,
blue: ‘maybe’,
orange: false,
// error - “orange” is not part of “Colors” union type
} satisfies Record<Colors, unknown>;
/**
* typeof myColors is not Record<Colors, unknown>
* but {“red”: string, “green”: boolean, “blue”: “string”}
* All the information about the ‘red’, ‘green’, and ‘blue’
* properties are retained.
*/
const isGreen: boolean = myColors.green;
//typeof myColors.green is not unknown despite “satisfies” type check

This operator can be combined with “as const” to infer type from constant values and
check them against a broader type at the same time:

const friends = [
{ name: ‘John’ },
{ name: ‘Paul’ },
] as const satisfies readonly { name: string }[];

In the example above, expression is type-checked with the satisfies operator, but
the inherited type for friends const comes directly from the value.

Satisfies is somewhat similar to “as” operator. The difference is that with “as,”
the programmer is responsible for the type safety and with “satisfies”, TypeScript
validates the type we assert automatically.

Angular v15 | The Ultimate Guide to Angular Evolution 48


type Red = ‘red’;
const x: Red = ‘blue’ as Red;
// everything ok
const y: Red = ‘blue’ satisfies Red;
// error: Type ‘”blue”’ does not satisfy the expected type ‘”red”’

Benefits:
The new operator improves the TypeScript developer experience as it enforces
type safety without the loss of information (i.e. via type widening). By replacing the
“as” operator with “satisfies,” we also reduce the reliance on untrustworthy type
assertions. The code readability is also improved in some cases.

Checks For Equality on NaN (available in 4.9)

Dev Experience

Challenge:
In IEEE-754 floats standard, supported by JavaScript and TypeScript, nothing is ever
equal to NaN, the“not a number” value. However, it is a common mistake to check it with
someValue === NaN, instead of using the built-in Number.isNaN function.

Solution:
Typescript no longer allows direct comparisons against NaN and suggests Number.is-
NaN instead:

function validate(someValue: number) {


return someValue !== NaN;
// ~~~~~~~~~~~~~~~~~
// error: This condition will always return ‘true’.
// Did you mean ‘!Number.isNaN(someValue)’?
}

Benefits:
Thanks to the new restriction, we avoid an obvious error and are clearly informed
about an error.

Angular v15 | The Ultimate Guide to Angular Evolution 49


Esbuild

Non-destructive Hydration

Signals

Angular v16
release date: 05.2023
Angular version 16 brings substantial advancements, focusing on creating a more
modern and efficient framework. The introduction of signals marks the beginning of
a refined change detection mechanism, while non-destructive hydration sets the stage
for advanced hydration scenarios crucial for public web platforms.

Enhancements in development ease are achieved through mandatory inputs, routing


parameter bindings and the new DestroyRef. Furthermore, the Angular CLI now supports
creating applications with standalone components, and the experimental esbuild-based
builder offers a substantial speed boost in application building.

Signals library (developer preview)

Performance Dev Experience Efficiency

Challenge:
Angular applications can face performance issues due to inefficient change detection
mechanisms, resulting in an excessive number of computations to determine what has
changed in the application state. Additionally, developers might struggle with a more
complex mental model for understanding reactivity, data flow, RxJS and dependencies
within the application, making it harder to write, maintain, and optimize their code
efficiently.

Solution:
In Angular 16, we received a developer preview of a brand new library containing
a primitive that represents a reactive value–the Angular signals library.

Angular’s signals library introduces a way to define reactive values and establish explicit
dependencies between them within an application. Unlike RxJS, observables that push
changes through a stream of values, signals allow for a more direct and straightforward
way to declare reactive state, compute derived values and handle side-effects, offering
a different approach to managing reactivity in Angular applications.

A signal in Angular is essentially a wrapper around a value, which notifies interested


consumers (e.g., components, functions) when that value changes. Signals can hold any
type of value, ranging from simple primitives like numbers and strings, to more complex
data structures such as objects or arrays.

Here are a few reasons behind Angular signals implementation:


• Angular can keep track of which signals are read in the view. This lets you know
which components need to be refreshed due to a change in state.
• The ability to synchronously read a value that is always available.
• Reading a value does not trigger side effects.

Angular v16 | The Ultimate Guide to Angular Evolution 51


• No glitches that would cause inconsistencies in reading the state.
• Automatic and dynamic dependency tracking, and thus no explicit subscriptions.
That frees us from having to manage them to avoid memory leaks.
• The possibility to use signals outside of components, which works well with the
Dependency Injection.
• The ability to write code in a declarative way.
This is how we can define signals and express dependencies between them:

@Component({...})
export class App {
firstName = signal(‘Ash’);
lastName = signal(‘Ketchum’);
fullName = computed(() => `${this.firstName()} ${this.lastName()}`);
setName(newName: string): void {
this.firstName.set(newName);
}
}

‘firstName’ and ‘lastName’ are writable signals, meaning they provide an API for updating
their values directly. ‘fullName’ is a readonly signal that depends on the other signals and
is recalculated every time any dependent signal changes its value.

In many cases, it is useful to define a side effect. This is a call to code that changes state
outside its local context, such as sending an http request or synchronizing two
independent data models. To create an effect we can use “effect” function:

effect(() => {
console.log(`The current value of my signal is: ${mySignal()}`);
});

The Angular Team also provides a bridge between signals and RxJS, placed in the
@angular/core/rxjs-interop library. It is possible to convert a signal to observable and an
observable to a signal.

Angular v16 | The Ultimate Guide to Angular Evolution 52


import { toObservable, toSignal } from ‘@angular/core/rxjs-interop’;
@Component({...})
export class App {
firstName = signal(‘Ash’);
firstName$ = toObservable(this.firstName);

lastName$ = of(‘Ketchum’);
lastName = toSignal(this.lastName$, ‘’);
}

The real revolution will come when the Angular Team releases signal-components. These
components will work without zoneJS and will have their own change detection strategy
based solely on signals. It will be possible to update DOM elements with surgical
precision, without unnecessary traversal of the component tree.

It’s also worth mentioning that external libraries like ngrx also implement support for
signals. The NgRx store will have its signal-based counterpart called SignalStore.

import { signalState } from ‘@ngrx/signals’;


const state = signalState({
user: {
firstName: ‘John’,
lastName: ‘Smith’,
},
foo: ‘bar’,
numbers: [1, 2, 3],
});
console.log(state()); // { user: { firstName: ‘John’, lastName: ‘Smith’ }, foo: ‘bar’ }
console.log(state.user()); // { firstName: ‘John’, lastName: ‘Smith’ }
console.log(state.user.firstName()); // ‘John’

Benefits:
The introduction of a new experimental reactive primitive is a promise of a simplified
mental model for reactivity in Angular. Its goals are to lower the learning curve, reduce
the number of errors and make the whole development process intuitive and
straightforward. With the future arrival of signal-based components, significant
performance improvements are expected.

Angular v16 | The Ultimate Guide to Angular Evolution 53


Expert Opinion:

I think signals will have a dramatic impact on many sides of the framework from
performance to bundle size improvements. This will be especially visible when signals are
integrated into the change detection mechanism. It will make the process much more
efficient and performant because, unlike the current zone.js-based implementation,
signals will allow Angular to know in which exact component a change occurred and
update only the affected components. Besides that, the change detection mech-
anism will become much simpler and unified, because we will have only one predictable
change detection strategy, which will also have a positive impact on the learning curve.
Finally, we will be able to reduce bundle size by removing the zone.js dependency, which
will not be needed for the new change detection mechanism. And this is just one of the
examples. I think that we are currently seeing only the tip of the iceberg.

~ Dmytro Mezhenskyi
Google Developer Expert

The Angular signal feature is currently in its Developer Preview stage. Once this feature
becomes stable, it is poised to introduce an entirely new approach to writing
Angular applications. The elimination of zone.js will free developers from concerns
related to change detection cycles and performance issues, allowing them to
concentrate more on their business logic. This is exemplified by the transition from
familiar [(ngModel)] binding syntax to the new signal and effect syntax, which requires
developers to adapt and update their projects. Despite these changes, I firmly believe
that this innovation holds great promise in advancing the framework to the next level.

~ Balram Chavan
Google Developer Expert

SSR Hydration (developer preview)

Performance UX

Challenge:
By default, Angular renders applications only in a browser. If any web crawler tries to
index the page, it receives an almost empty HTML file and is unable to navigate and find
searchable content. This is one of the reasons why we might want to implement
Server-Side Rendering, the process of rendering our site on the server side and returning
the whole HTML structure.

A server-side rendered HTML is displayed in the browser, while the Angular app
bootstraps in the background, reusing the information available in server-generated
HTML. The HTML then destroys DOM and replaces it with with a newly rendered and fully
interactive Angular app.

Angular v16 | The Ultimate Guide to Angular Evolution 54


Destroying and replacing an application’s DOM may result in a visible UI flicker and has
a negative impact on Core Web Vitals, such as FID, LCP and CLS.

Solution:
Angular introduced hydration, a new feature currently available for developer preview.
We can enable it as follows:

import { bootstrapApplication, provideClientHydration, } from ‘@angular/


platform-browser’;
...

bootstrapApplication(RootCmp, {
providers: [provideClientHydration()]
});

It enables reusing server-side rendered HTML in the browser without destroying it.
Angular matches the existing DOM elements with application structure at runtime. This
results in a performance improvement in Core Web Vitals and a better SEO performance.

There is also an option to skip hydration for some components, or rather component
trees, if they’re not compatible with hydration. For example, manipulating DOM directly
with browser APIs. Use one of the following options to enable hydration:

#1

<test-component ngSkipHydration />

#2

@Component({
...
host: {ngSkipHydration: ‘true’},
})
class TestComponent {}

Angular v16 | The Ultimate Guide to Angular Evolution 55


There are also some other improvements added for SSR. These include new
standalone-friendly utility functions to provide SSR capabilities to an application
(provideServerRendering()) and HTTP transfer state (withTransferCache()).

If your Angular application uses SSR, you should definitely check this feature out. More
details are available in the official documentation: https://angular.io/guide/hydration

Benefits:
Full hydration brings:
• Better UX: faster loading and interactivity, no more flickering
• Enhanced SEO: in comparison to destructive hydration

Expert Opinion:

The Angular team has been deeply committed to the ongoing refinement of Server-Side
Rendering (SSR) capabilities. Over time, they’ve introduced a series of enhancements to
SSR applications, aiming to provide a smoother and more efficient experience for both
developers and end-users. The team’s focus has been on achieving the goal of a fully
hydrated application, which means that the application is not just server-rendered but
also preloaded with the necessary data and components, offering a seamless and
responsive user experience.

This concerted effort to improve SSR reflects the Angular team’s dedication to staying
at the forefront of web development technology. It’s an exciting journey of innovation,
and it’s intriguing to anticipate how these ongoing efforts will shape the landscape
of Angular in the future. The advancements in SSR hold the promise of further
enhancing the performance, SEO-friendliness, and overall user experience of Angular
applications, making them even more competitive and appealing in the dynamic world
of web development.

~ Balram Chavan
Google Developer Expert

Vite-powered dev server

Performance Dev Experience

Challenge:
Previous experimental features related to the Angular building systems affected
only the building process and not the development process, especially in the case
of the development server with hot-reload.

Angular v16 | The Ultimate Guide to Angular Evolution 56


Solution:
The esbuild-based build system for Angular CLI entered developer preview in version 16,
with the goal of significantly speeding up the build process. Early tests showed
improvements of over 72% in cold production builds. With this update, Vite is utilized
as the development server, while esbuild enhances both development and production
builds for faster performance. However, it’s important to note that Angular CLI exclusively
uses Vite as a development server. This is due to the Angular compiler’s need to maintain
a dependency graph between components, requiring a different compilation model.

You can enable esbuild and Vite by updating your angular.json file:

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

Benefits:
Improvements in the Angular building system bring:
• An improved developer experience
• Reduced time and costin heavy CI/CD processes
Please note that these changes are still in experimental mode.

Required inputs

Dev Experience

Challenge:
Until Angular 16, it was not possible to mark inputs as required, but there was a common
workaround for this using a component selector:

Angular v16 | The Ultimate Guide to Angular Evolution 57


@Component({
selector: ‘app-test-component[title]’, // note attribute selector here
template: ‘{{ title }}’,
})
export class TestComponent {
@Input()
title!: string;
}

Unfortunately, this was far from ideal. The first thing was that we polluted the
component selector. We always had to put all required input names to the selector,
which was especially problematic during refactors. It also resulted in a malfunction of
the auto-importing mechanisms in IDEs. And second, forgetting to provide the value for
an input marked this way meant the error was not very accurate, as there was no match
at all for such an “incomplete” selector. You can see this here:

ERROR
src/app/app.component.html:1:1 - error NG8001: ‘app-test-component’ is not a known element:

1.If ‘app-test-component’ is an Angular component, then verify that it is part of this module.

2. If ‘app-test-component’ is a Web Component then add ‘CUSTOM_ELEMENTS_SCHEMA” to the @NgModule.schemas’ of

this component to suppress this message.

1.<app-test-component></app-test-component>

~~~~~~~~~~~~~~~

src/app/app.component.ts:5:16

5 templateURL: ‘./app.component.html’,

~~~~~~~~~~~~~~~

Error occurs in the template of component AppComponent.

Solution:
The new feature allows us to explicitly mark the input as required, either in the @Input
decorator:

@Input({required: true}) title!: string;

Angular v16 | The Ultimate Guide to Angular Evolution 58


or the @Component decorator inputs array:

@Component({
...
inputs: [
{name: ‘title’, required: true}
]
})

However, the new solution has two serious drawbacks. One of them is that it only works
in AOT compilation and not in JIT. The other drawback is that this feature still suffers from
the strictPropertyInitialization TypeScript compilation flag, which is enabled by default
in Angular. TypeScript will raise an error for this property since it was automatically
declared as non-nullable but not initialized in the constructor or inline.

ERROR
src/app/test-component/test-component.component.ts:9:3 - error TS2564: Property ‘title’ has no initializer and is not

definitely assigned in the constructor.

9 title: string;
~~~

This means that you still need to disable this check here e.g. by explicitly marking this
property with a non-null assertion operator, even though it has to be provided in the
consumer template:

@Input({required: true}) title!: string;

Benefits:
The existing workarounds are replaced by a new solution with a very simple and
straightforward syntax.

Angular v16 | The Ultimate Guide to Angular Evolution 59


Input transform function

Dev Experience

Challenge:
Angular interprets all static HTML attributes as strings. For example, if boolean attributes
are considered true when they are present on a DOM node,i.e. “disabled,” Angular,
by default, interprets them as strings.

Solution:
Since the release of Angular 16.1, we can provide an optional transform function for the
@Input decorator. This allows us to simplify the process of transforming values and
discontinue the use of setters and getters that have been used for this purpose so far.
Transform function:

function toNumber(value: string | number): number {


return isNumber(value) ? value : parseInt(value);
}

@Component({
selector: ‘app-foo’,
template: ``,
standalone: true,
})
export class FooComponent {
@Input({ transform: toNumber }) width: number;
}

Usage in template:

<app-foo width=”100” />


<app-foo [width]=”100” />

Angular v16 | The Ultimate Guide to Angular Evolution 60


Additionally, Angular offers us two built-in transform functions that we can use:

import { booleanAttribute, numberAttribute } from ‘@angular/core’;

// Transforms a value (typically a string) to a boolean.


@Input({ transform: booleanAttribute }) status!: boolean;

// Transforms a value (typically a string) to a number


@Input({ transform: numberAttribute }) id!: number;

Benefits:
Transform functions are pure functions, so they improve readability, reusability and
testability.

Router data input bindings

Dev Experience Efficiency

Challenge:
Obtaining router-related data inside components is not very comfortable. We have to
inject an ActivatedRoute token, subscribe to its properties, manage these subscriptions
to ensure no memory leaks, and so on.

Solution:
Angular version 16 brings another interesting feature related to component inputs: the
ability to bind them directly to the current route variables, such as path params and
query params. This eliminates the need to inject ActivatedRoute into the component in
order to use router data.

With this feature, data is being bound only to the routable components present in the
routing configuration, and inputs of children components used in the templates of
routable routes are not affected. Route data is matched with inputs by name,or input
alias name if present. This is done so there is more than one piece of data that can
potentially be put as the input value.

The list below shows the precedence of data being bound to input property if the names
are the same:

Angular v16 | The Ultimate Guide to Angular Evolution 61


1. Resolved route data
2. Static data
3. Optional/matrix params
4. Path params
5. Query params

There is much more complexity if we consider “inheriting” data from places like the
parent route configuration and the parent component presence.

T o ena b le thi s feature, we hav e to use a d e d ica te d function ca lle d w i t h


Com pone ntI n putBi ndi n g.

Example route definition:

bootstrapApplication(AppComponent, {
providers: [
provideRouter(
[
{
path: ‘example/:id’,
data: {
bar: true,
},
resolve: { baz: () => ‘example string’ },
loadComponent: () => import(‘./app/todo/todo.component’),
},
],
withComponentInputBinding()
),
],
});

Angular v16 | The Ultimate Guide to Angular Evolution 62


Exinput binding (input names must match route param names or their aliases):

@Component({
selector: ‘example-cmp’,
standalone: true,
template: ‘’,
})
export default class ExampleComponent {
@Input() id!: string;
@Input() bar!: boolean;
@Input() baz!: string;
}

Benefits:
Router data input bindings simplifies the task of transmitting router data to the routed
components, consequently diminishing the requirement for repetitive code and dealing
with extra RxJS subscriptions.

Injectable DestroyRef and takeUntilDestroyed

Dev Experience

Challenge:
The cleanup process was a common problem Angular programmers had to deal with,
especially when we wanted to implement a component/directive lifecycle with long
RxJS subscriptions. The standard way to do this was to use an OnDestroy lifecycle
hook to complete all relevant streams and execute other cleanup actions. However,
this caused some overhead and unwanted boilerplate code. Another way to achieve
this implementation was to use third-party solutions like @ngneat/until-destroy, but
as opposed to built-in ones, they can introduce compatibility issues and less seamless
integration.

Solution:
In the new Angular version, we receive a brand new injectable for registering cleanup
callback functions that will be executed when a corresponding injector is destroyed.

Angular v16 | The Ultimate Guide to Angular Evolution 63


const destroyRef = inject(DestroyRef);

// register a destroy callback


const unregisterFn = destroyRef.onDestroy(() => doSomethingOnDestroy());

// stop the destroy callback from executing if needed


unregisterFn();

Such an injectable is allowed to introduce yet another feature: the takeUntilDestroyed


operator. This operator uses the DestroyRef injectable underneath and is a safe way to
avoid memory leaks in Angular apps.

import { takeUntilDestroyed } from ‘@angular/core/rxjs-interop’;

@Component({
...
})
export default class MyComponent {
constructor() {
interval(3000).pipe(takeUntilDestroyed()).subscribe(
value => console.log(value)
);
}
}

Benefits:
The new operator is a convenient way to avoid memory leaks without dealing with
OnDestroy lifecycle hooks and subscription references. DestroyRef also allows you to
deal with other cleanup tasks in any way that fits your needs.

Angular v16 | The Ultimate Guide to Angular Evolution 64


Self-closing tags

Dev Experience

Challenge:
When we don’t use a content projection mechanism, we are not placing any content
between tags of the Angular components we use in templates. HTML specification
contains many self-closing tags for nodes without inner HTML (i.e. <img />, <input />,
<br />), but in Angular, we always had to repeat the same name in opening and closing
tags, as follows:

<app-your-component-name [foo]=”bar”>
</app-your-component-name>

Solution:
Self-closing tags is a highly requested feature that arrived in Angular version 16. You can
use self-closing tags for your components.

This feature is optional and backward compatible, so you can continue to use
the standard approach, especially when using content projection.

<app-your-component-name [foo]=”bar”/>

Benefits:
Angular template syntax gets easier and more readable.

Angular v16 | The Ultimate Guide to Angular Evolution 65


runInInjectionContext

Dev Experience

Challenge:
There was no convenient way to inject dependencies outside of construction time, like
after user interaction or conditionally after resolving some asynchronous value.

Solution:
An “inject” function is a function that Injects a token from a current injection context.
We mostly use it during the construction time of classes being instantiated by the DI
system. Examples of classes include components, pipes and injectable services.

There is, however, a possibility to call “inject” at any time of the life cycle if we use the
“runInInjectionContext” function. This allows us to instantiate dependency on demand
after a user interaction.

@Injectable({
providedIn: ‘root’,
})
export class MyService {
private injector = inject(EnvironmentInjector);
someMethod(): void {
runInInjectionContext(this.injector, () => {
const otherService = inject(OtherService);
});
}
}

Benefits:
We get a possibility to inject dependencies at any time, as long as we have a reference
to the injector. This approach can improve application performance if we combine it with
lazy loading.

Angular v16 | The Ultimate Guide to Angular Evolution 66


Standalone API CLI support

Dev Experience Efficiency

Challenge:
There was no way to generate a new project as a standalone from the beginning. We
had to manually remove AppModule and rewrite the bootstrapping process to
a standalone version with a bootstrapApplication function call. All components,
directives and pipes in such a project were generated using built-in code generators in
a non-standalone version by default.

Solution:
Angular version 16 introduced a new flag for CLI command with which we can generate
a new Angular project.

ng new --standalone [name]

The project output with such a flag is simpler compared to the old ngModule-based
version. Generators for components, directives and pipes in this project will also create
standalone versions by default.

Benefits:
Kickoff of a new standalone project is made very easy, increasing overall project efficiency.

Typescript/Node.js support

Angular v16 brings support for TypeScript 5.0, and Angular v16.1 for Typescript 5.1. This
results in further computation time, memory usage and package size optimizations,
as well as many feature enhancements.

Angular v16 | The Ultimate Guide to Angular Evolution 67


ECMAScript decorators (available in 5.0)

Dev Experience

Challenge:
Typescript supported custom experimental decorators for years. They’re familiar to every
Angular Developer, as we use them to define things like modules, components, injectable
services, pipes, directives, inputs, outputs and injections in constructor arguments. They
were implemented long before TC39 decided on a decorator’s standard, and now there
is a mismatch between the old TypeScript and the new ECMAScript decorator. Such
mismatch can cause compatibility and integration issues for developers.

Solution:
TypeScript 5.0 supports the new official ECMAScript decorators. This results in better
typing, like the ClassMethodDecoratorContext type which impacts meta information
like method name, access modifier and the extra function addInitializer

This is an example of a fully typed ECMAScript decorator in Typescript 5.0:

function loggedMethod<This, Args extends any[], Return>(


target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<
This,
(this: This, ...args: Args) => Return
>
){
const methodName = String(context.name);
function replacementMethod(this: This, ...args: Args): Return {
console.log(`LOG: Entering method ‘${methodName}’.`);
const result = target.call(this, ...args);
console.log(`LOG: Exiting method ‘${methodName}’.`);
return result;
}
return replacementMethod;
}

There is one important difference between the old and new implementation - decorators
used in the constructor parameters will not work, as the standard doesn’t support the
following code:

Angular v16 | The Ultimate Guide to Angular Evolution 68


constructor(@Optional() public myService: MyService) {}

So we need to use the inject function instead:

myService = inject(MyService, { optional: true });

Benefits:
We’re up to date with the official ECMAScript standard with improved typings. We no
longer need to rely on the TypeScript custom implementation without its JavaScript
counterpart. Instead, we can use a solution that will be directly supported by all modern
browsers.

Extending multiple TS configuration files

Dev Experience

Challenge:
Typescript supported custom experimental decorators for years. They’re familiar to every
It is uncomfortable to compose TypeScript configuration files with a single file extension,
especially in a mono-repository where multiple applications or libraries are involved.
This process requires us to create a chains of extensions as follows:

Angular v16 | The Ultimate Guide to Angular Evolution 69


// tsconfig1.json
{
“compilerOptions”: {
“strictNullChecks”: true
}
}
// tsconfig2.json
{
“extends”: “./tsconfig1.json”,
“compilerOptions”: {
“noImplicitAny”: true
}
}
// tsconfig.json
{
“extends”:”./tsconfig2.json”,
“files”: [“./index.ts”]
}

Solution:
Typescript 5.0 allows us to extend multiple tsconfig files.

// tsconfig1.json
{
“compilerOptions”: {
“strictNullChecks”: true
}
}
// tsconfig2.json
{
“compilerOptions”: {
“noImplicitAny”: true
}
}
// tsconfig.json
{
"extends": ["./tsconfig1.json", "./tsconfig2.json"],
"files": ["./index.ts"]
}

Angular v16 | The Ultimate Guide to Angular Evolution 70


Benefits:
Extending multiple TS configuration files can simplify the configuration of your Angular
workspace by streamlining the management of compiler options across multiple
projects.

All enums as Union enums (available in 5.0)

Dev Experience

Challenge:
Before Typescript 5.0, the language required all constant enum members to be string/
numeric literals, like 1,2, 300, “foo” or “bar,” for an enum to make it a union enum. In such
a case, union enum members become types as well. That means that certain members
can only have the value of an enum member. It also means that enum types themselves
effectively become a union of each enum member.

Solution:
In TypeScript 5.0, all enums, even the ones with computed members, are converted into
union enums. All members of all enums can be referenced as types and all enums can
be successfully narrowed.
The following example with computed enum members is correctly interpreted
in TypeScript 5.0 and above:

const prefix = 'data';

const enum Routes {


Parts = `${prefix}/parts`,
Invoices = `${prefix}/invoices`,
}

Benefits:
This feature brings more flexibility when defining types and more intelligent type
inference by the TypeScript transpiler.

Angular v16 | The Ultimate Guide to Angular Evolution 71


Unrelated Types for Getters and Setters

Dev Experience

Challenge:
In a getter/setter pair, the get type had to be a subtype of the set type, which potentially
limited the ability to return more specific or transformed data types through getters.

Solution:
Since the release of TypeScript version 5.1, we can specify two completely unrelated types
for a getter/setter pair.

interface CSSStyleRule {
// ...
/** Always reads as a `CSSStyleDeclaration` */
get style(): CSSStyleDeclaration;
/** Can only write a `string` here. */
set style(newValue: string);
// ...
}

Benefits:
The developer gains more flexibility by being able to cover edge cases that require
different types.

Angular v16 | The Ultimate Guide to Angular Evolution 72


Esbuild

Signals

New Control Flow

Deferred Loading

Angular v17
release date: 11.2023
Angular 17 marks a pivotal update in the framework’s evolution, introducing streamlined
features and setting a robust foundation for future advancements in signals and
template syntax. In this version, Angular also continues its journey towards enhancing
performance and improving SSR.

Signals library (stable)

Performance Dev Experience Efficiency

Note:
Signals are introduced and described in the “Angular 16” chapter. Here we will focus on
the changes that have occurred since then.

Challenge:
The Angular signals library, in its developer preview phase, encountered some
modifications and adjustments due to feedback from the Angular community. Various
Angular developers raised doubts related to mutability and the change notification
system in the initial proposal.

Solution:
In Angular 17, signals became stable, so we could confidently use them in commercial
applications. At the end of the developer preview, we received two significant changes.

The first change was the removal of the ‘mutate’ function that was previously used to
mutate the value stored by the signal. The mutate function skipped comparing values
because its purpose was to modify the value of the signal by design. Now, it is
recommended to use only the update function.

The second change had to do with the implementation of the default function for
comparing signal values.

The defaultEquals function implementation in v16 considered any two objects to be


different. so that even if we returned references to the same object, all dependent
signals were notified of the change.

Angular v17 | The Ultimate Guide to Angular Evolution 74


// Angular 16 version
export function defaultEquals<T>(a: T, b: T) {
return (a === null || typeof a !== 'object') && Object.is(a, b);
}
// Angular 17 version
export function defaultEquals<T>(a: T, b: T) {
return Object.is(a, b);
}

New implementation of the defaultEquals function relies solely on Object.is(). As a result,


if an object is mutated by using the update() function without changing the reference,
other dependent signals will not be notified of the change. To get a signal to notify you
about the change, create a new reference of the object with the updated properties
(e.g., by using the spread operator) or provide your own implementation of the equals
function and then specify it in the signal options.

The upgrade of the API to the stable version does not include the "effect" function,
as there is still ongoing discussion about its semantics and potential improvements.

A significant change is that if all components in the application use the OnPush change
detection strategy and a new signal value triggers Change Detection in a specific
component template, only this component is marked as dirty and its ancestors are
notified about the change. This “local” change detection can majorly affect application
performance.

It is important to note that Angular 17 still does not include signal-based component
implementation, even in the developer preview mode. When this implementation arrives,
it is supposed to revolutionize the change detection system, because components will
not use zoneJS and DOM recalculation will only happen after the values related to the
signal view are changed.

Benefits:
The stabilization of the Angular signals library in Angular 17 lays solid groundwork
for future advancements, particularly in moving away from zone.js and towards
signal-based components. This shift is a step towards change detection system
transformation, which promises enhanced performance and efficiency in application
development.

Angular v17 | The Ultimate Guide to Angular Evolution 75


Expert Opinion:

At this juncture, I hold no particularly strong opinion on the signals library. My perpetual
quest revolves around enhancing both user experience (UX) and developer experience
(DX). Nonetheless, I avoid excessive “hype” concerning the introduction of signals at the
framework's core, opting instead for a rational assessment of the potential opportunities
signals may herald in the future. A substantial benefit would arise if we could seamlessly
transition away from zone.js, aligning with our overarching objective of establishing
a viable route toward creating fully zoneless applications (as one of the goals).

~ Artur Androsovych
Angular Expert

Signals are becoming a mainstream technique, being introduced in the most popular
frontend frameworks, the reason being that it is the perfect technique for fine-grained
synchronous reactivity. For a long time, we used to handle all states in an asynchronous
reactive approach using RxJS, whereas state management itself is a synchronous task,
such that RxJS is not the right tool by default. This overcomplicated a few use cases
and is the reason for unexpected glitching. This is a lot simpler with signals. Additionally,
this fine-grained reactive primitive can, and will, be used for fine-grained and zoneless
change detection in the future, when signal-based components land in Angular. This
is no small change, and will rather change the way we develop reactive Angular
applications completely.

~ Stefan Haas
Nx Champion

Signal inputs

Performance Dev Experience Efficiency

Challenge:
The current implementation of component inputs is not very reactive. In order to listen
for changes, we use component lifecycle hooks. That means that if we want the input
values to interact with the Signal API, we need to handle the logic manually.

Solution:
Angular version 17.1 enables signal inputs for components. This means we no longer use
the decorator. Instead, a dedicated function defines the input:

Angular v17 | The Ultimate Guide to Angular Evolution 76


@Component({
selector: 'my-cmp’,
standalone: true,
template: `{{ name() }}`,
})
export class MyComponent {
name = input<string>();
}

This mechanism allows you to simplify the reactivity of the component, as it allows you
to define computed signals and side effects for input value changes. There is no longer
a need to use ngOnChanges lifecycle hook.

export class MyComponent {


name = input<string>();
helloMessage = computed(() => ‘Hello ’ + name());

constructor() {
effect(() => {
console.log(‘logging: ’ + this.name());
})
}
}

The Signal input API has extra capabilities compared to its standard counterpart, such as
providing default values, marking input as required, setting aliases, and various transform
functions.

Angular v17 | The Ultimate Guide to Angular Evolution 77


export class MyComponent {
// required input
name = input.required<string>();

// input with default value


color = input(‘red’);

// input with alias


disabled = input<boolean>(false, { alias: ‘isDisabled’ });

// input with transform function


age = input.required({ transform: numberAttribute });
}

Benefits:
Signal inputs mark a significant step towards the deeper integration of signals into the
ecosystem and transforms the way we deal with component input changes.

This feature significantly enhances the developer experience and code clarity by
streamlining the management of component inputs and facilitating more intuitive
coding practices.

New control flow (Developer preview)

Performance Dev Experience Efficiency

Challenge:
The standard control flow (if/else, switch/case, loop in component templates), based
on structural directives, has a few disadvantages. For example, the boilerplate, which
is large compared to other frontend frameworks, is necessary even for a single if-else
statement.

<div *ngIf="condition as value; else elseBlock">{{value}}</div>


<ng-template #elseBlock>Content to render when value is null.</ng-template>

Angular v17 | The Ultimate Guide to Angular Evolution 78


The second issue with the standard control flow is the current directives implementation,
which is strongly based on the current change detection system. Support for both
zone-based and signal-based approaches in directives would greatly complicate the
code with no possibility of tree shaking it.

Solution:
The introduction of a new control flow to templates is one of the biggest changes
in Angular v17. It is the first step to moving away from built-in structured directives,
whose current design would not work in zoneless signal-based applications.

The new syntax is based on a structure called a block. Its appearance significantly
differs from what we have seen in templates. We start each block with an "@" prefix
and then use a syntax very similar to the one we know from JavaScript.

The change affects the three most commonly used structured directives: ngIf,
ngSwitch and ngFor. At this point, there is no planned implementation of custom
blocks.

An if/else statement:

@if (time < 12) {


Good morning!
} @else if (time < 17) {
Good afternoon!
} @else {
Good evening!
}

A switch/case statement:

@switch (fruit) {
@case 'apple' {
<apple-cmp />
}
@case 'banana' {
<banana-cmp />
}
@default {
<unknown-fruit-cmp />
}
}

Angular v17 | The Ultimate Guide to Angular Evolution 79


A loop statement:

<ul>
@for (item of items; track item.id) {
<li>{{ item.name }}</li>
} @empty {
<li>No items...</li>
}
</ul>

The loop has received the most valuable improvements out of all the structures:

• The @empty block is available, which allows us to display the content when the
list we iterate over is empty.
• It is no longer necessary to create a special trackBy function to pass it as an
argument. Instead, we only need to specify the unique key of the object to be tracked.
• The @for block requires the use of the track function, which significantly
optimizes the process of rendering the list and managing changes without the need
for developer interference.

Since we have two ways of using the control flow, you may ask : what will happen to the
directives we are familiar with? In version 17, they remain unchanged. But with the arrival
of future versions and the exit of the new control flow from the developer preview, they
will go into a deprecated state.

However, there is no need to worry about refactoring. The Angular Team has created
a scheme that automatically migrates the control flow to the new syntax. In most cases,
the scheme should handle the transition to the new syntax without a programmer’s
interference. To switch to the new syntax, all you need to do is execute the following
command:

ng generate @angular/core:control-flow

Benefits:
The new control flow in Angular offers a more succinct and readable syntax,
significantly reducing boilerplate in templates. It ensures smoother integration with
future signal-based change detection systems, enhancing application performance.
Additionally, it lays the groundwork for advanced features such as deferred loading,
contributing to optimized load times and a better overall user experience.

Angular v17 | The Ultimate Guide to Angular Evolution 80


According to public benchmarks, operations on loops using the new built-in control flow
are up to 90% faster.

Expert Opinion:

The new control flow is fabulous. The syntax is quite similar to the JavaScript syntax, and
the readability has increased. This new syntax is what we are waiting for. It’s also more
similar to JSX (not equal), but this can reduce the gap with React, and that can help
React developers jump into the Angular environment. With this new approach, we can
focus on the view’s logic and not on what Angular needs to show our logic correctly;
that’s a fantastic improvement for the developers and reduces the entry-level in the
framework.
~ Luca Del Puppo
Google Developer Expert

The built-in new control flow brings several key improvements:


• No need to import NgIf, NgFor, and more in standalone components.
• Automatic enclosure of code blocks with {}. So no need for <ng-container>.
• Ergonomic and type-checking enhancements
• Improved repeater performance
• A step toward zoneless apps
These changes simplify development, reduce code verbosity, and enhance performance,
making Angular even more developer-friendly.

~ Fatima Amzil
Google Developer Expert

Deferred loading (developer preview)

Performance Dev Experience UX

Challenge:
A lack of lazy loading in a web app can lead to slower page load times and increased
bandwidth usage, as all content is loaded at once regardless of its immediate
necessity. As long as Angular has lazy-loading modules in routing, there is no simple
and convenient way to lazy load individual components, even though it’s achievable
with dynamic import() and ngComponentOutlet.

Solution:
Angular version 17 introduces the new lazy-loading primitive called "defer," based on the
syntax introduced in the new control flow. This extremely useful mechanism allows
a controlled delay in the loading of a page’s selected elements. That is particularly

Angular v17 | The Ultimate Guide to Angular Evolution 81


important because by using "defer," we can significantly reduce the initial bundle size,
which improves the application's loading speed, especially for users with a slower
internet connection.

To control when to defer a block’s content, use predefined conditions: when and on. You
can use them individually or in any combination, depending on when you want to load
the content.

“When”: defines a logical condition that will load the block’s content when it receives
a true value.

@defer (when condition) {


<deferred-cmp />
}

An important note is that once the block’s content has been asynchronously loaded,
there is no way to undo that loading. If we want to hide the block’s contents, we need to
combine the @defer block with the @if block.

“On”: allows us to use predefined triggers which initiate loading.

These predefined triggers are:


• Idle - This is the default trigger. The element will be loaded when the browser
enters the idle state. The requestIdleCallback method is used to determine when the
content will be loaded.
• Interaction - Content will be loaded upon user interaction, click, focus, touch,
and upon input events, keydown, blur, etc.
• Immediate - Loading will occur immediately after the page has been rendered.
• Timer(x) - Content will be loaded after X amount of time.
• Hover - Content will be loaded when the user hovers the mouse over the
area covered by the functionality, which could be the placeholder content or a
passed-in-element reference.
• Viewport - Content will be loaded when the indicated element appears in the
user's view. The Intersection Observer API is used to detect an element’s visibility.

@defer (on interaction) {


<deferred-cmp />
}

Angular v17 | The Ultimate Guide to Angular Evolution 82


We also have the ability to combine conditions and triggers:

@defer (when cond; on interaction, timer(5s)) {


<deferred-cmp />
}

It is worth noting that loading content, as in the case of when, is a one-time operation.

“Prefetch”: There may be situations where we want to separate the process of fetching
content from rendering it on the page. In such a case, we need the prefetch condition.
It allows us to specify the moment, using the previously mentioned triggers, when the
necessary dependencies will be downloaded. As a result, interaction with this content
becomes much faster, resulting in a better UX.

@defer (on interaction; prefetch on idle) {


<deferred-cmp />
}

We also have three very useful, optional blocks we can use inside the @defer block:

@placeholder - used to specify the content visible by default until the asynchronously
loaded content is activated. Example:

@defer (when condition) {


<deferred-cmp />
}
@placeholder (minimum 2s) {
<span>There will be deferred content.</span>
}

The "minimum" condition allows you to specify the minimum time after which
the delayed content can be loaded. In the example used above, this means that
even if the condition is met immediately, the content will be swapped after 2 seconds.

@loading - the content of this block is displayed when the dependencies are being
loaded. Example:

Angular v17 | The Ultimate Guide to Angular Evolution 83


@defer {
<deferred-cmp />
}
@loading (after 100ms; minimum 1s) {
<span>Content is loading...</span>
}

Within this block, we can also use the minimum condition, which works the same way as
within the @placeholder. It indicates the minimum time for which the block’s content will
be visible. We can also use the “after” condition, which indicates the amount of time it
will take for the block’s content to appear. If it takes less than 100ms to load, the loader
will not appear. Instead, `<deferred-cmp />` will replace it immediately.

@error - represents the content rendered when the deferred loading failed for some
reason. Example:

@defer (timeout 1s) {


<deferred-cmp />
}
@error {
<p>Failed to load the deferred component</p>
<p>Error: {{ $error.message }}</p>
}

When using the defer block together with the @error block, it is possible to use
a special timeout condition. This condition allows you to set a maximum loading time. If
dependencies take longer than the specified time, the contents of the @error block will
be displayed. Inside this block, the user has access to the $error variable, which contains
information about the error that occurred during the loading process.
Benefits:
Deferred loading in Angular enhances performance by reducing initial bundle
size, speeding up load times, and enabling controlled, asynchronous loading of
individual components based on user interaction or other predefined conditions.
This leads to a more efficient and user-friendly experience.

Angular v17 | The Ultimate Guide to Angular Evolution 84


Expert Opinion:

Deferred loading opens a new way to reduce the bundle size of every component and
introduces a new way to load only essential code needed for the current view. This
feature will probably be one of the leading solutions to reduce the page size in a Server
Side Render application; it helps to render only the needed parts and skip the dynamic
content that isn't required on the page start-up or for the SEO.

~ Luca Del Puppo


Google Developer Expert

As for the deferred loading feature, it's truly incredible! With this feature, we can
lazily-load content within an Angular template. It's much more than just simple
lazy loading; we can load chunks based on conditions and triggers and even prefetch
in advance. This is bound to revolutionize the way we construct our Angular templates.

~ Fatima Amzil
Google Developer Expert

Inputs Binding with NgComponentOutlet

Dev Experience Efficiency

Challenge:
When we used the NgComponentOutlet structural directive to create dynamic
components, there was no convenient way to pass the data to them. The usual solution
was to create a new injector and transfer data by a custom injection token.

Solution:
In Angular 16.2, we received a possibility to bind data to inputs of a component created
with NgComponentOutlet.

Angular v17 | The Ultimate Guide to Angular Evolution 85


Component({
selector: 'my-component',
imports: [NgComponentOutlet],
template: `
<ng-template
*ngComponentOutlet="dynamicComponent; inputs: dynamicComponentIn-
puts;" />
`
standalone: true,
})
class FooComponent {
readonly dynamicComponent = FooComponent;
readonly dynamicComponentInputs = { foo: 'Bar' }
}

In the child component from the example above, we have to create @Input with a ‘foo’
name, or alias and the data will be bound from the parent.
Benefits:
The new function simplifies the data passing process, making it more declarative. This
improves code readability and developer experience.

Animation lazy loading

Performance UX

Challenge:
Animations are always downloaded while the application is bootstrapped, even though
in most cases, animations occur while the user is interacting with an element on the
page. This results in increased bundle size.

Solution:
Animation lazy loading is a new functionality in version 17 that solves this problem by
introducing the ability to asynchronously load code that is associated with animations.

To start using lazy loading of animations, we need to add provideAnimationsAsync()


instead of provideAnimations() to the providers of our application:

Angular v17 | The Ultimate Guide to Angular Evolution 86


import { provideAnimationsAsync } from '@angular/platform-browser/anima-
tions/async';
bootstrapApplication(AppComponent, {
providers: [provideAnimationsAsync(), provideRouter(routes)],
});

And that's it! Now we just need to make sure that all functions from the @angular/
animations module are only imported into dynamically loaded components.

Controlling imports in our application is easy, but the process can become problematic
when we do it with libraries. An example of such a library is @angular/material, which
relies heavily on @angular/animations, making it very likely that the module
responsible for animations will be pulled into the initial bundle.

If we want to check if the animations were downloaded asynchronously, we can build


our application with the --named-chunks flag. Then we should see @angular/animations
and @angular/animations/browser in separate bundles in the Lazy Chunk Files section.

build with asynchronously loaded animations

Initial Chunk Files Names Raw Size


chunk-IETGG730.js - 98.56 kB
main-S3BE5YUD.js main 77.80 kB
polyfills-W2VU2Y2Q.js polyfills 33.23 kB
styles-YD2MB7C6.css styles 43 bytes

Initial Total 209.63 kB

Lazy Chunk Files Names Raw Size


chunk-FBVTU5M2.js browser 62.20 kB
chunk-D75ILFX5.js - 3.53 kB
chunk-FW06XWLR.js animated-component 1.25 kB

build with standard loaded animations

Angular v17 | The Ultimate Guide to Angular Evolution 87


Initial Chunk Files Names Raw Size
main-C6GNJMLR.js main 133.40 kB
chunk-5AHZPM5V.js - 102.12 kB
polyfills-W2VU2Y2Q.js polyfills 33.23 kB
styles-YD2MB7C6.css styles 43 bytes

Initial Total 268.79 kB

Lazy Chunk Files Names Raw Size


chunk-0CFIALD3.js animated-component 1.23 kB

Benefits:
Lazy loading of any kind of resources enhances user experience by speeding up initial
page load times, saving bandwidth, prioritizing critical resources, and potentially
improving website performance and SEO.

View Transitions

UX

Challenge:
There is no convenient way to implement smooth animated transitions between pages
in Angular.

Solution:
Support for the View Transition API has been introduced. This is a relatively new
mechanism that allows us to create smooth and interactive transition effects between
different views of a web page. Thanks to this API, we can make changes to the DOM tree
while an animation is running between two states.

Adding the View Transition API to our project is very simple.

First, we need to import the ‘withViewTransitions’ function at the bootstrap point of our
application.

Angular v17 | The Ultimate Guide to Angular Evolution 88


import { provideRouter, withViewTransitions } from '@angular/router';
bootstrapApplication(AppComponent, {
providers: [provideRouter(routes, withViewTransitions())],
});

Now, the application immediately gains a subtle input and output effect when
changing the URL. Of course, we have the ability to create custom animations. In the
example below, the transition effect was extended to 2 seconds by using prepared
pseudo-elements in the `styles.css` file.

/* Screenshot of the view of the page we are leaving */


::view-transition-old(root),
/* Representation of the new page view */
::view-transition-new(root) {
animation-duration: 2s;
}

This example is a very small sample of what we can achieve with this API. If you are
interested in the practical use of this mechanism, We encourage you to read the
material available in the Chrome browser documentation.

However, it is important to remember that this is a relatively new and experimental


feature. This means that it may not be fully supported in some browsers. You can check
the level of support for individual browsers on caniuse.

How does this feature work internally? When navigation starts, the API takes a screenshot
of the current page and puts it in ::view-transition-old pseudoelement. It also renders
a new page, puts it in view-transition-new and plays the animation between them.

Benefits:
The View Transitions API simplifies the creation of smooth and cohesive navigation
animations, enhancing user experience by providing a seamless visual transition
between different states or views of a web application.

Angular v17 | The Ultimate Guide to Angular Evolution 89


Esbuild + Vite (stable)

Dev Experience Performance

Challenge:
Until now, Angular's build system primarily relied on Webpack for compiling applications,
requiring separate builders for different purposes like production builds, development
server, server-side rendering, and prerendering. This led to more complex configurations
in angular.json, slower build processes due to repetition of common steps, and less
efficient module loading as it did not fully utilize ECMAScript Modules (ESM).

Solution:
In Angular v17, we received the new 'application' builder, which streamlines the build
process by using @angular-devkit/build-angular:browser-esbuild as its core. This
builder simplifies configurations by consolidating various tasks such as production
builds, server-side rendering (SSR), and prerendering into a single, more efficient
process. It enhances performance by executing common build steps only once and
fully supports ECMAScript Modules (ESM), leading to faster build times and more
efficient, modern module loading.

To apply these changes to an existing Angular project, change the builder from:

@angular-devkit/build-angular:browser

to:

@angular-devkit/build-angular:application

If your application supports SRR capabilities, you might need a few extra configuration
changes (in such case check the official documentation for details).

The new application builder provides the functionality for all of the preexisting builders:
• browser
• prerender
• server
• ssr-dev-server

The usage of Vite build pipeline is encapsulated within the ‘dev-server’ builder, with no

Angular v17 | The Ultimate Guide to Angular Evolution 90


changes necessary to use new build system.

Benefits:
Thanks to the unified solution, faster build-time performance is also brought to SSR apps.
Using the same builder for both types of applications reduces the risk of potential
differences resulting from using different bundlers. For new build systems with SSR & SSG,
‘ng build’ gets its speed increased by up to 87%, and ‘ng serve’ gets an 80% increase
in speed.

SSR Hydration (stable)

Performance UX

Challenge:
Full Hydration presented in Angular version 16 was in developer preview mode, which
could have stopped us from using it in applications since it was not yet stable.

Solution:
From version 17, the solution is marked as stable, which means it is considered safe
for use in a production environment.

To enable it, use the following utility function in your project:

import { bootstrapApplication, provideClientHydration, } from '@angular/plat-


form-browser';

...

bootstrapApplication(RootCmp, {
providers: [provideClientHydration()]
});

Benefits:
We can safely enjoy the benefits of hydration such as better UX (faster loading and
interactivity, no more flickering) and enhanced SEO in a production environment.

Angular v17 | The Ultimate Guide to Angular Evolution 91


CLI improvements

Dev Experience Efficiency

Challenge:
The CLI does not take several new features from recent releases into account and uses
older solutions by default.

Solution:
In Angular 17, applications generated by ‘ng new’ will be standalone by default and will
use Vite as the default build system. All relevant code generators (i.e. for components,
directives, pipes) will produce standalone output as well.

You can use the following migration to convert existing projects to the standalone APIs:

ng generate @angular/core:standalone

‘ng-new’ also supports the ‘--ssr’ flag or alternatively includes a prompt asking if we
wan’t to enable SSR or SSG by default, including hydration.

Do you want to enable Server-Side Rendering (SSR) and Static Site Generation
(SSG/Prerendering)? (y/N) y

To try out the new built-in control flow syntax in your existing project, you can use the
following migration:

ng generate @angular/core:control-flow

Benefits:
New features in the framework are becoming more accessible, and we get ready-made
configurations out-of-the-box right from the start, which speeds up the project launch.

Angular v17 | The Ultimate Guide to Angular Evolution 92


Devtools Dependency Graph

Dev Experience

Challenge:
Debugging dependency injection (DI) has always been problematic. If there were errors,
they were always caused by the inability to inject a specific token, and if a token was
successfully injected, there was no information about what injection context it came
from.

Solution:
The Angular Team implemented brand new debugging APIs related to dependency
injection. On top of that, they extended the capabilities of Angular Devtools and made it
possible to inspect DI in the runtime. It is possible to verify a component’s dependencies
in a component inspector, injection tree with a dependency resolution path and
providers declared in individual injectors.

Environment Hierarchy

Platform Root Standalo

Element Hierarchy

AppComponent LayoutComponent

Benefits:
The new debugging APIs for dependency injection in Angular helps developers to build
more reliable and efficient applications.

Angular v17 | The Ultimate Guide to Angular Evolution 93


Rebranding and introduction of angular.dev

Dev Experience

Challenge:
The branding of Angular has been almost identical since the beginning of AngularJS,
which was released over 10 years ago. The framework documentation is also somewhat
outdated, often based on ngModules.

Solution:
With the premiere of Angular version 17, we also received a new graphic identification of
the framework and a new angular.dev - a home for Angular’s new documentation.

Old logo: New logo:

Angular.dev is a revamp with cutting-edge, interactive documentation. All examples


have been reimagined with the standalone API and the latest Angular magic. Plus, we've
got guides, tutorials and shiny Angular Playgrounds to code right in our browsers.

Benefits:
The new Angular.dev documentation makes it easier for developers to learn and use
Angular.

Angular v17 | The Ultimate Guide to Angular Evolution 94


Typescript/Node.js support

Angular v17 requires Typescript 5.2+ and Node.js v18+. Since 17.1, Angular also supports
Typescript 5.3. New versions bring a handful of interesting changes that may be useful in
Angular projects.

Explicit Resource Management (available in 5.2)

Dev Experience

Challenge:
There is no convenient way to explicitly manage resources with a specific lifetime.
For example, run some cleanup code when a given object reaches the end of its
life.

Solution:
The system for managing objects with a specific lifetime will be part of the ECMAScript
standard. For Typescript, it was already delivered in version 5.2. This system is inspired
by solutions from other programming languages, for example, the “using” syntax from
C# or the “try-with-resource” syntax from Java. It allows a programmer to manage the
resource declaratively, meaning an object is disposed when execution leaves the scope
where object has been defined, or imperatively by calling a Symbol.dispose method
manually.

You can create a manageable resource by implementing a new Disposable interface


in your class or by creating a function that returns an object implementing Disposable
interface. Usually we use such a feature when we need some sort of “clean-up” after
creating an object. Common cases include deleting temporary files and closing internet
connections.

In the example below, we define a manageable object inside the function scope. When
execution leaves the function context, the Symbol.dispose method is automatically
called. This happens even if there was an exception during processing. We no longer
have to wrap logic into the try/catch/finally block to make sure that clean-up logic is
executed in all cases.

Angular v17 | The Ultimate Guide to Angular Evolution 95


class Foo implements Disposable {
[Symbol.dispose]() {
// “clean-up” code goes here
}
}

function doSomething() {
using foo = new Foo();

// do something
if(someCondition()) {
// do another thing
return;
}
}

This feature also contains support for async disposable resources, Symbol.asyncDispose,
and extra methods for dealing with stacks like DisposableStack and AsyncDisposableStack.

Note that this feature is only available if you set the compiler target to es2022 and added
“esnext” or “esnext.disposable” to the “lib” array.

{
"compilerOptions": {
"target": "es2022",
"lib": ["es2022", "esnext.disposable", "dom"]
}
}

Benefits:
Explicit Resource Management enhances code cleanliness and safety by providing
a straightforward and efficient way to manage the lifecycle of resources. This ensures
that clean-up or disposal code is automatically executed when the object goes out of
scope or encounters an exception, without the need for extensive try/catch/finally blocks.

Angular v17 | The Ultimate Guide to Angular Evolution 96


Named and Anonymous Tuple Elements (available in 5.2)

Dev Experience

Challenge:
There is no convenient way to explicitly manage resources with a specific
lifetime. For example, run some cleanup code when a given object reaches
the end of its life.

Solution:
Typescript gained support for optional names or labels for each element of the tuple.
The all-or-nothing restriction has been removed and labels go as far as preserving
the merging with other tuples.

// fully labeled tuple


type Coordinates = [latitude: number, longitude: number];

//partially labeled tuple


type RGB = [number, green: number, number];

// all labels are still preserved


type Combined = [...Coordinates, ...RGB];

Benefits:
The improvement enables greater flexibility and readability in TypeScript code.

Copying Array Methods (available in 5.2)

Dev Experience

Challenge:
Old and well-known array methods like sort, splice or reverse, all update an array
in-place, meaning they mutate the original array. This can lead to unintended side
effects and make code harder to understand and debug, especially in complex
applications with shared state.

Angular v17 | The Ultimate Guide to Angular Evolution 97


Solution:
TypeScript 5.2 adds definitions of ECMAScript array methods for modifications by
copy. All methods that mutate the original array have their non-mutating variants
available and ready to use.

• array.reverse() => array.toReversed()


• array.sort(...) => array.toSorted(...)
• array.splice(...) => array.toSpliced(...)
• array[i] = foo => array.with(i, foo)

Benefits:
Using non-mutable array methods over methods that will mutate arrays in place
in the JavaScript language have multiple benefits. These include enhancing
code predictability, maintainability, and readability by avoiding unintended side
effects and ensuring consistent data state across different parts of an application.

Angular v17 | The Ultimate Guide to Angular Evolution 98


Angular v1
release date:
Angular 18 introduces significant advancements that enhance both performance and
developer experience. This version replaces Zone.js with Hybrid Change Detection,
which improves application speed and simplifies change tracking using signals. It also
brings Signal Inputs and Model Inputs for more reactive components, along with Signal
Queries for improved DOM handling. The new Output Syntax aligns with the signal-based
approach, and function-based route redirects offer dynamic navigation options.
Additionally, the introduction of ng-content fallback simplifies component development
by allowing default content when no projection is provided.

Hybrid Change Detection

Performance Dev Experience

Challenge:
Angular and Zone.js have been tightly connected from the beginning. While Zone.js
helps Angular track asynchronous operations and automatically manage change
detection, it can also slow down the application.Developers are often faced with the
problem of the extra load that Zone.js adds and the difficulty of manually managing
the detection of changes. Main Functions of Zone.js in Angular include:

• Tracking Asynchronous Context: Zone.js creates "zones" that track asynchro-


nous actions like setTimeout, Promise, DOM events, and HTTP requests. This helps
Angular to know when these actions have been done and to react properly.
• Decorating Asynchronous APIs: Zone.js intercepts and wraps standard browser
APIs responsible for asynchronous tasks (setTimeout, setInterval, addEventLis-
tener, Promises). This allows Angular to monitor when these tasks start and finish.
• Change Detection: After an asynchronous task ends, Zone.js tells Angular that
something might have changed in the application data. Angular then runs its
change detection to update the view if there are any changes.

These functions are key to Angular's ability to manage asynchronous actions and
update the view automatically. However, Zone.js adds performance overhead because
it has to track and wrap all these asynchronous tasks. In large apps with many asyn-
chronous calls, this can slow down performance. Techniques like running code outside
the zone can help, but they are tricky and can negatively affect the developer experience.

Angular v19 | The Ultimate Guide to Angular Evolution 100


Solution:
Hybrid Change Detection is an experimental solution introduced in version 18 of Angular.
It allows us to get rid of the zone.js completely and track our changes using a new change
81 detection mechanism. It’s worth noting that this change is strongly related to the
signals which simplify the mental model of detecting the changes. We can enable the new
change detection using the dedicated function
provideExperimentalZonelessChangeDetection.
After adding the provider, remove zone.js from your polyfills in angular.json.

bootstrapApplication(RootCmp, {
providers: [provideExperimentalZonelessChangeDetection()]
});

The new change detection mechanism should only respond to data changes that are
extremely easy to detect from the signals. The new change detection cycle will be
scheduled when:

• A Signal updated
• Calls to changeDetectorRef.markForCheck()
• A subscribed Observable with the AsyncPipe receives a new value
• A component gets attached/detached
• Setting an input

Benefits:
By eliminating Zone.js, the application experiences less overhead, resulting in faster per-
formance, especially in applications with many asynchronous operations. We should
expect the code to become simpler.

Some combinations related to running code outside of the zone or just manually trig-
gering change detection should be rare cases which in effect will enhance developer
experience. Excluding the scenario of manually invoking change detection, it will actually
react to data changes, since data is what drives our applications, we will avoid unneces-
sary checks, improving overall performance and application responsiveness.

Additionally it should reduce application bundle size, improve core web vitals, speed
uppage loading time and simplify stack trace and debugging.

Angular v19 | The Ultimate Guide to Angular Evolution 101


Expert Opinion:

Zone.js affects Angular applications in terms of loading and runtime performance.


The library size and the monkey patching of browser events that might trigger change
detection more often than necessary can degrade Core Web Vitals metrics. Zoneless
Change Detection will remove the dependency and improve the runtime performance
of most existing Angular applications.

~ Aristeidis Bampakos
Google Developer Expert

If your app is already optimized for performance, zoneless will improve the performance
even more, but if the performance is already good enough for you, it would be a nice-
to-have. If you’re using OnPush Change Detection, it means that your app is already
zoneless compatible, so you can enable it without having any issues.

~ Enea Jahollari
Google Developer Expert

Removing Zone JS, it's one of the biggest changes for the Angular team in the end.
I think it's one of the ideas from one of the first releases of Angular. Now, after the release
of Ivy, I saw a lot of improvement in the framework at the end. With Ivy under the hood,
the framework is starting to be more community-driven, first of all, because Angular
team is able to release a lot of elements that are requested by the community. They're
also faster in their release process.

~ Luca Del Puppo


Google Developer Expert

Angular v19 | The Ultimate Guide to Angular Evolution 102


Signal inputs

Performance Efficiency Dev Experience

Challenge:
The current implementation of component inputs is not very reactive. In order to look for
the changes, we use component lifecycle hooks. That means that if we want the input
values to interact with the Signal API, we have to handle the logic manually.

Solution:
Angular version 17.1 enables signal inputs for components. This means we no longer use
the decorator. Instead, a dedicated function defines the input:

@Component({
selector: 'my-cmp’,
standalone: true,
template: `{{ name() }}`,
})
export class MyComponent {
name = input<string>();
}

This mechanism allows you to simplify the responsiveness of the component by letting
you define computed signals and side effects for input value changes. It is no longer
necessary to use the ngOnChanges lifecycle hook.

export class MyComponent {


name = input<string>();
helloMessage = computed(() => ‘Hello’ + name());

constructor() {
effect(() => {
console.log(‘logging: ’ + this.name());
})
}
}

Angular v19 | The Ultimate Guide to Angular Evolution 103


The Signal input API has extra capabilities compared to its standard counterpart, such as
providing default values, marking inputs as required, setting aliases, and various trans-
formingfunctions.

export class MyComponent {


// required input
name = input.required<string>();

// input with default value


color = input(‘red’);

// input with alias


disabled = input<boolean>(false, { alias: ‘isDisabled’ });

// input with transform function


age = input.required({ transform: numberAttribute });
}

Benefits:
Signal inputs mark a significant step towards the deeper integration of signals into the
ecosystem and transform the way we deal with component input changes. This feature
significantly enhances the developer experience and code clarity by streamlining the
management of component inputs and facilitating more intuitive coding practices.

Expert Opinion:

Don’t sleep on signals! While there may be some rough edges to migrating if you're
using RxJS for everything, signal will give you an even better developer experience and
performance!

~ Enea Jahollari
Google Developer Expert

Angular v19 | The Ultimate Guide to Angular Evolution 104


Model inputs

Performance Efficiency Dev Experience

Challenge:
Standard inputs in Angular are read-only and do not allow components to send new
values back to the parent components. This limitation restricts the creation of interactive
components that need to update the parent component's state based on user actions.

Solution:
Angular introduces model inputs, a special type of input that allows components to
propagate new values back to the parent component. Currently in developer preview,
this feature facilitates two-way binding, allowing for more dynamic interactions.

Model inputs are defined in much the same way as standard inputs, but with added
capabilities for two-way data binding. Here's how to define and use model inputs in
Angular:

1. Defining Model Inputs:


Model inputs can be created using the model() function in Angular. Unlike
standard inputs, model inputs can update their values and propagate these
changes back to the parent component.

import { Component, model, input } from '@angular/core';

@Component({
selector: 'custom-toggle',
template: '<button (click)="toggle()">Toggle</button>'
})
export class CustomToggle {
active = model(false); // This is a model input
disabled = input(false); // This is a standard input

toggle() {
this.active.set(!this.active());
}
}

Angular v19 | The Ultimate Guide to Angular Evolution 105


1. Using Model Inputs in Parent Components:
Parent components can bind properties to model inputs using two-way
binding syntax.

import { Component } from '@angular/core';


import { signal } from '@angular/core';

@Component({
selector: 'app-root',
template: '<custom-toggle [(active)]="isActive" />',
})
export class AppComponent {
isActive = signal(false); // Writable signal
}

In this example, the isActive signal in the parent component (AppComponent) stays in
sync with the active model input in the child component (CustomToggle).
1. Two-Way Binding with Plain Properties:
You can also bind plain JavaScript properties to model inputs.

import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: '<custom-toggle [(active)]="isActive" />'
})
export class AppComponent {
isActive = false; // Plain property
}

1. Implicit Change Events:


Declaring a model input automatically creates a corresponding output event
suffixed with "Change". This event is triggered whenever the value of the model
input changes.

Angular v19 | The Ultimate Guide to Angular Evolution 106


import { Directive, model } from '@angular/core';

@Directive({
selector: 'custom-toggle',
})
export class CustomToggle {
active = model(false); // Creates an output event "activeChange"
}

Benefits:
Model inputs have several advantages. They enable dynamic interactions by allowing
child components to update the state of parent components through two-way data
binding. This results in more interactive and responsive applications. Additionally, mod-
el inputs simplify code by reducing the need for additional properties and boilerplate
code, leading to cleaner and more maintainable codebases. They also enhance flexibil-
ity, making it easier to create custom form controls and other interactive components
that need to modify values based on user actions.

By improving the flexibility and interactivity of Angular components, the model inputs
make it easier to build dynamic and user-friendly applications. This leads to better user
experiences and more efficient management of complex state changes within the An-
gular components.

Signal queries

Performance Dev Experience

Challenge:
Similar to the old inputs, the current implementation of view queries is not very reactive.
We need to use setters or observables to track when the queried dom changes.

Solution:
New functions have been introduced as an alternative to decorators. Each of them pro-
duces as a result a signal value of the queried DOM.

ViewChild:
The first of these is ViewChild().It’s a signal based replacement for @ViewChild().
We use it when we are looking for a single result in our component. Similarly to input() or
model(), we can also use the required option here.

Angular v19 | The Ultimate Guide to Angular Evolution 107


@Component({
...,
template: `
<div #el></div>
<div #requiredDiv></div>
<my-child />
`
})
export class MyComponent {
divEl = viewChild<ElementRef>('el'); // Signal<ElementRef|undefined>
requiredDivEl = viewChild.required<ElementRef>('requiredDiv');
// Signal<ElementRef>
cmp = viewChild(ChildComponent); // Signal<ChildComponent|undefined>
}

ViewChildren:
Similarly, the ViewChildren function is the signal-based counterpart to @ViewChildren().
It is used for querying multiple elements within the component.

@Component({
template: `
<div #el></div>
<div #el></div>
<div #el></div>
`,
})
export class MyComponent {
firstSelector = viewChildren<ElementRef>('el');
// Signal<readonly ElementRef<any>[]>
secondSelector = viewChildren<ElementRef<HTMLDivElement>>('el');
// Signal<readonly ElementRef<HTMLDivElement>[]>
}

Angular v19 | The Ultimate Guide to Angular Evolution 108


ContentChild and ContentChildren:
The contentChild() and contentChildren() functions replace @ContentChild and
@ContentChildren, respectively, and are used for querying projected content within
a component.

// parent.component.ts
@Component({
template: `
<ng-content></ng-content>
`, // Zwróćcie uwagę na tag ng-content
standalone: true,
selector: 'app-parent'
})
export class ParentComponent {
content = contentChild(ChildComponent);
// Signal<ChildComponent | undefined>

contentElements = contentChildren(ChildComponent);
// Signal<readonly ChildComponent[]>
}

Benefits:
The new signal-based approach simplifies the codebase, making it easier to read and
maintain. Signals provide a more reactive way of handling DOM queries, automatically
updating as the queried elements change. This results in a better developer experience
with less need for manual settings and immediate improvements in responsiveness. This
leads to a smoother development process and less boilerplate code.

Output syntax

Efficiency Dev Experience

Challenge:
Inputs have been changed to be signal based. The output so far was still in the old
syntax. This meant we needed decorators to declare them. What’s more, there was
inconsistency in terms of the type safety, as the emit function was accepting following type:
T | undefined which was allowing us to emit an event without value even though we had
typed it to be specific type.

Angular v19 | The Ultimate Guide to Angular Evolution 109


@Component(...)
export class MyComponent {
@Output() oldOutput = new EventEmitter<string>();

onEvent(): void {
this.oldOutput.emit(); // It’s ok even though we typed it as string
}
}

Solution:
A new output function has been introduced. A new output function has been introduced.
Mostly it's just a syntax adjustment, it has nothing to do with signals. For example, it doesn't
mean that a sent value is immediately a signal, as it is with input, where sent values are
always read as signals.

@Component(...)
export class MyComponent {
valueChanged = output<string>();

onValueChanged(msg: string): void {


this.valueChanged.emit(msg);
}
}

Changes have also been made to the return type of the output function. It now returns
OutputEmitterRef<T>, which prevents the output of a value inconsistent with the declared
one. The example we provided in the challenge section will generate an error when using
the new syntax.

@Component(...)
export class MyComponent {
newOutput = output<string>();

onEvent(): void {
this.newOutput.emit(); // ERROR: Expected 1 arguments, but got 0.
}
}

Angular v19 | The Ultimate Guide to Angular Evolution 110


Benefits:
The new syntax alignment with signal-based DOM queries and inputs offers several ben-
efits. It prevents the emission of undefined values, improving the reliability and accuracy
of our code. Better typing makes our code less error-prone, reducing the likelihood of
runtime errors and ensuring a smoother user experience.

Collaboration Between Angular and Wiz

Performance Dev Experience

Challenge:
Angular, despite its extensive feature set and developer-friendly tools, has faced
significant competition from other frameworks like React and Vue. A major challenge
has been balancing the need for performance optimization with maintaining good
developer experience. In addition, Angular relies on zone.js for reactive programming,
which has introduced overhead and complexity that affects optimal performance,
especially in performance-critical applications used by millions of users, such as those
developed by Google.

Solution:
To address these challenges, the Angular team has begun a collaboration with Wiz,
an internal web framework developed by Google and known for its performance opti-
misations in applications such as Google Search and Photos. This collaboration aims to
integrate Wiz's performance-related features into Angular's developer tools, starting with
the introduction of Angular Signals.

Angular Signals, previewed at NG Conf 2024, represents a significant change to theAngu-


lar's reactive programming model. Designed to replace zone.js-based changedetection,
Signals provides a new reactive foundation that improves both performance and devel-
oper experience. This integration has already been put into practice, with Signals being
used in production for YouTube Mobile, demonstrating its effectiveness and potential.

Benefits:
The collaboration between Angular and Wiz brings several significant benefits. Integrating
Wiz's performance optimizations with Angular improves the overall efficiency and speed
of applications, addressing one of the keychallenges faced by Angular developers.

This not only reduces the overhead mostly associated with zone.js but also simplifies
reactive programming, making it more intuitive and less prone to errors. As a result, de-
velopers can enjoy a smoother, more productive coding experience.

Moreover, the synergy between the two teams is good for continuous innovation, ensur-
ing that both frameworks benefit from shared insights and technological advancements.

Angular v19 | The Ultimate Guide to Angular Evolution 111


This partnership has the potential to enhance Angular's position in the web development
community, attracting more developers due to its improved performance and us-
er-friendly features. This ongoing collaboration signifies a future where Angular not only
retains its solidity but also evolves to meet the high performance demands of modern
web applications, making it an attractive choice for developers.

Expert Opinion:

The Angular and Wiz collaboration will try to blend together the best parts of both
frameworks. In Angular, we will see improvements in hydration through Wiz's event dis-
patch library, which can record and replay events on the client while it is not yet inter-
active. In Wiz, we will see improvements in the state management with the extended
usage of signals.

We are not going to see Wiz switching to a full open-source model. Rather, we will see
some of its parts becoming open source through the Angular framework.

~ Aristeidis Bampakos
Google Developer Expert

What are the core aspects and benefits of merging Angular and Wiz? When can we
expect Wiz to switch to an open-source model?

Maybe Google has a secret sauce that makes things super fast. How fast? Think how
performant Gmail and Google search are. That's WIZ in action!

On the other hand, Angular is great for complex interactive UIs.


Combining the performance of WIZ with the awesomeness of angular will make it an
even more popular framework.

Some of WIZ's features may become open source and others will find their way to An-
gular. We have already seen how great the deferrable views are, which is WIZ’s influ-
ence on Angular.

~ Fanis Prodromou
Google Developer Expert

Angular v19 | The Ultimate Guide to Angular Evolution 112


Ng-content fallback

Efficiency Dev Experience

Challenge:
One of the well-known challenges in Angular development has been handling scenarios
where a component may not receive any projected content. In such cases, developers
traditionally had to implement complex workarounds to ensure that component still dis-
plays useful default content. This lack of native support for fallback content in ng-con-
tent has been a limitation, causing additional overhead and complexity to component
design and development.

Solution:
Angular v18 introduces a significant enhancement with built-in support for fallback
content in ng-content slots. This new feature simplifies the process of defining default
content that will be displayed if no content is projected into the component. By allowing
developers to specify fallback content directly within the ng-content tag, Angular v18
improves component development, making it more intuitive and efficient.

The implementation is straightforward. Developers can now include default content


within the ng-content tag itself. For instance:

import { Component } from '@angular/core';

@Component({
standalone: true,
selector: 'my-component',
template: `
<ng-content select="header"> Default header </ng-content>
<ng-content> Default main content </ng-content>
<ng-content select="footer"> Default footer </ng-content>
`,
})
export class MyComponent {}

In this example, if no content is provided for the header, main content, or footer, the de-
fault values ("Default header", "Default main content", "Default footer") will be displayed.
This feature significantly reduces the need for conditional logic and additional checks
within the component's logic to ensure that fallback content is shown.

Angular v19 | The Ultimate Guide to Angular Evolution 113


Benefits:
The introduction of fallback content in ng-content slots brings multiple benefits to
Angular developers. Firstly, it gives us the way to ensure that components always display
meaningful content, even when no projected content is provided. This improvement re-
duces the risk of blank or incomplete UI elements in applications.

Additionally, the new feature simplifies the component development process. By em-
bedding fallback content directly into the component's template, developers can avoid
complex workarounds. This approach improves the overall developer experience.

Route Redirects as Functions

Dev Experience

Challenge:
In previous versions of Angular, route redirects were limited to static strings. This limita-
tion made it difficult to handle redirects that depend on runtime conditions or complex
logic. Developers needed a more flexible way to define redirects based on dynamic data,
such as query parameters or application state.

Solution:
Angular version 18 introduces a new feature that allows redirectTo to accept a function
that returns a string. This function can implement complex logic, enabling more flexi-
ble and dynamic redirects. It essentially receives the current route's context, including
query parameters, allowing developers to tailor the redirection logic to specific runtime
conditions.

Consider a scenario where you need to redirect users based on the presence of a use-
rId query parameter. If the userId is provided, users should be redirected to their profile
page. If not, an error should be logged, and users should be redirected to a "not found"
page.

Here’s how this can be achieved using the new redirectTo function feature:

Angular v19 | The Ultimate Guide to Angular Evolution 114


const routes: Routes = [
{ path: "first-component", component: FirstComponent },
{
path: "old-user-page",
redirectTo: ({ queryParams }) => {
const errorHandler = inject(ErrorHandler);
const userIdParam = queryParams['userId'];
if (userIdParam !== undefined) {
return `/user/${userIdParam}`;
} else {
errorHandler.handleError(new Error('Attempted navigation to user page
without user ID.'));
return `/not-found`;
}
},
},

{ path: "user/:userId", component: OtherComponent },


];

In this example:

• The redirectTo function checks the queryParams for a userId.


• If userId is present, the function returns the URL tothe user's profile.
• If userId is missing, the function logs an error using an ErrorHandler service and
redirects to a "not found" page

Benefits:
The introduction of function-based route redirects in Angular greatly enhances the flexibil-
ity of routing in applications. It allows developers to define dynamic redirection logic based
on the application's current state or runtime data. This means that developers can easily
implement complex routing requirements without resorting to workarounds or additional
code structures.

This feature not only improves the developer experience by simplifying the implementation
of dynamic routing but also enhances the maintainability and readability of the routing
code. It streamlines navigation logic, making it more intuitive and reducing the potential for
errors, thus improving overall application reliability.

Angular v19 | The Ultimate Guide to Angular Evolution 115


New RedirectCommand

Dev Experience

Challenge:
In previous Angular versions, managing complex navigation patterns and using Navi-
gationExtras with Guards and Resolvers could be cumbersome and less maintainable.
Developers struggled to handle advanced navigation scenarios, which required a more
streamlined and flexible approach to manage redirections and navigation states.

Solution:
Angular version 18 introduces the RedirectCommand class, designed to manage Naviga-
tionExtras more effectively. This class allows for improved redirection capabilities within An-
gular applications, particularly when used in conjunction with Guards and Resolvers. The
RedirectCommand class makes it easier to implement and maintain complex navigation
logic, enhancing both flexibility and developer experience.

Consider a scenario where a route guard needs to redirect users based on certaincondi-
tions, such as checking user permissions or application state. The new RedirectCommand
class simplifies this process by allowing developers to create a UrlTree and manage Navi-
gationExtras in a concise manner.

Here’s an example that demonstrates the use of RedirectCommand within a route guard:

const route: Route = {


path: 'page1',
component: PageComponent,
canActivate: [
() => {
const router: Router = inject(Router);
const urlTree: UrlTree = router.parseUrl('./page2');
return new RedirectCommand(urlTree, { skipLocationChange: true });
},
],
};

Angular v19 | The Ultimate Guide to Angular Evolution 116


In this example:

• The guard function uses Angular's dependency injection to get an instance of


the Router.
• It then creates a UrlTree for the desired redirect URL ('./page2').
• The guard returns a new RedirectCommand, that includes the UrlTree and ad-
ditional NavigationExtras options such as skipLocationChange.

Benefits:
The introduction of the RedirectCommand class in Angular 18 provides several advan-
tages. It increases the flexibility and maintainability of routing logic, particularly in the
complex navigation scenarios involving Guards and Resolvers. This feature streamlines
the management of redirections and NavigationExtras, reducing boilerplate code and
making it easier to implement robust navigation patterns. By simplifying the handling
of challenging navigation tasks, the RedirectCommand class improves developer pro-
ductivity and the overall developer experience in Angular applications.

This feature makes it more straightforward for developers to manage advanced redi-
rection scenarios, improving the reliability and readability of navigation-related code.
This leads to a better-maintained applications and a smoother development process.

Improved Hydration Debugging Experience

Dev Experience

Challenge:
Debugging hydration issues in Angular applications used to be challenging. Developers
lacked visibility into the hydration status of components, making it difficult to identify and
resolve hydration errors effectively. This limited insight often led to extended debugging
sessions and reduced productivity.

Solution:
Angular has updated Angular DevTools to enhance the debugging experience by visual-
izing Angular’s hydration process. This update includes new icons next to each compo-
nent indicating its hydration status. Additionally, developers can enable an overlay mode to
see which components have been hydrated on the page. If there are any hydration errors,
Angular DevTools will visualize them directly in the component explorer, providing immedi-
ate feedback and detailed information about the problem.

Angular v19 | The Ultimate Guide to Angular Evolution 117


Benefits:
The improved debugging experience with Angular DevTools provides several key benefits.
It significantly enhances developers' ability to diagnose and fix hydration issues by provid-
ing clear visual indicators of each component's hydration status. The overlay mode pro-
vides a comprehensive view of the hydration process across the entire application, making
it easier to understand and debug complex hydration scenarios. Additionally, visualizing
hydration errors directly in the Component Explorer helps developers quickly pinpoint and
resolve problems, improving productivity and reducing the time spent on debugging.

Angular v19 | The Ultimate Guide to Angular Evolution 118


New Observables in Forms

Dev Experience

Challenge:
Managing state changes in form controls has been a recurring challenge. Traditional-
ly, we have had to subscribe to multiple observables or use different methods to track
changes in form control values, status, and other states such as pristine or touched. This
approach can lead to increased complexity of the code, making it harder to maintain
and understand, especially in large applications with many form controls. There were
events we couldn't subscribe to at all, such as touched and untouched.

Solution:
Angular v18 introduces a unified approach to handling control state changes with a new
feature added to the AbstractControl class. This feature exposes an event observable that
tracks various state changes in form controls, such as value changes, pristine state, touch
status, and overall control status. By combining these state changes into a single observa-
ble stream, Angular simplifies the management of form control states.

Here's how it works:

import { Component, OnInit } from '@angular/core';


import { FormControl } from '@angular/forms';

@Component({
selector: 'app-my-form',
template: `
<form>
<input [formControl]="control">
</form>
`
})
export class MyFormComponent implements OnInit {
control = new FormControl('');

ngOnInit() {
this.control.events.subscribe(event => {
console.log(event);
});
}
}

Angular v19 | The Ultimate Guide to Angular Evolution 119


In this example, the control.events observable emits events whenever the value,
status or state of the form control changes. This unified event stream allows de-
velopers to handle all state changes in a single subscription, making the code
easier to maintain.

Benefits:

The unified control state change events make it easier to track and manage form
control states by combining all state changes into one observable. This simplifies
the code, making it clearer and easier to maintain. It also improves the reliability
of handling user interactions and form submissions. Overall, this feature boosts
developer productivity and simplifies the development process.

New Documentation

Dev Experience

Challenge:
The previous Angular documentation site, angular.io, had several areas that could be
improved to enhance the learning experience for developers. Developers needed more
intuitive navigation, better search functionality, and interactive learning tools to get
started with Angular in a more effective way. What's more, some of the examples were
already out of date (e.g. syntax) and updating them to the latest standards was quite
difficult.

Solution:
Angular has launched a new documentation site, angular.dev, which is now the official
documentation website for Angular. This new site has a modern look and feel and includes
several enhancements aimed at improving the developer experience.

Features of Angular.dev:

1. Interactive Hands-On Tutorial:


Angular.dev features an interactive, hands-on tutorial based on WebContainers,
allowing developers to try out Angular concepts and code
directly in the browser.

2. Interactive Playground:
The site includes an interactive playground with examples, enabling developers
to experiment with Angular code snippets and see the results in real time.

Angular v19 | The Ultimate Guide to Angular Evolution 120


3. Improved Search:
Powered by Algolia, the new search functionality offers faster and more accurate
results, helping developers find the information they need more efficiently.

4. Refreshed Guides:
The guides on angular.dev have been updated and improved to provide clearer,
more detailed explanations and instructions.

5. Simplified Navigation:
Navigation has been streamlined to make it easier for developers to find their
way around the site and access the resources they need.

6. Modern Look and Feel:


The site has been redesigned with a modern aesthetic, making it more visually
appealing and easier to use.

Benefits:
The new angular.dev site significantly enriches the developer experience by providing
a more intuitive and engaging learning environment. The interactive tutorials and play-
ground allow developers to learn by doing, which can be more effective than just reading
documentation. Improved search and simplified navigation help developers find the infor-
mation they need quickly, reducing the time spent searching for answers and increasing
productivity. Additionally, the refreshed guides and modern design make the documenta-
tion more accessible and enjoyable to use.

By redirecting all requests from angular.io to angular.dev and ensuring that existing links
continue to work by forwarding to v17.angular.io, Angular has made the transition smooth
for developers. This ensures that the community can continue to access valuable resources
without disruption.

Visit angular.dev to explore the new site and take advantage of these improvements!

Angular v19 | The Ultimate Guide to Angular Evolution 121


Hydration Support in CDK and Material

Performance Efficiency Dev Experience

Challenge:
In Angular version 17, some Angular material and CDK components were removed from
hydration. This meant that these components would not retain their server-rendered
state and would be rerendered on the client side. This re-rendering could impact per-
formance and user experience, as the components had to be reinitialized and redrawn
after the initial load.

Solution:
Starting with Angular version 18, all Angular Material and CDK components are fully hy-
dration compatible. This means that these components will now retain their server-ren-
dered state and do not require rerendering on the client side. This change improves
performance by reducing the need for additional rendering work once the application
loads in the browser.

Benefits:
The update to make all Angular Material and CDK components fully hydration compat-
ible brings several benefits. It enhances the performance of Angular applications by
reducing unnecessary rerendering of components. This leads to faster load times and
a smoother user experience, as the components are ready to interact with immediately
after the initial server-side rendering. It also simplifies the development process by en-
suring consistent behaviour across all Material and CDK components, without the need
for special handling or workarounds for hydration issues.

Angular v19 | The Ultimate Guide to Angular Evolution 122


Material 3

Efficiency Dev Experience

Challenge:
A few months ago, Angular introduced experimental support for Material 3, the latest version
of Google’s Material Design guidelines. During this experimental phase, developers provided
feedback highlighting areas for improvement. The goal was to refine these components to
ensure they met the high standards expected by the developer community before releas-
ing them as stable. At the same time, Angular Material was still based on Material 2, which
made customization difficult. This lack of flexibility posed challenges for developers looking
to implement the latest design trends and tailor components to their specific needs.

Solution:
After listening to feedback and making the necessary adjustments, Angular has
now graduated the Material 3 components to stable status. This means that Material
3 is fully supported and ready for production use, providing developers with a polished
set of UI components that adhere to the latest Material Design guidelines. Additionally,
Angular Material 3 introduces tokens for theming, which greatly enhance customiza-
tion possibilities. These tokens allow for more granular control over component styling,
giving developers greater flexibility to tailor their applications to specific design re-
quirements.

Benefits:
The transition of Material 3 to stable status provides several key benefits. First, it provides
reliability, as developers can now use Material 3 components in production environments
with confidence, knowing that they have been thoroughly tested and refined. Second, the
components adhere to the latest Material Design guidelines, ensuring a modern and
consistent look and feel across applications. Third, the updated material.angular.io site
provides comprehensive documentation and resources, making it easier for develop-
ers to implement and customize Material 3 components. Together, these enhancements
simplify the development process and ensure that applications built with Angular and
Material 3 are visually appealing, consistent, and maintain high standards of user inter-
face design.

Angular v19 | The Ultimate Guide to Angular Evolution 123


Typescript/Node.js support

instanceof Narrowing Through Symbol.hasInstance


(available in 5.3)

Dev Experience

Challenge:
Prior to TypeScript 5.3, the instanceof operator did not always work well with custom type
checks. This made it hard to correctly check the type of some objects, which could lead
to errors in the code.

Solution:
TypeScript 5.3 adds a new feature that allows the instanceof operator to use custom type
checks. If a class has a [Symbol.hasInstance] method, TypeScript will use it to check the
type of an object. This makes type checking more accurate.

interface PointLike {
x: number;
y: number;
}

class Point implements PointLike {


x: number;
y: number;

constructor(x: number, y: number) {


this.x = x;
this.y = y;
}

static [Symbol.hasInstance](val: unknown): val is PointLike {


return (
!!val &&
typeof val === 'object' &&
'x' in val &&
'y' in val &&
typeof val.x === 'number' &&
typeof val.y === 'number'
);
}
}

Angular v19 | The Ultimate Guide to Angular Evolution 124


function checkValue(value: unknown) {
if (value instanceof Point) {
console.log(value.x); // Safe to access x and y
console.log(value.y);
}
}

Summary:
Before TypeScript 5.3, developers had to create manual type guard functions to handle
custom instance checks, which made the code less clean and increased the potential for
errors. This manual process involved writing functions to check whether an object met
certain criteria to be considered of a particular type, such as checking properties and
their types individually.

With the introduction of TypeScript 5.3, the instanceof operator can now leverage custom
type guards defined through the Symbol.hasInstance method. This allows developers to
embed custom type-checking logic directly within classes, making the type-checking
process more accurate and the code cleaner. The instanceof operator now automatically
uses this custom logic, simplifying the code and enhancing type safety.

In summary, TypeScript 5.3 enhances the instanceof operator by allowing it to use


custom type guards via Symbol.hasInstance, resulting in more accurate type checking
and cleaner code. This improvement reduces the need for manual type guard functions
and makes the code more reliable and easier to maintain.

Angular v19 | The Ultimate Guide to Angular Evolution 125


Checks for super Property Accesses on Instance Fields
(available in 5.3)

Dev Experience

Challenge:
Using super incorrectly on instance fields could cause runtime errors because super only
works on prototype methods, not instance properties. This led to confusing bugs that
were difficult to diagnose and fix.

Solution:
TypeScript 5.3 adds checks to ensure that super is used correctly, preventing runtime
errors by issuing a type error when super is used on instance fields.

class Base {
someMethod = () => {
console.log("Base method called!");
}
}
class Derived extends Base {
someOtherMethod() {
super.someMethod(); // Error: 'super.someMethod' is 'undefined'
}
}

new Derived().someOtherMethod();

This improvement prevents runtime errors, making the code safer and easier to debug.

Benefits:
Checks for super property accesses on instance fields prevent runtime errors, making
code safer and easier to debug.

Angular v19 | The Ultimate Guide to Angular Evolution 126


Narrowing on Comparisons to Booleans (available in 5.3)

Dev Experience

Challenge:
Boolean value comparisons were not well understood by TypeScript, leading to inaccu-
rate type narrowing and potential type errors.

Solution:
TypeScript 5.3 improves the handling of boolean comparisons, enabling for accurate
type narrowing and more secure code.

type MyType = { a: string } | { b: string };

function isA(x: MyType): x is { a: string } {


return "a" in x;
}

function someFn(x: MyType) {


if (isA(x) === true) {
console.log(x.a); // Works correctly
}
}

Benefits:
Narrowing on comparisons to booleans improves type safety, reduces errors, and
enhances code clarity.

Angular v19 | The Ultimate Guide to Angular Evolution 127


Smarter Narrowing in Non-hoisted Functions (available in 5.4)

Dev Experience

Challenge:
Previously, type escaping in non-hoisted functions was inaccurate, leading to potential
type errors and less reliable code. This was particularly problematic in functions where
variables could change type or value multiple times, making it difficult for TypeScript to
keep track of the correct type.

Solution:
TypeScript 5.4 refines type narrowing in non-hoisted functions by considering the last
assignment point for type checking. This improvement ensures that TypeScript accu-
rately tracks the type of variables based on their most recent value, resulting in more
reliable and accurate type checks.

function example(flag: boolean) {


let value: string | number;

if (flag) {
value = "string";
} else {
value = 42;
}

// TypeScript now correctly infers the type of 'value' based on the last assign-
ment
if (typeof value === "string") {
console.log(value.toUpperCase()); // Works because 'value' is correctly in-
ferred as string
} else {
console.log(value.toFixed(2)); // Works because 'value' is correctly inferred as
number
}
}

Benefits:
Smarter narrowing in non-hoisted functions improves type accuracy, reduces type errors,
and improves the overall clarity and reliability of the code. This makes it easier for devel-
opers to write safe and precise code without worrying about incorrect type inferences.

Angular v19 | The Ultimate Guide to Angular Evolution 128


Expanded Template Literal Types (available in 5.4)

Dev Experience

Challenge:
Template literal types in previous versions of TypeScript were limited in their expressive-
ness and flexibility. Developers faced challenges when trying to define complex types that
involved string manipulation and pattern matching, reducing the usefulness of template
literal types in certain scenarios.

Solution:
TypeScript 5.4 extends the capabilities of template literal types, making type definitions
more expressive and flexible. This enhancement makes it possible to create more com-
plex and precise types that better meet the needs of various coding scenarios, espe-
cially those involving dynamic string patterns.

type OrderStatus = "pending" | "shipped" | "delivered";


type UppercaseOrderStatus = Uppercase<OrderStatus>; // "PENDING" |
"SHIPPED" | "DELIVERED"
type StatusMessage = `Order is ${OrderStatus}`; // "Order is pending" | "Order is
shipped" | "Order is delivered"

function getStatusMessage(status: OrderStatus): StatusMessage {


return `Order is ${status}`;
}

let message: StatusMessage = getStatusMessage("pending");


console.log(message); // Output: "Order is pending"

Benefits:
Expanded template literal types improve the flexibility and expressiveness of type defini-
tions, making it easier for developers to work with complex string patterns and dynamic
types. This enhancement leads to clearer and more maintainable code, as types can now
more accurately reflect the intended data structures.

Angular v19 | The Ultimate Guide to Angular Evolution 129


Enhanced readonly Arrays and Tuples (available in 5.4)

Dev Experience

Challenge:
Read-only arrays and tuples in previous versions of TypeScript had limited type infer-
ence and immutability guarantees. This made it challenging to work with immutable
data structures, as the type system did not always provide accurate type information or
enforce immutability effectively.

Solution:
TypeScript 5.4 enhances the handling of read-only arrays and tuples by improving type
inference and immutability guarantees. This ensures that the type system accurately
reflects the immutability of these data structures, provides better type information and
enforces immutability more effectively.

const numbers: readonly number[] = [1, 2, 3];


numbers.push(4); // Error: Property 'push' does not exist on type 'readonly
number[]'
type Point = readonly [number, number];
const point: Point = [1, 2];
point[0] = 3; // Error: Index signature in type 'readonly [number, number]' only
permits reading

Benefits:
Enhanced read-only arrays and tuples improve type inference, guarantee stronger
immutability guarantees, and make it easier for developers to work with immutable data
structures. This leads to more reliable and maintainable code, as developers can be con-
fident that read-only data structures will not be inadvertently modified.

Angular v19 | The Ultimate Guide to Angular Evolution 130


New Type Checking Compiler Options (available in 5.4)

Dev Experience

Challenge:
Developers previously had limited control over type checking settings in TypeScript, mak-
ing it difficult to customize type checks to meet specific project needs. This lack of flex-
ibility could result in type checking that was either too strict or too lenient, reducing the
effectiveness of the type system.

Solution:
TypeScript 5.4 introduces new compiler options that give developers more control over
type checking settings. These options allow for greater customization of type checks,
so developers can tailor the type system to the specific needs of their projects.

{
"compilerOptions": {
"strictNullChecks": true,
"noImplicitAny": true,
"noImplicitThis": true,
"strictPropertyInitialization": true
}
}

Benefits:
New type checking options enhance customization and give developers more control over
how TypeScript enforces type checks. This flexibility leads to a more tailored and effective
type system, resulting in higher quality code and a better development experience.

Angular v19 | The Ultimate Guide to Angular Evolution 131


Angular v1
release date:

Angular v19 | The Ultimate Guide to Angular Evolution 132


Angular v19 introduces a wealth of features designed to increase developer productivity,
enhance application performance, and enrich the user experience.

With updates such as linkedSignal for reactive state management, hot module replace-
ment for faster development cycles, and the experimental resource API for streamlined
async operations, Angular continues to evolve as a modern framework.

From simplifying initializer setups to advancing language service capabilities, this ver-
sion release makes it easy for developers to build efficient, maintainable, and responsive
applications.

New reactive primitive - linkedSignal (experimental)

Dev Experience Efficiency

Challenge:

Managing the dependencies and changes between reactive signals in Angular can
be a challenging task in complex applications.

Although the existing reactive primitives, such as signal and computed, offer robust
solutions for handling reactivity, they lack mechanisms for directly coupling reactive
state changes with derived calculations that automatically reset when their source
changes.

Developers are often required to add extra boilerplate to achieve this functionality,
which increases the complexity of the code and reduces its maintainability.

Solution:

Angular 19 introduces linkedSignal, a new experimental reactive primitive designed


to fill this gap.

linkedSignal is a writable signal that links its value to a source signal and recalculates
itself based on a given computation function whenever the source signal changes.

Angular v19 | The Ultimate Guide to Angular Evolution 133


Key features of linkedSignal:

• when the source signal changes, the value of linkedSignal automatically resets,
• the value of linkedSignal can be set manually using the set method, but it will
revert to the computed value when the source signal updates,
• the computation logic is located inside linkedSignal, keeping dependencies
clear and declarative while preserving all reactive programming features.

As shown in the example below, the set method of favoriteColorId allows the user
to specify a chosen color ID (see onFavoriteColorChange). When the available colors
are updated (see changeColorOptions), the value of favoriteColorId is recalculated.
If the selected color exists in the new list, the signal value remains unchanged; oth-
erwise, it defaults to null.

protected readonly colorOptions = signal<Color[]>([


{ id: 1, name: 'Red',},
{ id: 2, name: 'Green'},
{ id: 3, name: 'Blue'}
]);

protected favoriteColorId = linkedSignal<Color[], number | null>({


source: this.colorOptions,
computation: (source, previous) => {
if(previous?.value) {
return source.some(color => color.id === previous.value) ? previous.value : null;
}
return null;
}
});

protected onFavoriteColorChange(colorId: number): void {


this.favoriteColorId.set(colorId);
}

protected changeColorOptions(): void {


this.colorOptions.set([
{ id: 1, name: 'Red' },
{ id: 4, name: 'Yellow' },
{ id: 5, name: 'Orange' }
])
}}

Angular v19 | The Ultimate Guide to Angular Evolution 134


Benefits:

linkedSignal makes state management easier by linking updates directly to source


signals, ensuring that dependencies remain up-to-date without explicit subscriptions
and/or side effects. With the flexibility to override values as needed, it reduces boiler-
plate, improves maintainability, and optimizes responsiveness.

Expert Opinion:

The Signal APIs have significantly changed how Angular applications are built, making
them easier for new developers to learn, especially as the need for the RxJS library will
become optional.

However, in some situations, there were limitations that required developers to find
new patterns. For example, changing the value of signals that were not designed
to be changed (such as signal inputs) presented challenges.

The introduction of linkedSignal addresses this issue. It allows you to create a new signal
that reflects the state of another signal while still enabling you to modify the new sig-
nal's value independently. It ensures data consistency while keeping the original source
signal unchanged.

To further enhance linkedSignal, the Angular team added the ability to derive the sig-
nal's state conditionally using a computation function. It is particularly useful for sce-
narios where you need to reset the signal's value or provide a value based on certain
conditions.

~ Fanis Prodromou
Google Developer Expert

Angular v19 | The Ultimate Guide to Angular Evolution 135


Resource API (experimental)

Dev Experience Efficiency

Challenge:

Managing asynchronous operations in Angular applications often involves complex


patterns such as subscriptions, manual state tracking, and additional boilerplate.
Current practices, such as combining RxJS for data fetching with signals for state man-
agement, can result in fragmented and difficult-to-maintain code. Developers need
a simpler and more integrated approach to handle async data reactively.

Solution:

Angular 19 introduces the experimental resource API, which integrates asynchronous


operations into the signal graph. A resource is a declarative way to define, load,
and manage async dependencies, providing both the value and status of an oper-
ation as signals.

This API simplifies the management of asynchronous workflows by coupling data


fetching, state, and reactivity into a single coherent abstraction.

Angular v19 | The Ultimate Guide to Angular Evolution 136


fruitId = signal<string>('apple-id-1');
fruitDetails = resource({
request: this.fruitId,
loader: async (params) => {
const fruitId = params.request;
const response = await fetch(`https://api.example.com/fruit/${fruitId}`,
{signal: params.abortSignal});
return await response.json() as Fruit;
}
});
protected isFruitLoading = this.fruitDetails.isLoading;
protected fruit = this.fruitDetails.value;
protected error = this.fruitDetails.error;
protected updateFruit(name: string): void {
this.fruitDetails.update((fruit) => (fruit ? {
...fruit,
name,
} : undefined))
}
protected reloadFruit(): void {
this.fruitDetails.reload();
}
protected onFruitIdChange(fruitId: string): void {
this.fruitId.set(fruitId);
}

The optional request parameter accepts the input signal that the asynchronous re-
source is associated with (in our example, it is fruitId, but it could just easily be a com-
puted signal consisting of multiple values).

We also define the loader function, with which we asynchronously download the data
(the function should return a promise). The created resource named fruitDetails allows
us to do the following, among other things:

• access the current value signal (which also returns undefined if the resource
is not available at the moment),
• access the status signal (one of: idle, error, loading, reloading, resolved, local),
• access extra signals like ‘isLoading’ or ‘error’,
• trigger the ‘loader’ function again (using the ‘reload’ method),
• update the local state of the resource (using the ‘update’ method).

Angular v19 | The Ultimate Guide to Angular Evolution 137


The resource is automatically reloaded when the 'request’ signal (in our case fruitId)
changes. The loader is also triggered when the resource is first created.

What about RxJS Interop? Angular also provides an RxJS counterpart to the resource
method called rxResource. In this case, the loader method returns Observable, but all
other properties remain signals.

fruitDetails = rxResource({
request: this.fruitId,
loader: (params) => this.httpClient.get<Fruit>(`https://api.example.com/fruit/
${params.request}`)
})

Benefits:

The resource API streamlines the management of async operations in Angular by us-
ing an integrated, declarative approach. It reduces boilerplate, boosts maintainabili-
ty, and keeps async data seamlessly reactive with the app state. By exposing signals
for both value and status, resources make it easier to consistently handle loading, suc-
cess, and error states.

Angular v19 | The Ultimate Guide to Angular Evolution 138


Expert Opinion:

Before the introduction of the Resource API, developers often struggled to find effective
patterns to make HTTP requests based on changes to signal values.

Using effect: While useful in many situations, the effect was not designed to handle HTTP
requests. It could lead to issues like race conditions, where multiple requests might be
made simultaneously, and unpredictable application states.

Using RxJS subjects: Another approach involved using RxJS subjects to manage the sig-
nal values. However, this combined RxJS and Signals, increasing complexity and making
debugging more challenging.

The Resource API simplifies making HTTP requests based on signal changes.

Declarative requests: Define HTTP requests that automatically trigger when the source
signal updates.

Improved reliability: The API is designed to handle race conditions and prevent unpre-
dictable application states.

Built-in loading state: The Resource API provides a built-in loading state, making it easy
to display loading indicators to users without needing custom logic.

~ Fanis Prodromou
Google Developer Expert

Both linkedSignal and the resource API are the cherry on top of all the features the An-
gular team shipped in the last releases! linkedSignal enables the possibility of always
having a valid state based on another signal, and the resource API is truly impressive!
Using it, we can handle async requests exquisitely, giving the user the best feedback
possible and keeping the code base clean and easy to develop and maintain.

~ Luca Del Puppo


Google Developer Expert

Angular v19 | The Ultimate Guide to Angular Evolution 139


AfterRenderEffect Function (experimental)

Performance Dev Experience

Challenge:

Managing side effects that depend on the DOM state in Angular can be challenging,
especially when those effects need to take place only after the DOM has been updated.
While Angular provides afterRender and afterNextRender to schedule post-render call-
backs, these APIs do not track signal dependencies, making them less suitable for sce-
narios where side effects need to be re-executed based on changes in reactive data.
Developers must often resort to manual tracking, which produces complex and less
maintainable code.

Solution:

Angular’s afterRenderEffect is an experimental function designed to handle side ef-


fects that should only occur after the component has finished rendering and spe-
cific dependencies have changed. Unlike afterRender and afterNextRender, which
always schedule post-render callbacks without dependency tracking, afterRender-
Effect ties callback execution to specific reactive dependencies. This makes it ideal
for ongoing post-render tasks tied to dynamic application state.

counter = signal(0);
constructor() {
afterRenderEffect(() => {
console.log('after render effect', this.counter());
})
afterRender(() => {
console.log('after render', this.counter())
})
}

In this example, afterRender schedules its callback to run after every render cycle re-
gardless of any state changes. In contrast, afterRenderEffect runs its callback only
when the counter signal changes. This ensures that the effect is selectively executed,
based on relevant updates, cutting down on unnecessary operations and improving
application efficiency.

Angular v19 | The Ultimate Guide to Angular Evolution 140


Benefits:

The afterRenderEffect function offers a powerful tool for managing post-render side effects
tied to reactive dependencies. By tracking dependencies and executing only on relevant
changes, it simplifies code, reduces boilerplate, and avoids unnecessary executions.

This makes applications more efficient and easier to maintain, especially in scenar-
ios with frequent state updates and DOM interactions. As an experimental feature,
afterRenderEffect lays the groundwork for more sophisticated reactive workflows
in the evolving Angular's ecosystem.

Minor signal improvements

Performance Dev Experience

Challenge:

Reactive programming in Angular previously faced limitations in handling effects and sig-
nal updates. The effect() function restricted signal writes, adding additional complexity
in certain scenarios. At the same time its execution timing often caused i sues with pre-
mature or delayed updates. Similarly, the toSignal function lacked flexibility in value
comparison, forcing unnecessary updates due to a lack of custom equality logic.

Solution:

Angular 19 tackles these challenges with major updates to effect() and improve-
ments to toSignal.

Removing the allowSignalWrites flag in effect() simplifies usage, letting developers


set signals directly without extra restrictions. In addition, effect() execution is now
integrated into Angular’s change detection cycle, ensuring logical alignment with
the component hierarchy. This eliminates timing issues and makes effect execution
more reliable.

Angular v19 | The Ultimate Guide to Angular Evolution 141


effect(
() => {
console.log(this.users());
},
//This flag is removed in the new version
{ allowSignalWrites: true }
);

Angular now supports custom equality functions in toSignal. Developers can set cus-
tom equality logic to trigger updates only when meaningful changes are detected,
improving performance.

// Create a Subject to emit array values


const arraySubject$ = new Subject<number[]>();
// Define a custom equality function to compare arrays based on their content
const arraysAreEqual = (a: number[], b: number[]): boolean => {
return a.length === b.length && a.every((value, index) => value === b[index]);
};
// Convert the Subject to a signal with a custom equality function
const arraySignal = toSignal(arraySubject$, {
initialValue: [1, 2, 3],
equals: arraysAreEqual, // Custom equality function for arrays
});

Benefits:

These updates improve reactive programming in Angular. The revamped effect() func-
tion simplifies workflows by removing unnecessary restrictions and ensuring correct ex-
ecution timing.
In turn, The custom equality function in toSignal gives more control over updates, reduc-
ing unnecessary re-renders and boosting performance.. Together, these changes sim-
plify the code, make it easier to maintain, and improve the overall developer experience
in Angular apps.

Angular v19 | The Ultimate Guide to Angular Evolution 142


@let template variable syntax

Dev Experience

Challenge:

In previous versions of Angular, declaring variables within templates often meant using
the ngIf directive and @if with the as keyword. This method had its limitations, especial-
ly when dealing with falsy values (such as 0, empty strings, null, undefined, and false),
which would prevent content from rendering. For example:

<div *ngIf="userName$ | async as userName">


<h1>Welcome, {{ userName }}</h1>
</div>

If userName were an empty string, nothing would be displayed.

Solution:

Angular introduces the @let block to simplify template logic by enabling variable dec-
larations directly in the template. This prevents issues with falsy values and improves
readability. The @let block lets developers declare variables directly in the template,
making it easier to handle complex conditions and asynchronous data.

With @let:

<div>
@let userName = (userName$ | async) ?? 'Guest';
<h1>Welcome, {{ userName }}</h1>
</div>

This code handles falsy values like empty strings by providing a default value ('Guest').

Benefits:

With the @let block, developers can declare variables within the template, simplifying
the overall logic. This improvement makes the code cleaner and more readable, espe-
cially when dealing with complex conditions and dynamic data. It also prevents falsy
value issues in ngIf, making sure content appears as expected.

Angular v19 | The Ultimate Guide to Angular Evolution 143


Incremental Hydration (experimental)

Performance UX

Challenge:

Modern web applications demand both high performance and interactivity, espe-
cially for server-rendered content. Hydrating an entire application on the client side
can be resource-intensive, leading to slower load times and delayed interactivity.
Developers need a way to selectively hydrate parts of the application as needed to op-
timize resource usage and improve the user experience.

Solution:

Angular introduces Incremental Hydration as an experimental feature, building


on the foundations of defer blocks, deferrable views (introduced in v17), and event
replay (v18). This feature hydrates server-rendered sections selectively, depending
on predefined triggers. This gives developers control over component activation,
enhancing both speed and user experience.

To enable Incremental Hydration, update the application configuration:

export const appConfig: ApplicationConfig = {


providers: [
provideClientHydration(
withIncrementalHydration()
)
// other providers...
]
};

Incremental Hydration works in tandem with defer blocks. To use it, developers can add
new hydration triggers to specific components:

@defer (hydrate on hover) {


<app-hydrated-cmp />
}

Angular v19 | The Ultimate Guide to Angular Evolution 144


Supported hydration triggers include:

• idle: Hydrate during idle time.


• interaction: Trigger hydration upon user interaction.
• immediate: Hydrate immediately.
• timer(ms): Hydrate after a specified delay.
• hover: Trigger hydration on hover.
• viewport: Hydrate when the component enters the viewport.
• never: Keep the component dehydrated indefinitely.
• when {{ condition }}: Conditional hydration based on a specified condition.

These triggers give developers fine-grained control over hydration timing and behavior,
optimizing resources based on app needs and user actions.

Benefits:

Incremental Hydration improves load times and interactivity by activating components


only when needed, reducing the initial payload and resource consumption. This allows
large applications to run more efficiently by hydrating only the necessary components
at startup.

In the same way, the flexibility provided by different triggers allows developers to tailor hy-
dration strategies to specific use cases, making applications more efficient and responsive.

Angular v19 | The Ultimate Guide to Angular Evolution 145


Expert Opinion:

Incremental hydration is an amazing technique that can significantly improve ap-


plication performance while ensuring a smooth user experience (UX). Every feature
has strengths and weaknesses, and it's important to use them effectively.

While Server-Side Rendering (SSR) and Incremental Hydration offer significant perfor-
mance gains, they are not meant to replace Single-Page Applications (SPAs) completely.
These techniques benefit client-facing applications, especially when it's essential to min-
imize the time it takes for users to see and interact with the page for the first time.

Furthermore, incremental hydration can improve performance without negatively im-


pacting the SEO.

~ Fanis Prodromou
Google Developer Expert

Incremental hydration has been missing for a while. Its predecessor, Server Side Render, was
a nightmare to implement, and it has impacted the decisions between React and Angular
for many teams in the past. Unfortunately, many chose React because it was already “ready.”

But now it is here and straightforward to use.


Not all projects need this feature, so it will probably impact only a tiny part of the pro-
jects followed by the Angular community. But it can help open the doors for new meta
frameworks based on Angular.

~ Luca Del Puppo


Google Developer Expert

Angular v19 | The Ultimate Guide to Angular Evolution 146


Server Route Configuration (experimental)

Performance UX

Challenge:

Hybrid rendering in Angular applications requires a high degree of flexibility to op-


timize performance and user experience. Without a clear way to define route ren-
dering—server-side, pre-rendered, or client-side—developers rely on complex setups
and manual work. The absence of a clear API can lead to inefficiencies and higher
maintenance efforts.

Solution:

Angular's experimental Server Route Configuration API lets developers set rendering
modes for routes flexibly and declaratively. Using this API, developers can optimize
the performance of their applications by choosing the most appropriate rendering
strategy for each route, such as server-side rendering (SSR), static site generation
(SSG), or client-side rendering (CSR).

Example configuration:

import { RenderMode, ServerRoute } from '@angular/ssr';


export const serverRouteConfig: ServerRoute[] = [
{ path: '/login', renderMode: RenderMode.Server },
{ path: '/fruits', renderMode: RenderMode.Prerender },
{ path: '/**', renderMode: RenderMode.Client }
];

In this configuration:

• /login: Uses SSR to ensure that the latest data is rendered on every request.
• /fruits: Uses SSG to generate content at build time for faster loading.
• /*: Defaults to CSR for all other routes, optimizing for interactivity.

Angular v19 | The Ultimate Guide to Angular Evolution 147


In addition, the API supports dynamic path parameters in pre-render mode by defining
functions to resolve path parameters:

export const serverRouteConfig2: ServerRoute[] = [


{
path: '/fruit/:id',
renderMode: RenderMode.Prerender,
async getPrerenderParams() {
const fruitService = inject(FruitService);
const fruitIds = await fruitService.getAllFruitIds();
return fruitIds.map(id => ({ id }));
},
},
];

This example shows how the /fruit/:id route can dynamically generate static pag-
es for all available fruit IDs, guaranteeing optimized performance for frequently ac-
cessed resources.

Benefits:

The Server Route Configuration API makes it easier to manage hybrid rendering
in Angular applications. It reduces complexity and maintenance by allowing devel-
opers to declaratively define rendering modes for specific routes. The ability to dy-
namically resolve parameters for pre-rendered paths further increases flexibility,
making it easier to build high-performance and scalable applications.

RouterOutlet data input

Dev Experience

Challenge:

In Angular applications, sharing data between parent and child components routed
through a RouterOutlet often requires manual implementations, such as input bindings
or service-based state management. These methods can be tedious and prone to er-
rors, especially when dynamic updates are needed. Developers are looking for a more
efficient way to pass and update data.

Angular v19 | The Ultimate Guide to Angular Evolution 148


Solution:

Angular 19 introduces the routerOutletData input for RouterOutlet, simplifying the process
of sending data from parent components to child components routed through the outlet.
When routerOutletData is set, the data becomes accessible within the child components
using the ROUTER_OUTLET_DATA token. This token takes advantage of Angular's Signal
API. Changes in input data are dynamically reflected in child components, eliminating
the need for manual assignments or extra code.

Parent Component:

<router-outlet [routerOutletData]="routerOutletData()" />

Child Component:

import { Signal, inject } from '@angular/core';


import { ROUTER_OUTLET_DATA } from '@angular/router';

export class ChildComponent {


readonly routerOutletData: Signal<MyType> = inject(ROUTER_OUTLET_DATA);
}

With this configuration, the parent component can dynamically update routerOutletDa-
ta, and the changes are automatically reflected in the child component. This increases
the flexibility of data sharing within routed components by eliminating the need for static
data mappings.

Benefits:

The routerOutletData input streamlines data communication between parent and child
components routed through RouterOutlet. Using Angular's reactive signaling type,
it makes dynamic updates possible, reducing the need for boilerplate code and manual
state synchronization.

Angular v19 | The Ultimate Guide to Angular Evolution 149


RouterLink directive enhancements

Dev Experience

Challenge:

Configuring route navigation in Angular often requires the use of multiple inputs to Router-
Link, such as query parameters, fragment, and other options. When it comes to complex
navigation scenarios, this approach can become cumbersome and can cause errors.

Solution:
As of Angular version 18.1, the RouterLink directive now accepts anUrlTree object as its
input. By encapsulating route options like query parameters and paths into a single Url-
Tree object, this update streamlines the navigation process.

Example:

<a [routerLink]="homeUrlTree">Home</a>

Using a UrlTree allows developers to define navigation configurations in one place,


which helps to clarify and maintain the code. However, to avoid conflicts, Angular enforces
strict rules: if an UrlTree object is passed to RouterLink, additional inputs such as queryPar-
ams or fragments cannot be used. If such inputs are provided alongside an UrlTree, Angular
will throw an error:

Cannot configure queryParams or fragment when using a UrlTree


as the routerLink input value.

It guarantees a consistent navigation setup and eliminates any confusion in route handling.

Benefits:
By allowing RouterLink to accept UrlTree objects, Angular gives developers a cleaner
and more flexible way to define navigation logic. This improvement simplifies code,
enhances readability, and keeps navigation configurations in one place. Additionally,
the clear error messages help developers avoid common pitfalls, making applica-
tions more robust and easier to maintain.

Angular v19 | The Ultimate Guide to Angular Evolution 150


Default query params handling strategy

Dev Experience Efficiency

Challenge:

Configuring query parameter handling strategies individually for each navigation


can become repetitive and susceptible to errors. Developers often need a consistent
way to manage query parameters across the entire application without having to man-
ually configure each route.

Solution:

Angular now allows developers to globally set a default query parameter handling strat-
egy in the provideRouter() configuration. The result is the elimination of repetitive per-
route configurations, which streamlines navigation logic and improves maintainability.

Example:

export const appConfig: ApplicationConfig = {


providers: [
provideRouter(routes, withRouterConfig({ defaultQueryParamsHandling: 'preserve' }))
]
};

Angular’s default strategy is replace, but developers can choose between:

• preserve: It retains the current query parameters during navigation.


• merge: It combines the new query parameters with the existing ones.

This flexibility ensures that query parameter handling meets application-specific require-
ments, reducing boilerplate and potential inconsistencies.

Benefits:

With a global query parameter handling strategy, Angular reduces repetitive code, simpli-
fies navigation, and ensures consistency across the app. This feature increases the pro-
ductivity of the developer while maintaining the flexibility for advanced use cases.

Angular v19 | The Ultimate Guide to Angular Evolution 151


Components become standalone by default

Performance Dev Experience Efficiency

Challenge:

The legacy approach to Angular components often relies on explicit module declara-
tions, which can add complexity and create barriers for new developers. Standalone
components were introduced in v14, but not by default, creating inconsistencies
and additional setup for developers wishing to use them.

Solution:

With Angular v19, standalone: true becomes the default for components, directives,
and pipes. Angular becomes more intuitive and accessible with this change, as ex-
plicit module declarations are no longer needed in most cases.

Example:

@Component({
imports: [],
selector: 'home',
template: './home-component.html'
// standalone in Angular 19!
})
export class HomeComponent { }

For existing projects, an automated migration during the ng update will adjust standalone
flag settings as needed, ensuring compatibility and facilitating a smooth transition.

Benefits:

Making standalone components the default makes Angular's mental model simpler,
especially for new developers. In keeping with Angular's goal of streamlining devel-
opment workflows, this enhancement better supports features such as lazy loading
and component composition, and reduces boilerplate. Automated migration tools
make sure that existing projects can seamlessly adopt the new defaults, minimizing
disruption while providing a modern development experience.

Angular v19 | The Ultimate Guide to Angular Evolution 152


New useful migrations (injections, standalone API)

Dev Experience Efficiency

Challenge:

Keeping Angular projects up to date with the latest features and best practices
can be a daunting task. Especially for large or legacy codebases, manually refactoring
code to take advantage of new APIs, improve performance, or simplify the architecture
is a significant maintenance burden.

Solution:

A set of automated migrations in Angular helps developers transition smoothly


to modern features. These migrations are designed to address common refactoring
needs, reduce manual effort, and ensure a problem-free upgrade process.

Shift from constructor-based injection to inject() function:

ng g @angular/core:inject

This migration simplifies the code by replacing the constructor syntax with a more
streamlined approach:

// before
constructor(private productService: ProductService) {}

// after
private productService = inject(ProductService);

After migration, you may encounter compilation issues, especially in tests that di-
rectly create instances of injectables. The migration utility provides several options
to customize the process, such as how to handle abstract classes, how to maintain
backward-compatible constructors, and how to manage nullable settings to ensure
a smooth transition without breaking existing code.

Angular v19 | The Ultimate Guide to Angular Evolution 153


Lazy loading of standalone components in routing configuration:

ng g @angular/core:route-lazy-loading

This migration changes direct references to components into dynamic imports


(lazy loading):

// before
{
path: 'products',
component: ProductsComponent
}
// after
{
path: 'products',
loadComponent: () => import('./products.component').then(m => m.ProductsComponent)
}

The migrations for signal inputs, outputs, and queries make Angular more responsive
by converting traditional input properties, event outputs, and query fields to their mod-
ern, signal-based counterparts. This improves code efficiency and maintainability.

ng generate @angular/core:signal-input-migration
ng generate @angular/core:output-migration
ng generate @angular/core:signal-queries-migration

Angular v19 | The Ultimate Guide to Angular Evolution 154


Example:

export class MyComponent {


// before
@Input() name: string|undefined = undefined;
@Output() someChange = new EventEmitter<string>();
@ContentChild('someRef') ref: ElementRef|undefined = undefined;
// after
name = input<string>();
someChange = output<string>();
ref = contentChild<ElementRef>('someRef');
}

Benefits:

These migrations dramatically increase developer productivity by simplifying


the adoption of the latest Angular features, such as the inject() function and stan-
dalone APIs. By automating refactoring tasks, they reduce errors, ensure code con-
sistency, and allow developers to focus on building modern, maintainable appli-
cations. Developers can focus on delivering new features and improving the user
experience instead of spending time on manual updates.

Strict standalone flag

Dev Experience Efficiency

Challenge:

Using Angular's standalone components, directives, and pipes consistently can be hard
in projects migrating from older versions or where developers are unfamiliar with stan-
dalone conventions. Without enforcement, non-standalone components can uninten-
tionally proliferate, leading to mixed patterns and increased complexity in maintaining
the codebase.

Solution:

Angular introduces the strictStandalone flag in angularCompilerOptions to enforce


the exclusive use of standalone components, directives, and pipes. By default, this flag
is set to false, to allow for gradual adoption. When enabled, this flag prohibits any com-
ponent, directive, or pipe from being explicitly marked as non-standalone. This ensures

Angular v19 | The Ultimate Guide to Angular Evolution 155


alignment with Angular's default standalone architecture, which was introduced in ver-
sion 19.

Configuration Example:

{
"angularCompilerOptions": {
"strictStandalone": true
}
}

Enabling this flag ensures that only standalone entities are used, reinforcing Angular’s
evolution toward a modular and simplified framework. Violations will result in a com-
piler error:

[ERROR] TS-992023: Only standalone components/directives are allowed when


'strictStandalone' is enabled. [plugin angular-compiler]

Benefits:

The strictStandalone flag simplifies project architecture by enforcing consistent usage


of standalone components, directives, and pipes. This reduces developer cognitive load,
eliminates legacy patterns, and aligns with Angular’s modern design principles. Enforc-
ing standalone usage keeps the codebase cleaner, easier to manage, and ready for ad-
vanced features like lazy coding or modular composition.

Initializer provider functions

Dev Experience

Challenge:

Setting up initializers in Angular has traditionally relied on using the APP_INITIALIZER,


ENVIRONMENT_INITIALIZER, and PLATFORM_INITIALIZER tokens. While functional, this ap-
proach often results in verbose and less readable code, making the configuration pro-
cess cumbersome, especially for complex projects. Developers need a simpler, more
intuitive way to manage application, environment, and platform initialization.

Angular v19 | The Ultimate Guide to Angular Evolution 156


Solution:

Angular v19 introduces new helper functions:

• provideAppInitializer
• provideEnvironmentInitializer
• providePlatformInitializer

These functions serve as syntactic sugar that simplifies the setup process for application,
environment, and platform-level initializers. By replacing traditional tokens, they provide
a cleaner, more readable, and more intuitive way to configure initialization logic.

Example:

export const appConfig: ApplicationConfig = {


providers: [
provideAppInitializer(() => {
console.log('app initialized');
})
]
};

In addition, Angular v19 includes a migration tool to help developers convert existing
initializers to this new format. This automation minimizes manual effort and facilitates
a smooth adoption of the updated approach.

Benefits:

New initializer provider features improve the readability and maintainability of initial-
izer setups by reducing boilerplate and simplifying configuration.

Angular v19 | The Ultimate Guide to Angular Evolution 157


New angular diagnostics

Dev Experience Efficiency

Challenge:

Maintaining clean, efficient, and bug-free Angular applications is a challenge, espe-


cially when subtle issues like unused imports or incorrect event bindings can go unno-
ticed during development. Traditional diagnostics can miss these nuanced problems,
leading to technical debt and inefficient applications over time.

Solution:

Angular’s Extended Diagnostics provide real-time code checks that go beyond stand-
ard errors and warnings to identify potential problems and enforce best practices.
In Angular 19, two new diagnostics extend this capability:

Uninvoked Functions: Flags instances where a function is used in an event binding but
isn’t called due to missing parentheses in the template. This helps ensure that func-
tions in event bindings are executed correctly, preventing runtime errors.

Example:

<!-- Incorrect -->


<button (click)="onClick">Click me</button>
<!-- Correct -->
<button (click)="onClick()">Click me</button>

Unused Standalone Imports:

Detects standalone components, directives, or pipes that are imported but not used
in the application. This maintains a clean codebase by prompting developers to re-
move unused imports.

Angular v19 | The Ultimate Guide to Angular Evolution 158


Example:

// Incorrect
@Component({
imports: [UnusedComponent],
template: '<div>hello</div>'
})
export class MyComponent {}
// Correct
@Component({
imports: [],
template: '<div>hello</div>'
})
export class MyComponent {}

We encourage you to explore and test other Angular diagnostics documented in the offi-
cial Angular documentation to further refine your projects and uphold strong code quality.

Benefits:

These new diagnostics improve the quality of your code by catching subtle prob-
lems early in the development process, reducing technical debt and run-time errors.
They ensure adherence to Angular’s best practices, resulting in cleaner, more main-
tainable applications.

Hot module replacement for ng serve

Dev Experience Efficiency

Challenge:

Developers often face workflow disruptions when making changes to styles or tem-
plates, because the Angular CLI traditionally requires a full page refresh for updates
to take effect. This process not only slows down the development cycle, but also results
in the loss of application state, breaking the flow of iterative design and debugging.

Solution:
Angular v19 introduces built-in support for Hot Module Replacement (HMR) for styles
and experimental support for template HMR, greatly improving the development expe-

Angular v19 | The Ultimate Guide to Angular Evolution 159


rience. With HMR, any changes made to styles or templates are compiled and patched
into the application in real time, without requiring a page refresh. This ensures that up-
dates are applied immediately while preserving the state of the application, resulting
in a faster and more seamless development workflow.

The HMR for styles is enabled by default. Simply modify the styles of a component, save
the changes, and see them immediately reflected in the browser without reloading
the page.

To enable HMR for templates, use the following command:

NG_HMR_TEMPLATES=1 ng serve

To disable HMR for development servers, use:

ng serve --no-hmr

Benefits:

Hot module replacement updates styles and templates in real time, cutting develop-
ment time without losing application state. This feature increases developer produc-
tivity by maintaining an uninterrupted workflow, speeding up iterations, and promoting
a more efficient debugging process.

New features in Angular Language Service

Dev Experience Efficiency

Challenge:

Angular developers rely heavily on the Angular Language Service (ALS) to guarantee
productive and bug-free development. However, keeping ALS up to date with the latest
features and supporting advanced functionalities has been a challenge. Developers
often lack auto-completion for unimported directives, and may struggle with unused
imports or migrating to newer APIs like signals.

Angular v19 | The Ultimate Guide to Angular Evolution 160


Solution:

The latest updates to the Angular Language Service bring a host of new features to en-
hance the development experience:

Diagnostics for Unused Standalone Imports: It automatically flags standalone compo-


nents, directives, or pipes that are imported but not used, helping developers maintain
a clean and efficient codebase.

Support for Signal Migrations: It provides refactoring tools for migrating @Input prop-
erties to signal inputs and updating queries to signal-based APIs, reducing the effort
required to adopt Angular’s modern features.

In-Template Autocompletion: It enables autocompletion for all directives, even those


that haven’t yet been imported, speeding up template development.

Refactoring Schematics: Integrates useful refactoring schematics directly into sup-


ported IDEs, streamlining the transition to updated Angular APIs and features.

Benefits:

The enhanced Angular Language Service significantly boosts developer productivi-


ty by providing real-time insights, intelligent diagnostics, and automated refactoring
tools. With these improvements, developers can now integrate the latest Angular fea-
tures more easily while ensuring clean, efficient code.

Auto-completion for non-imported directives further simplifies template authoring, re-


ducing errors and saving time. By integrating powerful tools into IDEs, the updated ALS
fosters a seamless and efficient development experience.

Angular v19 | The Ultimate Guide to Angular Evolution 161


Typescript/Node.js support

Angular v18.1 introduces support for TypeScript 5.5, while Angular v19.0 extends com-
patibility to TypeScript 5.6 and removes support for versions prior to 5.5.

Highlighted below are some notable feature updates.

Inferred Type Predicates (available in 5.5)

TypeScript automatically infers type predicates, narrowing types in places where


we previously had to explicitly define predicate.

const availableProducts = productIds


.map(id => productCatalog.get(id))
.filter(product => product !== undefined);

/* TypeScript now knows availableProducts are no longer considered as possibly undefined */


availableProducts.forEach(product => product.displayDetails());

Control Flow Narrowing for Constant Indexed Accesses (avail-


able in 5.5)

TypeScript can now narrow expressions such as obj[key] when both obj and key are ac-
tually constants.

function logUpperCase(key: string, dictionary:Record<string, unknown>): void {


if(typeof dictionary[key] === 'string') {
/* valid since ts 5.5 */
console.log(dictionary[key].toUpperCase());
}
}

Angular v19 | The Ultimate Guide to Angular Evolution 162


Disallowed Nullish and Truthy Checks (available in 5.6)

Typescript will throw an error when truthy or nullish checks always evaluate to true
(which is correct in terms of JS syntax, but usually implies some logical error).

The following examples will trigger an error:

if(/^[a-z]+$/) {
/* missing .test(value) call, regex itself is always truthy */
}

if (x => 0) {
/* "x => 0" is an arrow function, always truthy */
}

Isolated modules

Angular 18.2 introduced support for TypeScript’s isolatedModules, which can improve
production build times by up to 10% by allowing code transpilation through the bundle.
This optimizes TypeScript constructs and reduces Babel-based passes.

To enable isolatedModules support in your Angular project, update your TypeScript


configuration (tsconfig.json) as follows:

"compilerOptions": {
"isolatedModules": true
}

It applies a few extra restrictions, such as no cross-file type inference, allowing only
const enums to be exported, and forcing explicit declarations of type-only exports (us-
ing import type syntax).

Without isolatedModules, full type checking is performed for the entire codebase during
the compilation process. In contrast, with isolatedModules enabled, each file is com-
piled independently, and certain cross-file type analysis is skipped.

Angular v19 | The Ultimate Guide to Angular Evolution 163


The future
Angular continues to evolve with exciting new features and enhancements aimed at im-
proving performance, developer experience, and accessibility. The framework is focus-
ing on modernizing tools, streamlining workflows, and introducing innovative capabilities
to make building dynamic, scalable applications more efficient than ever.

We have a lot of features that are currently in the experimental phase and will sooner or later
become stable. In addition, let's take a look at some extra areas that seem very promising:

Signal Forms
Angular is exploring the use of signals in form management through the experimen-
tal Signal Forms API. This approach integrates reactive state management directly into
forms, enabling dynamic synchronization between form fields and templates. Signal
Forms aims to reduce boilerplate code, simplify cross-field validations, and enhance
flexibility by leveraging Angular’s signals. Although in its early stages, this feature shows
promise in streamlining complex form handling while aligning with Angular’s modern
development paradigm.

Early concepts for the potential API of this feature are already emerging, offering a glimpse
into how developers might interact with it in future releases:

const nameSchema = group({


first: field(''),
last: field('')
}).xlink({
last: (schema, form) => schema
.validate(value => form.first.$() === value() ? 'cannot be the same as your first name' : '')
})

The Ultimate Guide to Angular Evolution: version 14-19 164


Expert Opinion:

The Signal Forms might end the long-standing “battle” between Reactive Forms and Tem-
plate-Driven Forms in Angular. Many developers strongly prefer one approach over
the other. Both have their level of difficulty, which often increases when you need to divide
a form into smaller parts.

Working with Signals in forms also presented some challenges. In Reactive Forms, devel-
opers had to find patterns to integrate Signals. Converting `.valueChanges` to a Signal
using `toSignal` was a common step, but developers had to figure out how to use other
Signal features like `effect` within the form logic. While this seems like a nice solution/pat-
tern, there is no single way to do this, leading to inconsistent approaches across projects.

Angular's philosophy is to provide clear and well-established solutions. This approach


makes it easier for developers to understand and work on different Angular projects,
as they can rely on consistent patterns and APIs.

I believe Signal Forms will significantly improve the developer experience (DX). They will
make working with Signals within forms much easier and simplify the process of breaking
down large forms into smaller, more manageable components.

~ Fanis Prodromou
Google Developer Expert

Reactive Forms are still the best solution for most cases, but there are specific scenar-
ios where you need more control in the form, and a Signal form is open to this solu-
tion. In my opinion, this solution is a bit verbose in the steps to follow (create a signal
for every field and create the form to link every signal), but yes, you can have more
control and simplify some controls or checks in the form.
It will play a large part in the Angular Form world.

~ Luca Del Puppo


Google Developer Expert

The Ultimate Guide to Angular Evolution: version 14-19 165


Selectorless
Angular is considering ways to simplify the usage of standalone components by mak-
ing selectors optional. This would enable developers to use components or directives
in templates directly after importing them, removing the need to define selectors.
The aim is to reduce code complexity and make component integration more seam-
less. This feature is still under early development, with plans to gather feedback from
the community through a formal request for comments (RFC) once preliminary de-
signs are ready.

Zoneless Angular
Angular is actively advancing its zoneless capabilities to enhance performance and de-
veloper experience. In version 18, experimental support for zoneless change detection was
introduced, allowing developers to build applications without relying on Zone.js, thereby
improving performance and debugging. Version 19 expanded this support to server-side
rendering and provided tools to facilitate the creation of zoneless projects. The Angular
team is currently focusing on addressing remaining edge cases and gathering feed-
back to ensure a seamless transition before moving this feature to developer preview.
Developers can anticipate further refinements and broader adoption of zoneless change
detection in upcoming releases.

if you want to test zoneless now, you can do it as follows:

bootstrapApplication(App, {
providers: [
provideExperimentalZonelessChangeDetection()
]
});

Streamed server-side rendering


Streamed server-side rendering (SSR) is an advanced approach to delivering web appli-
cations where rendered content is sent to the client incrementally as it becomes avail-
able. Unlike traditional SSR, which waits for the entire page to render before sending
it, streamed SSR allows users to start interacting with the application sooner by progres-
sively loading visible parts of the page. This method reduces initial load times, enhances
interactivity, and is particularly beneficial for complex applications. Angular is actively
exploring streamed SSR to improve performance, especially in applications that do not
use Zone.js.

The Ultimate Guide to Angular Evolution: version 14-19 166


Expert Opinion:

In the future Angular SSR technique will be easier to implement, it will not require much
configuration and will provide hydration so that Angular apps will be able to resume
at some point.

~ Aristeidis Bampakos
Google Developer Expert

Overall
The future of the Angular framework is focused on embracing modern web develop-
ment paradigms, prioritizing performance, and simplifying developer workflows. Key ef-
forts include advancing reactive state management with signals, enhancing server-side
rendering capabilities through streaming and hydration, and reducing boilerplate with
features like selectorless components. Angular is committed to evolving as a versa-
tile and developer-friendly framework, integrating community feedback and adapting
to emerging trends in web technology.

The Ultimate Guide to Angular Evolution: version 14-19 167


What the experts
are saying
How important is it to stay up-to-date with all the
latest changes in Angular, given the current pace
of changes?
Nowadays, Angular is making rapid changes, by introducing standalone APIs, signals,
new control flow, etc. Although it is completely possible to not adapt the new changes
and for example, keep using NgModules because of the backwards compatibility, it is still
recommended to adapt to the changes, because there are, and will be even more, fea-
tures only available for newer APIs. A great example is host directives, which are only
possible with standalone APIs. But don’t worry about refactoring, because Angular usually
delivers migration schematics to migrate to standalone APIs, or to the new control flow.

~ Stefan Haas
Nx Champion

In this context, a rational approach is derived from the observed behavior of the ma-
jority of clients I have encountered. Within the organizations I have collaborated with,
those employing Angular-based applications exhibit a certain reluctance when it comes
to embracing the latest Angular versions. Their hesitancy arises from their search
for compelling reasons to justify such upgrades. At the core of this deliberation lies
the end consumer, the user, and their user experience.

For instance, take the case of Angular 15's Standalone API. Its implementation may not
manifest any discernible impact on the end user; it could be argued that its primary
effects are confined to enhancing the developer experience, and even this judgment
is inherently subjective. It's important to acknowledge the formidable challenge posed
by the task of updating huge applications that lack comprehensive test coverage. The
absence of robust testing engenders substantial risks, rendering the application more
susceptible to vulnerability in the face of such alterations.

~ Artur Androsovych
Angular Expert

In my opinion, it is very important. Especially in times of dynamic changes, I think


the strategy of frequent and small updates is much more beneficial than waiting un-
til everything settles down. I always invest time to keep my Angular projects updated
to benefit from new Angular features and improvements. New features bring new patterns

The Ultimate Guide to Angular Evolution: version 14-19 168


and unleash better architectural seditions. Also, a simple update can often bring you
performance, security, and bundle size improvements for free.

~ Dmytro Mezhenskyi
Google Developer Expert

I know with all the exciting new changes coming out, you may be excited to embrace
the latest and greatest. And, pragmatically, I think it's a great way to learn for personal
projects or small isolated projects, but when it comes to larger projects involving a lot
of developers, I'd urge you to hold back for 2 things: For your team to feel comfortable
discussing the change and taking on the work, and for good documentation and best
practices to be established. Some of the new concepts are just the beginning of bigger
things and we may find reasons to tweak, iterate, or pivot. No need to chase shiny as the
goal. On the other hand, Angular has also released security updates, enhancements,
and updating their dependencies to ensure the Angular projects we create are secure
in recent versions. It just doesn't get all the attention some of the bigger changes get.
It's vital to update Angular and dependencies following security guidelines and when
enhanced security features are released.

~ J. Alisa Duncan
Google Developer Expert

If your current project is based on Angular 10 or an earlier version, it may start to feel
outdated within the rapidly evolving framework landscape. The community
is continually striving to stay up-to-date, and you'll often find that answers
on platforms like StackOverflow and other websites revolve around the latest Angular
frameworks.

Therefore, it is crucial to consider regularly upgrading your enterprise application to a ver-


sion close to the latest release. From a practical standpoint, I recommend maintaining
a gap of -1 or -2 Angular versions from the current one. For instance, if Angular 16 is the
current version with Angular 17 on the horizon, I advise companies to upgrade their
projects to at least Angular 14 or 15. This approach allows ample time for many enter-
prise projects with dependencies on various Angular libraries, both open-source
and commercial, to catch up and align their libraries with the most recent developments.

~ Balram Chavan
Google Developer Expert

I think it's more important than ever to stay up-to-date and try to adopt every new major
version of the framework as it is released. That way, you won't feel overwhelmed in a year
or two, making it even more difficult to catch up.

~ Alain Chautard
Google Developer Expert

The Ultimate Guide to Angular Evolution: version 14-19 169


The innovation phase in Angular is still ongoing. It will end at some point and we will have
another stable phase, but I expect that the innovation trend will continue this year and
in 2025. Significant new features are likely to include selector-less and signal compo-
nents, a new signal-based form feature, zoneless partial hydration, and probably a Http-
Client without Observables.

Upgrading is about gaining access to new features and productivity improvements


(e.g. the new API (input, output)). With Signals and the new upcoming APIs, a codebase
pre-dating Angular 14 is currently outdated.

Tooling is also essential. Built tools are becoming faster and better, and often tend to de-
pend on a modern codebase.

Another issue with an outdated codebase is the difficulty of onboarding new developers.
They might not have heard about NgModules, have been trained to work with Signals,
and struggle with OnChanges, @Input decorators, etc.

Staying up to date with the latest version of Angular is crucial.


However, there are also other criteria to consider. The main one is available resources.
One may not be able to modify/rewrite huge code bases with every new release.

Most companies will find themselves in the middle. This means trying to make use of An-
gular's new features when they implement a new feature themselves or when refactoring
parts of their app. That would be an incremental migration with different layers of "Angu-
lar generations." It's a compromise, but it's always better than sticking to the old waysor
doing a big-bang migration.

~ Rainer Hahnekamp
Google Developer Expert

You have to be up-to-date with all the frameworks that you want to choose.
Sometimes it's difficult to explain this to the business, but with a few small steps you can
make it happen.

What we can do in my team is basically use the Dependabot. Within a minor release
or a patch, we have a CI, and the Dependabot will try to update the library. And if the
CI passes and it's green, it's an auto-merge in the codebase. This is fantastic. When you
have a major update, you need to schedule it, as it's normal to spend some time upgrad-
ing. However, if you spend three days upgrading to the major version, you'll benefit from
all the code-based updates and can utilize the latest features. This not only helps your
current team members but also makes it easier to integrate new developers. Conversely,
if you lose the ability to upgrade to the latest version, you risk losing valuable talent from
your team.
.
~ Luca del Puppo
Google Developer Expert

The Ultimate Guide to Angular Evolution: version 14-19 170


How do the overall changes affect the learning
curve? Is it easier to learn Angular in its latest form?
I think that in the future it will definitely become easier. Before, everyone who came to try
Angular was hit immediately by complex concepts like RxJS or NgModules. Now, the Now,
the learning curve has become more gentle and you have to learn less to build the sim-
plest Angular app. Nowadays, we experience a transition period which is hard for new-
comers because they have to learn the old stuff as well as new ones but I think in a year
or two things will settle down.

~ Dmytro Mezhenskyi
Google Developer Expert

This question is somewhat complex to answer. If you're just starting to learn Angular now,
the process is considerably easier than it was a few years ago. This is because most of the
popular web frameworks have embraced TypeScript, and Angular is not the only framework
advocating for a typed approach to web development. Notably, features like standalone com-
ponents, simplified routing APIs, the introduction of new control flow syntax, and improved error
reporting have made it more accessible for newcomers to dive into the Angular ecosystem.

In addition, the Angular documentation has grown significantly, offering a wealth of tutori-
als and advanced concepts. There are official cheat sheets, resources to address common
errors and their solutions, an open Discord channel for developers to connect, and a range
of Google-sponsored and community-driven events that enable developers to network
and learn from one another.

However, it's worth noting that many experienced developers have expressed concerns
about the rapid pace of change and the introduction of new features. This sometimes re-
quires them to revisit and potentially rewrite their existing projects, as well as continually
learn and adapt to stay current in the Angular landscape.

~ Balram Chavan
Google Developer Expert

I have been able to collect a lot of feedback from my students over the last few years.
RxJs and the app structure with the modules were often described as too complicated.
Little by little, we are eliminating these pain points
.
~ David Muellerchen
Google Developer Expert

The learning curve will be flat for new beginners because they won’t have to go through
the same pains that experienced developers went through.

When experienced Angular developers learned the old version of Angular, they had
to know NgModule and import it to AppModule before using Angular components.
To write reactive code, they used Subject to store the state, subscribed the Subject to re-
solve the Observable, and finally unsubscribed the subscription to avoid a memory leak.
These are a few examples of how Angular intimidated beginners in the old days.

Fast forward to 2023, the difficulties of building Angular applications are significantly
lower. We can learn standalone components and use them as the building blocks

The Ultimate Guide to Angular Evolution: version 14-19 171


of building Angular applications are significantly lower. We can learn standalone com-
ponents and use them as the building blocks of an application. We can store state in Sig-
nal and call APIs to set, update and compute to calculate Signal value. If the require-
ments require an HTTP request or asynchronous reactivity, developers can learn RxJS
and the minimal operators. Learning RxJS can move from the top learning list to a lower
position. toSignal and toObservable RxJS-interop can convert between Signal and Ob-
servable but they must be used with discretion.
With the new control flow, we should learn @if, @for and @switch instead of the di-
rective counterparts. From my personal experience, @if is easier than ngIf when there
are 2 or more else-if. I cannot find a precise and elegant way with ngIf, ngElseIf and ngElse
directives. ngFor has trackBy, but it is not compulsory to use whereas @for makes track
mandatory and can be used to optimize a long list.

~ Connie Leung
Google Developer Expert

Wow, I feel bad for new Angular developers. They must feel like there's quite a dichotomy.
I think the difficulty in learning changes from the learner's past experience. If they
are already familiar with the web frameworks out there, they may prefer the new
changes, and if they are .NET devs, they may prefer the prior form of Angular. New devs
who have to learn how to maintain legacy Angular projects while simultaneously learning
new changes for new work are the ones I feel the most for. That said, while learning can
cause discomfort for all of us, I'm looking forward to these changes, how the framework
grows, and seeing all the cool things we can do with these features.

~ J. Alisa Duncan
Google Developer Expert

Angular’s learning curve used to be steep. Newcomers had to learn how to handle
the modules, how to work with the structural directives, how to extract the required info
from the error stack trace, and learn how to master RxJS.

The latest features and improvements made these hurdles easy to overcome.
The modules are no longer needed as an application can be standalone.
The structural directives have been replaced with the new control flow and as a result
the developer experience has been improved a lot!
The error stack trace provides useful information and it's easier to understand where
an error is coming from.
Last but not least, the presence of signals means that mastering RxJS is no longer
a necessity.

~ Fanis Prodromou
Google Developer Expert

The Ultimate Guide to Angular Evolution: version 14-19 172


What are your expectations for the direction
and pace of change in Angular in the future?
In the next few versions, I expect that changes in the framework will reduce a lot of boil-
erplate code, improve the developer experience and apply ergonomic approaches when
it comes to building Angular apps.

~ Aristeidis Bampakos
Google Developer Expert

The Angular team is highly responsive to community feedback, and their recent additions
reflect their commitment to improving the framework. Features like standalone
components, the inject() function for dependency injection, functional guard routes,
and the introduction of new control flow syntax for directives like ngFor and ngIf
demonstrate the team's endeavor to incorporate the best practices from other popular
frameworks such as React, Svelte, and Vue. Furthermore, the Angular team is investing
more effort into areas like Server-Side Rendering, Hydration, SEO optimization,
integrating ESBuild, engaging in discussions around Microfrontends, and more.
These initiatives clearly indicate that there are exciting developments on the horizon
for Angular.

~ Balram Chavan,
Google Developer Expert

There is a brighter future for Angular with all the great provided features
and improvements since v14 to v17. It’s the renaissance of Angular. These up-
dates bring significant improvements and simplifications, such as standalone compo-
nents, the removal of the need for modules, and the introduction of functional guards.
The latest versions introduce powerful features, including built-in control flow, en-
hanced performance in v17, and deferred loading, which allows you to achieve a lot with
minimal code. The Angular team has hinted at a big surprise in v17, I won't spoil it (ha-
hah), but this surprise will for sure make learning Angular easier and greatly enhance the
learning experience.

~ Fatima Amzil
Google Developer Expert

It is the best time to be an Angular developer, and the Angular team works tirelessly
to include new and exciting features in each release. Angular 17 has not come out yet,
and major Angular podcasters have already scheduled meetings with Angular team
members to discuss Angular 18 and beyond.

~ Connie Leung
Google Developer Expert

The Ultimate Guide to Angular Evolution: version 14-19 173


What is your most anticipated feature
in the Angular Roadmap?
Signal-based change detection (and APIs) will be a game-changer. Moving the form
and router APIs to signal-based would remove the need for RxJS entirely, making Angular
much easier to learn. As a person who teaches Angular regularly, this will make my life
easier!

~ Alain Chautard
Google Developer Expert

When you need to develop a reusable component, you can use different techniques
and different angular features. One of them is the content projection where you can have
either a single slot or multiple slots.

There are many cases where you need to have default content since you are not always
sure that the consumer of that component will project content to that slot. Content pro-
jection with fallback content makes things much easier!

~ Fanis Prodromou
Google Developer Expert

Partial Hydration. It’s the feature that will directly affect our app users experience.

~ Enea Jahollari
Google Developer Expert

The Ultimate Guide to Angular Evolution: version 14-19 174


Thank You
We hope you found this ebook informative and that its contents will help you improve
your app’s performance, user experience and overall project efficiency.

If you’d like to upgrade the Angular version in your project but don’t know where to start,
or if you have any questions about Angular or its features, we would be happy to help.

Book a 30-minute meeting with an ebook author to discuss your challenges or just
contact us by e-mail: [email protected]

The Ultimate Guide to Angular Evolution: version 14-19 175


Consulting and audit
Identifying bottlenecks within an Angular project can be challenging. Why can’t your
product scale, and new team members don’t increase the project's velocity?

This is where expert advice can make a significant difference.

Our team at House of Angular is passionate about Angular. We have worked with the
framework since its release and will happily share our knowledge and experience with
you through audits or consulting.

How the audit can benefit your project

An audit of the code can help you identify vulnerabilities within the code and figure
out how to deal with them. It will also help you determine how to leverage Angular’s
new features in your project effectively. Our experienced Angular Developers will give
you recommendations in four key areas:

• Code quality
• Functionality
• Architecture
• Performance

You will receive a detailed report and get access to:


• A list of places that should be improved, sorted by priority and complexity
• Identified risks when it comes to long-term development
• Recommendations about tools and libraries your team should use
• Best Angular practices that will benefit the project

If you’d like to find out whether the audit or consulting will address the challenges in
your Angular project, just fill out the contact form. We will schedule the first 30-minute
talk just to discuss your project.

Contact us

The Ultimate Guide to Angular Evolution: version 14-19 176


Media partners

WeareDevelopers

The WeAreDevelopers World Congress in Berlin from July 9th


to 11th, 2025, unites 15,000 software developers, decision-mak-
ers, and experts for discussions on software trends with 500+
speakers. Topics include software development, cloud com-
puting, AI, software architecture, DevOps, and emerging
tech for networking and growth. Noteworthy speakers in-
clude Prashanth Chandrasekar, CEO of Stack Overflow, Scott
Hanselman, VP of Developer Community at Microsoft, Thomas
Dohmke, CEO of GitHub, Jonas Andrulis, Founder & CEO of Aleph
Alpha, David Heinemeier Hansson and many more.

WaysConf
WaysConf, the largest event for creators of digital products
in Central and Eastern Europe, is set to take place on Sep-
tember 17-18, 2025. WaysConf has evolved to embrace pro-
fessionals from diverse backgrounds, including researchers,
designers, developers, and managers.

The Ultimate Guide to Angular Evolution: version 14-19 177


Bibliography
Official Typescript Documentation: https://www.typescriptlang.org/
Official Angular Documentation (old): https://angular.io/
Official Angular Documentation (new): https://angular.dev/
Official Angular blog: https://blog.angular.dev/
Angular.love blog: https://angular.love/

About authors
Main Authors
Mateusz Dobrowolski
Angular Developer at House of Angular. Key Contributor to Angular.love. He takes an
active part in the angular.love community, writing expert articles, and sharing his
knowledge at Angular meetups.

If you found this e-book useful (or not) please, let us know. Send your feedack to the
author: @m-dobrowolski

Mateusz Stefańczyk
Angular Team Leader a House of Angular. Key Contributor to Angular.love. For 7 years,
he has been developing web applications with Angular. He has performed dozens of
audits for Angular projects worldwide. Mateusz actively participates in the angular.love
community, writing expert articles, and sharing his knowledge at Angular meetups in
Poland, Norway, Germany, and the UK.

If you found this e-book useful (or not) please, let us know. Send your feedack to the
author: @m-stefanczyk

Special thanks to
Krzysztof Skorupka
Angular Team Leader & Architect at House of Angular. Key Contributor at Angular.love
community, blogger, and speaker.
Przemysław Łęczycki
Angular Team Leader at House of Angular. Expert at Angular.love community, blogger,
and speaker.
Dominik Donoch
Angular Developer at House of Angular. Key Contributor Angular.love.

The Ultimate Guide to Angular Evolution: version 14-19 178


Dawid Kostka
Angular Developer at House of Angular. Angular.love Contributor.
Miłosz Rutkowski
Angular Developer at House of Angular. Angular.love Contributor.
Piotr Wiórek
Blogger and member of Angular.love community.
Mateusz Basiński
Angular Developer at House of Angular. Angular.love blogger.
Maja Hendzel
Angular Developer at House of Angular. Angular.love blogger.

Invited experts
Fatima Amzil
Fatima is a cross-functional frontend technical leader and an ex-
pert in Angular. Outside of her professional life, she contributes as a tech-
nical writer on Medium, mentor at MyJobGlasses, and an Angular GDE.

Artur Androsovych
Artur is an OSS core developer of various projects as NGXS, NG-ZORRO, single-spa, ng-
neat (until-destroy code owner), and RxAngular. He was a Google Developer Expert
in Angular in 2023. He focuses on runtime performance and has taught teams about
Angular internals for the past few years.

Aristeidis Bampakos
Aristeidis is an Angular GDE who works as Web Development Team Lead at Plex-Earth.
He is an award-winning author of Learning Angular and Angular Projects books. Cur-
rently, he is leading the effort of making Angular accessible to the Greek develop-
ment community by maintaining the Greek translation of the official Angular docu-
mentation.

Alain Chautard
Alain is a Google Developer Expert in Angular, and Google Maps. His daily mission is to
help development teams adopt Angular and build at scale with the framework. He has
taught Angular on all six continents!

Balram Chavan
Balram is an Architect specializing in designing and creating scalable, secure solutions
for various domains. His expertise spans the cloud, AI and web development land-
scape. As the author of an Angular framework book, he excels as a team player, an
effective communicator, and a mentor.

Luca Del Puppo


Luca is a Senior Software Developer, Microsoft MVP, Google Developer Expert and Git-
Kraken Ambassador. He loves JavaScript and TypeScript. In his free time, I loves study-
ing new technologies, improving himself, creating YouTube content or writing technical
articles.

J. Alisa Duncan
Alisa is a Senior Developer Advocate at Okta, full-stack developer, content creator,
conference speaker, Pluralsight author, and community builder who loves the thrill of
learning new things. She is a Google Developer Expert in Angular, a Women Techmaker
Ambassador, a ngGirls core team member, and a volunteer at community events sup-
porting underrepresented groups entering tech.

The Ultimate Guide to Angular Evolution: version 14-19 179


Stefan Haas
Stefan is a freelancer and trainer focusing on Angular from Austria. He is the author
of NG Journal - The Place Beyond Fundamentals - and an Nx Champion.

Rainer Hahnekamp
Rainer is a Google Developer Expert, working as a trainer and consultant in the expert
network of Angular Architects. In addition, he offers a weekly brief overview of relevant
events in the Angular ecosystem on YouTube through ng-news.

Enea Jahollari
Enea is a Google Developer Expert for Angular, Consultant, Trainer & Senior Software
Engineer who builds, audits and optimizes web applications @ Push-Based.io. He loves
open source and contributes to it by writing code, content, and tweets! Loves hyping
Angular in his free time.

Connie Leung
Google Developer Expert for Angular. Software Architect at Diginex, blogger
and youtube content creator.

Dmytro Mezhenskyi
Dmytro Mezhenskyi is the founder of the Decoded Frontend YouTube channel, where
he shares his knowledge about the Angular framework. As an author, he has created a
series of advanced video courses focusing on Angular and GraphQL. With over 10 years
of experience in frontend development, Dmytro has been recognized as a Google De-
veloper Expert in Angular and a Microsoft MVP in Web Development.

David Muellerchen
Better known as WebDave, David has been an integral part of the Angular community
since 2014. He is a Google Developer Expert, an Angular consultant and trainer, and
a frequent speaker at meetups and conferences. He also organizes weekly Angular
livestreams and the Hamburg Angular Meetup.

Fanis Prodromou
Fanis is a Senior Front-End Developer, Google Developer Expert for Angular, and Con-
tent Creator with a passion for sharing his knowledge and helping other developers.
He has developed vast experience in code quality, application architecture, and appli-
cation performance. Fanis is also a strong advocate for staying up-to-date on the lat-
est technologies, and regularly attends conferences and meetups to learn from other
experts.

Marko Stanimirović
Marko is a Principal Frontend Engineer at Swiss Marketplace Group. He is also a core
member of the NgRx and AnalogJS teams, a Google Developer Expert in Angular, and
an organizer of the Angular Belgrade group.

The Ultimate Guide to Angular Evolution: version 14-19 180


House of Angular is a software agency with a focus on developing and supporting
Angular applications. They offer a variety of services, including:
• application development,
• team extension,
• product and team audits,
• and business consulting.
Having worked with Angular since its beginning and with over 10 years in app development,
they have become a trusted business partner for companies of different sizes and sectors.

Their goal is to craft the finest frontend solutions using Angular, while also imparting their
knowledge and expertise to other users of this framework.

The company is actively involved in nurturing the Angular community. They support key
Angular libraries like ngrx, NGXS, and ngneat. Additionally, their team members contribute
to open-source Angular projects and spread their expertise through writing articles and
giving talks at European meetups.

House of Angular has been recognized for their contributions with the Angular Hero of
Community 2021 award.

Angular.Love is a community platform for Angular enthusiasts, supported by House of


Angular to facilitate the growth of Angular developers through knowledge-sharing
initiatives. It started as a blog where experts published articles about Angular news,
features, and best practices. Now angular.love also organizes in-person and online
meetups, which frequently feature Google Developer Experts.

Angular developers can find expert insights and expand their Angular knowledge by
reading the angular.love blog, attending meetups with talks from experts, and following
the community platform’s social media pages.

Angular.love community is a recipient of the Angular Hero of Education 2022 award.

Blog | Meetups | YouTube | X (formerly Twitter)

The Ultimate Guide to Angular Evolution: version 14-19 181

Вам также может понравиться