Angular 10 in Depth

Everything you need to know about Angular 10

Angular 10 in Depth

NEWS: Angular 11 is out. Check out my article to learn everything about it.

In this article, I’ll go over (almost) everything noteworthy in this brand new release. I’ll also highlight what’s changed around Angular.

If you want an helicopter view of what’s included, then check out the official Angular blog. Here, I’ll try to dig deeper into the release notes.

Angular 10 is already here, just four months after version 9. Of course during this short time period, there’s not that much that has changed. Still, there are quite a few noteworthy features, in addition to the large number of bug fixes brought by this release.

As a reminder, the Angular team tries to release two major versions per year, so Angular 11 should arrive this fall.

Support for TypeScript 3.9.x

What can I say? You know me, I love TypeScript. So the very first thing that makes me happy about this release of Angular is the fact that it supports TypeScript 3.9.

I’ve already published an article about the new features of TS 3.9, so if you didn’t read it, go ahead and upgrade asap, it’s really worth it! I have also written another one about what’s coming with TypeScript 4.0.

Note that Angular 10 has dropped support for TS 3.6, 3.7 and 3.8! I hope that it won’t hold you back.

Thanks to its support for TS 3.9.x and other improvements in the compiler CLI, type-checking is faster than ever in Angular 10, which should be positive for most projects out there; especially larger ones.

Aside from that, Angular 10 also upgraded to TSLib 2.0. For those who don’t know, TSLib is an official library providing TypeScript helper functions that can be used at runtime. TSLib works in combination with the importHelpers flag of “tsconfig.json”; when enabled, it allows the compiler to generate more condensed/readable code. Anyways, nothing to worry about; TSLib hasn’t changed much..

Optional stricter settings

Strict mode for the win!

Angular 10 brings the possibility to create stricter projects right at creation time, which is great and should certainly be used for all new projects. To create a project with stricter defaults, use:

ng new --strict

This will allow you to detect issues much sooner (finding out about bugs at build time is better than at runtime, right?).

This new option enables TypeScript strict mode (which you should all enable on your projects!).

Next to that, it also enables strict Angular template type checking, which I wrote about last week.

It also lowers the budgets in “angular.json” quite drastically:

This is good as it will encourage new users to pay attention to the bundle size of their applications (about that, I’m planning an article on how to analyze the bundle size of your apps).

It also enforces a stricter TSLint configuration which bans “any” (“no-any” is set to true), and also enables quite a few interesting rules provided by codelyzer. Note that even though strict, you can still go much further with TSLint. For instance, here’s the config of one of my projects, which you can use as starting point.

I think that this new “strict” option is awesome, but am a bit sad that it isn’t the default rather than an optional flag. I feel like stricter means safer, so why make safer optional? I imagine that the rationale is that by being more lenient by default, Angular feels less scary at first?

Anyways, if you do create a new project, please enable this and go even further; you’ll thank me later.

New TypeScript configuration layout

With this new release, the TypeScript configuration provided by default in new projects has changed. There’s now a “tsconfig.base.json” file in addition to “tsconfig.json”, “tsconfig.app.json” and “tsconfig.spec.json”.

So why all these configuration files? To better support the way IDEs and build tools look up the types and compiler configuration.

With the new setup, “tsconfig.json” simply contains TypeScript project references based on the so-called “solution style” (Visual Studio is back? :p) brought by TypeScript 3.9, which is great to improve compilation times and enforce a stricter separation between parts of the project:

In this case, the separation is there to cleanly isolate application code (taken care of by “tsconfig.app.json”) from tests (handled by “tsconfig.spec.json”).

If you look at the “tsconfig.base.json” file, then you’ll find the bulk of the TypeScript configuration:

Note that this one was generated using the strict option discussed in the previous section.

As you can see above, this file only configures TypeScript compiler and Angular compiler options; it doesn’t list/include/exclude files to compile.

The answer is indeed in the “tsconfig.app.json” file, which lists the “main.ts” and “polyfills.ts”:

If you have an existing project without this layout, then you should probably review your TypeScript configuration in order to stay aligned and benefit

Ok ok, enough about the TypeScript config.

NGCC

In case you haven’t done this yet (this was already true with NG9), make sure that you have a postinstall script in your “package.json” file to execute NGCC right after an installation:

Note that in this release, NGCC is more resilient. Previously, it couldn’t always recover when one of its worker processes crashed. So if you sometimes saw issues with NGCC hanging, this should now be fixed.

There were also quite a lot of improvements made to NGCC, including performance-related ones, which is clearly my biggest pain point around NGCC ;-)

