Back to the homepage
Angular

Angular 16: What’s new?

Angular is gaining momentum we’ve never seen before. In the intro of the last article of this series, we pointed out that both Angular 14 and 15 introduced a lot of new ideas and features, bringing a breath of freshness to the framework. Now, when we are looking at version 16, we can clearly see that such an impressive pace of Angular features development has become a standard. Believe it or not, there are even more ground-breaking features released in NG 16 than in previous versions. Let’s check out the most important bits.

Signals in Angular 16

There is no doubt that signals are the hottest feature area in the newest release of Angular. The topic of signals is very broad, and we will devote a lot of content to it on our blog. That’s why I won’t go into details right now, but instead, I’ll briefly summarize what signals are and why they’re so important.

The concept of signals is not completely new. Some other frameworks, like Solid.js, have already been using them for a long time, so they’re just new to the Angular world.  

You may have already heard that signals are the new “reactive primitive” in Angular, and are wondering what that actually means. You can think of a signal as the basic unit on which the reactivity system is based. A signal is similar to a standard variable in the sense that it can hold a value, which you can always access synchronously. However, apart from that, when the underlying value changes, a signal is able to notify all the nodes that depend on this signal. It can be a component template read, another (computed) signal, a special effect function, and many other items. Thus we get a powerful item that is capable of notifying others of its value changes, creating a derived state in a declarative way and at the same time allowing synchronous reads with a great performance baked in. Isn’t that cool?

So why are signals so important? Mostly because of the great impact they can (and certainly will) have on the other features of the framework. Right now Angular strongly relies on zone.js when it comes to change detection. With signals, the reactivity system will look totally different as the change detection layer will be able to rely on signal notifications and then trigger re-rendering. Here we come to another vital point – technically zone.js just tells the framework that something might have changed. With signals, the framework will not only know if something really changed, but also what exactly changed. This means we can have per-component (or even more granular) change detection. These are just some highlights of signals, but there are many more areas that can be affected, e.g. state management libraries adopting signals and building new solutions on top of them.

At this point, it’s worth noting that signals are optional, so you can still use Angular like you used to. If you are also wondering if Angular is abandoning RxJS, that’s not the case. It is staying and will interoperate with signals.  

If you would like to learn more about signals, please stay tuned as we are already working on the first article on this topic. In the meantime we highly encourage you to check the official RFC. This is where the Angular team has  described the initial design of signals along with the reasoning behind their decisions and, equally important, where everyone can ask questions and discuss the final solutions. RFC is split into four parts: why signals as the reactive primitive, signals API, signal-based components, and interoperability between the signals and observables. You can find the links in the main document linked above.

Server-side rendering

Server-side rendering is the next area where we got some serious improvements in Angular 16. So far, Angular apps working with SSR were using destructive hydration. This means that the server renders the app, we get it on the screen, and then when the client app gets downloaded and bootstrapped, it completely destroys the DOM being already in place and rerenders the client app from scratch. This has some significant drawbacks such as screen flickering and negatively impacts some Core Web Vitals such as LCP or CLS. There are some workarounds that can be applied in order to reduce the negative effects of destructive hydration, but they are far from perfect and don’t resolve the issue itself.

Angular 16 adds support for non-destructive hydration. This approach is much better: the server renders the app, we get it on the screen, and then when the client app gets downloaded and bootstrapped, it reuses the DOM being already in place and enriches it with client-side capabilities, such as event listeners. 

Non-destructive hydration is available as a developer preview, but you can try it today. Just add provideClientHydration() to the providers when bootstrapping the standalone app:

or in the case of a module-based app, add the same provider to your root module (usually AppModule).

There is also an option to skip hydration for some components (or rather component trees) if they’re not compatible with hydration (e.g. manipulating DOM directly with browser APIs). You can use either:

or

There are also some other improvements added for SSR. These include caching HTTP requests made on the server to reuse them on the client – the implementation is now part of HttpClient itself and this mechanism can be activated by using withTransferCache function. Another one is provideServerRendering function that sets up providers necessary to enable SSR for the standalone app (ServerModule counterpart).

Required Inputs

One of the long-awaited features is being able to mark inputs as required. Until Angular 16,it was not possible to do so, but there was a common workaround for this using a component selector:

Unfortunately, this is far from ideal. The first thing is that we pollute the component selector. We always have to put all required input names to the selector, which is especially problematic during refactors. It will also result in a malfunction of the auto-importing mechanisms in IDEs. And second, when we forget to provide the value for an input marked this way, the error is not very accurate (as there is no match at all for such “incomplete” selector):

Error not known element