New default browser configuration

Web browsers move faster than ever. Angular follows course and now uses an updated browserslist file (.browserslistrc).

As explained in the official blog post, the side effect of the new configuration is that ES5 builds are disabled by default for new projects.

Of course, at this point it doesn’t make much sense anymore to generate ES5 code. Modern Web browsers support at the very least ES2015. If you still use Internet Explorer, then it’s clearly time to let go of the past!

To get the exact list of supported Web browsers, just execute the following command in your project:

npx browserslist

The output is generated based on the contents of the “.browserslistrc” file at the root; by default it now looks as follows:

You can find out more about this here.

Bazel

Sorry to disappoint, but did you know that Angular Bazel has left Angular labs? Alex Eagle wrote about it on dev.to a while ago. Basically, support for Bazel is not part of the Angular project anymore.

Bazel will never be the default build tool in Angular CLI after all…

I won’t go over the reasons here, but make sure to take a look at the article of Alex as it is very interesting (as usual).

@angular-devkit/build-angular 0.1000.0)

Behind this barbaric name (and version???!), hides an important piece of the way Angular apps are built.

The newest version of this package brought us some cool new features.

The coolest one (if you’re using SASS that is) is the fact that build-angular will now rebase relative paths to assets.

As stated in the commit, previously, paths like url(./foo.png) referenced in stylesheets and imported in other stylesheets would retain the exact URL. This was problematic since it broke as soon as the importing stylesheet was not in the same folder. Now, all resources using relative paths will be found. Cool!

Another hidden gem in that release is the fact that build-angular now dedupes duplicate modules that Webpack can’t handle. This is done through a custom Webpack resolve plugin.

And more…

Incremental template type checking

In this release, the compiler CLI is now able to perform template type checking incrementally. Hopefully this will save quite a few trees (and maybe a laptop or two)! :)

CanLoad

Previously, CanLoad guards could only return booleans. Now, it’s possible to return a UrlTree. This matches the behavior of CanActivate guards.

Note that this doesn’t affect preloading.

I18N/L10N

Previously, only one translation file was supported per locale. Now, it is possible to specify multiple files for each locale. All of those then get merged by message id.

I can’t say much more about this since I’m only using ngx-translate & transloco these days… Check out this issue for more details.

Service Workers

The default SwRegistrationStrategy has been improved. Previously, there were cases where the Service Worker never registered (e.g., when there were long-running tasks like intervals and recurring timeouts).

Again, I can’t say much more as I’m not using NGSW but Workbox.

Angular Material

As usual, Angular Material’s releases follow those of Angular, so Angular Material 10 is here, with many changes.

I won’t go over these in this article as it is quite long already, so go check out the release notes if you’re interested!

Bug fixes galore

As mentioned a few weeks back, the Angular team has invested a lot of time and effort in bug fixing and backlog grooming. They’ve decreased their issue count by > 700 issues, which is quite impressive.

If you were the victim of known bugs in previous versions of Angular, then it’s probably time to take a look around and see if those aren’t fixed by Angular 10.

A funny one (to me that is) is the fact that enabling strict template type checking caused issues with routerLinks because their underlying type didn’t include null/undefined. Another one that was fixed is the KeyValuePipe, which didn’t play along well with the async pipe.

While we’re on templates, note that the language service of Angular now supports more array-like objects such as ReadonlyArray and readonly property arrays for *ngFor loops. How cool is that? :)

Deprecations and removals

As stated in the official blog post, the ESM5/FESM5 bundles that were previously part of the Angular Package Format are now gone because the downleveling to ES5 is now done at the end of the build process. If you don’t use the Angular CLI to build your application/library and still need ES5 bundles (poor souls..), then you’ll need to downlevel the Angular code to es5 on your own.

IE 9, 10 and Internet Explorer Mobile are not supported anymore. But again, if you ask me, you should just ditch IE altogether at this point. It’s nonsense to keep zombies around.

There are quite a few deprecated elements such as ReflectiveInjector, CollectionChangeRecord, DefaultIterableDiffer, ReflectiveKey, RenderComponentType, ViewEncapsulation.Native, ngModelwith Reactive Forms, preserveQueryParams, @angular/upgrade, defineInjectable, entryComponents, TestBed.get, etc.

You can check out the full list here.

Classes using Angular features without an Angular decorator are not supported anymore

Up to version 9, it was okay to have a class using Angular features without specifying one of the decorators (@Component, @Directive, etc).

With Angular 10, it is now mandatory to add an Angular decorator if a class uses Angular features. This change impacts all cases where you have components extending from a base class and one of the two (i.e., parent or child) is missing an Angular decorator.

Why is this change mandatory? Simply put, because Ivy needs it!

When there’s no Angular decorator on a class, the Angular compiler doesn’t add extra code for dependency injection.

As stated in the official doc, when the decorator is missing from the parent class, the subclass will inherit a constructor from a class for which the compiler did not generate special constructor info (because it was not decorated as a directive). When Angular then tries to create the subclass, it doesn’t have the correct info to create it.

In View Engine, the compiler has global knowledge, so it can look up the missing data. However, the Ivy compiler only processes each directive in isolation. This means that compilation can be faster, but the compiler can’t automatically infer the same information as before. Adding the @[Directive](https://next.angular.io/api/core/Directive)() explicitly provides this information.

When the child class is missing the decorator, the child class inherits from the parent class yet has no decorators of its own. Without a decorator, the compiler has no way of knowing that the class is a @[Directive](https://next.angular.io/api/core/Directive) or @[Component](https://next.angular.io/api/core/Component), so it doesn't generate the proper instructions for the directive.

The nice thing about this change is that it brings more consistency into the Angular world (and consistency is good :p). Now things are simple: if you use Angular features, then you must add a decorator.

To give you an example, the following code won’t compile with Ivy:

To fix the issue, you need to add a decorator to the Base class.

You can learn more about this change here.

Mandatory generic type for ModuleWithProviders

In previous releases, ModuleWithProviders already accepted a generic type, but it was not mandatory. With NG 10, the generic argument is required.

It’s a good thing for type safety anyways, so hopefully you already had the parameter defined:

If you stumble upon the following error because of a library that you’re using:

error TS2314: Generic type 'ModuleWithProviders<T>' requires 1 type argument(s).

Then you should contact the library author to get it fixed as ngcc can’t help there. A workaround there is to set skipLibChecks to false

Other breaking changes

Here are notable breaking changes:

  • Resolvers behave differently; those that return EMPTY will now cancel navigation. If you want to allow navigation to continue, then you need to make sure that your resolvers emit a value; for instance using defaultIfEmpty(...), of(...) and the like
  • Service worker implementations that rely on resources with Vary headers will not work like they did previously. Vary headers will be ignored. The proposed “solution” is to avoid caching such resources as they tend to cause unpredictable behavior depending on the user agents. Because of this, resources may be retrieved even when their headers are different. Note that cache match options may now be configured in NGSW’s config file
  • Property bindings such as [foo]=(bar$ | async).fubar will not trigger change detection if the fubar value is the same as the previous one. The workaround if you rely on the previous behavior is to manually subscribe / force change detection or adapt the binding in order to make sure that the reference does change
  • The following format codes of formatDate() and DatePipe have changed; apparently the previous behavior was incorrect for day periods that crossed midnight
  • The function that stands behind the UrlMatcher utility type (function alias) now correctly states that its return type may be null. If you have a custom Router or Recognizer class, then you need to adapt those
  • Additional occurrences of ExpressionChangedAfterItHasBeenChecked can now be raised by Angular for errors that it didn’t detect before
  • Angular now logs at error level when it notices unknown elements / property bindings in your templates. These were previously warnings
  • Reactive forms’s valueChangeshad a bug with FormControls that were bound to inputs of type number (they fired twice since 2016! A first time after typing in the input field and a second time when the input field lost focus). Now, number inputs don’t listen to the change event anymore, but to the input event. Don’t forget to adapt your tests accordingly. Note that this breaks IE9 compatibility, but that’s not a problem for anyone.. right? ;-)
  • The minLength and maxLength validators now make sure that the associated form controls values have a numeric length property. If that’s not the case, then these won’t be validated. Previously, falsy values without a length property (e.g., false or 0) were triggering validation errors. If you rely on that behavior, then you should add other validators like min or requiredTrue

Upgrading

As usual, there’s a complete upgrade guide available and ng update will help you out: https://update.angular.io/#9.0:10.0l3

If you do the upgrade manually and still use Protractor (just in case), then don’t forget to update protractor to 7.0.0+ as previous versions had a vulnerability.

Conclusion

In this article, I’ve explored the new features of Angular 10, as well as the deprecations, removals and breaking changes.

All in all, even if this isn’t an earth-shattering release, it’s clearly a rock solid one with tons of bug fixes and a few gems.

As usual, we can only be thankful for all the efforts made by the Angular team and the community that surrounds it!

That’s it for today.