The new feature fills this gap and allows us to explicitly mark the input as required, either in the @Input decorator:

or the @Component decorator inputs array:

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 thing is that it still suffers from the strictPropertyInitialization TypeScript compilation flag which is enabled by default in Angular (this is recommended and useful). TypeScript will raise an error for this property as it was declared as non-nullable but not initialized in the constructor or inline.

property not initialized error

So 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:

Router Inputs

Another interesting feature related to component inputs is the ability to bind them directly to the current route variables – it can be path params, query params, etc. This eliminates the need to inject ActivatedRoute into the component in order to use router data.

Speaking of implementation details, data is being bound only to the routable components (present in 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), so it can occur that 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:

  1. resolved route data
  2. static data
  3. optional/matrix params
  4. path params
  5. query params

It looks like there is much more complexity if we consider “inheriting” data from the parent route configuration, parent component presence, etc. You can play with the following live example we’ve prepared to test more scenarios:

esbuild dev server

Version 14 of Angular brought an esbuild-based Angular builder, but it only works for production-like builds (ng build) and not for the development server (ng serve). In Angular 16, we also get support for the latter. The new dev server is powered by Vite. However, it still uses esbuild to build the artifacts. Vite acts just as a server, so this means the build process doesn’t use the full potential of Vite. Despite this it’s a great step forward as we can still benefit from esbuild’s performance gains, and maybe there will be more of Vite involved in the future.

Esbuild-based ng build and ng serve both remain experimental. If you want to try them, just change the build target for your project in angular.json so it uses esbuild (no need to update anything in serve target):

Other notable changes in Angular 16

There are so many changes coming in Angular 16, we cannot list them all. But here are some more examples:

  • ngcc (Angular Compatibility Compiler) has been removed. Angular View Engine libraries will no longer work in Ivy projects.
  • DestroyRef concept was introduced. It’s an injection token tied to the lifecycle scope of a component/directive/injector. It allows us to register a callback which is called when the related scope is destroyed.

  • takeUntilDestroyed RxJS operator was implemented in the framework itself. As a result, we no longer need to reach for third-party solutions. The implementation is based on DestroyRef.
  • TypeScript 4.8 is no longer supported, and we get TypeScript 5.0 support. This is interesting, as version 5.0 implements ECMAScript Decorators. In the past, decorators in Angular relied on so-called “experimental decorators” which was a TypeScript custom implementation before TC39 decided on a decorators standard. From now on, we can switch experimentalDecorators off in our Angular projects, and decorators should still work but will be based on the standard implementation. There is only one exception to this – decorators used in the constructor parameters will not work, as the standard doesn’t support this:


    So we need to use the inject function instead:

  • A number of schematics are updated to support standalone apps: ng-new & application schematic, Angular Universal schematic, or app-shell schematic.
  • With Angular Material being based on MDC as of Angular 15, for version 16, the team was working on adopting design tokens provided by Material Design. Thanks to that, they can migrate to Angular Material 3 more easily in the future and allow the designers and developers to highly customize the UI of their apps.

What’s next after Angular 16?

Given the large set of features delivered in Angular 16, we can expect that the trend will be continued. And speaking of focus areas of Angular 17 and further, there are two sources we can base our predictions on. The first remains invariably the roadmap. The second one is, in fact, a combo of multiple streams/podcasts with Angular team members (like https://www.youtube.com/watch?v=s9ZFyMkDPmg or https://www.youtube.com/watch?v=aXfCNbU-9EY).  Putting it all together, we can expect a further focus on developer experience and performance, but also it seems that most efforts will be focused around:

  • Signals (reactivity, signal-based components, local change detection, zoneless approach, interoperability between RxJS and signals),
  • SSR (partial hydration, resumability),
  • esbuild builders (missing features),
  • cooperation with Angular Material around Material 3 support.

Summary

As you can see, there is so much happening in Angular that it is barely possible to keep up with all the new features. Fortunately, the Angular team cares not only about better developer experience but also about backwards compatibility. This means everyone can adopt these changes at their own pace, and I hope that in the end, we all benefit from them. 

What’s your opinion? Do you like the direction and rate of change that the framework has taken? We’d highly appreciate your input in the comments.

 

About the author

Krzysztof Skorupka

Angular Team Leader at House of Angular. Krzysztof guides team members and helps them develop a senior software engineer skill set, while developing Angular web apps by himself. He is interested in business analysis and architecture of web apps, adapted to client needs.

Don’t miss anything! Subscribe to our newsletter. Stay up-to-date with the latest trends, tips, meetups, courses and be a part of a thriving community. The job market appreciates community members.

Leave a Reply

Your email address will not be published. Required fields are marked *