Angular 13 in Depth

Everything you need to know about Angular 13

Angular 13 in Depth

Angular 13 is now available. Time to discover what's new!

In this article, I’ll go over (almost) everything noteworthy in this new release. I’ll also highlight what’s changed around Angular, as I did with my previous articles about Angular 12, Angular 11, and Angular 10.

What's new with Angular 13

If you're looking for the bird-eye view, then here it is:

  • IE11 is no longer supported
  • Faster production builds (+68%)
  • Persistent build cache by default: https://github.com/angular/angular-cli/issues/21545
  • View Engine is gone
  • New Angular Package Format (APF): https://angular.io/apf
  • Simplified API to dynamically create components
  • Minor improvements to forms type safety
  • Improvements to Angular TestBed
  • Accessibility (a11y) improvements to MDC-based Angular Material components
  • TypeScript >=4.4.x
  • RxJS >=7.4

If you want more, then check out the official release announcement.

And if you want all the details, then stay here with me! We're going to dig much deeper into the release notes ✨.

IE11 is no longer supported

Angular 13 no longer supports Internet Explorer. I consider this to be one of the most important news.

By dropping support for Internet Explorer, Angular can finally make use of native Web browser features such as CSS variables and Web animations. Previously, Angular had to include polyfills to ensure retro-compatibility with IE.

This means that Angular is now lighter than before, and will load/execute faster (i.e., better UX!). Moreover, the Angular team can now focus on evergreen Web browsers, which will enable faster innovation cycles.

It also renders differential loading bogus. Previously, Angular used that technique to generate specific JavaScript bundles for legacy browsers, which slowed everything down and increased the size of production builds.

The update process of angular automatically drops the old IE polyfills and reduces bundle size during migration.

Note that Angular 12 will continue to support IE11 until November 2022.

If you're working for a company that still uses Internet Explorer, the message is clear: it's time to stop that nonsense!

View Engine is not supported anymore

The View Engine is not supported by Angular 13.

This is a great step forward for Angular. Again, it means that the Angular team will be able to focus more on the forward-looking evolutions of the framework.

As mentioned in the official announcement, removing View Engine also means that Angular can reduce its reliance on ngcc (Angular compatibility compiler) in the future, and teams can look forward to faster compilation because metadata and summary files are no longer included.

Changes to the Angular Package Format (APF)

As mentioned in the official announcement, the Angular Package Format (APF) has been streamlined and modernized to better serve developers. To streamline the APF in Angular 13, older output formats such as CommonJS (CJS) and UMD have been removed. As mentioned by Igor Minar, the whole Web community will be better off once ES Modules (ESM) are used everywhere.

The APF now uses more modern JS formats: ES Modules and ES2020. This means that libraries built with the latest version of the APF will no longer need to use ngcc. ngcc is painful for all Angular developers, as I've mentioned previously. Thanks to this change, the first build after an installation will be much faster. Awesome! 🚀

The Angular team has also updated the APF to support Node Package Exports. This will help developers from inadvertently relying on internal APIs that may change.

It is now time for Angular library authors to stop using enableIvy: false, and to instead use compilationMode: partial (in tsconfig.prod.json), as recommended by Younes. The partial compilation mode is a partial-Ivy code format that is stable between different versions of Angular, and thus safe to publish to npm. Angular code that uses this partial format is processed during the build of the application using the same version of the Angular compiler. The benefit is that it both the application and its libraries rely on a single version of Angular, removing potential compatibility issues. Check out the official guidelines for library authors to learn more.

Hopefully, once enough library authors have started shipping their libraries with the new partially compiled format, ngcc should finally go away.

Note that the Angular CLI now prefers the ESM version of dependencies (if it is available).

As a result of these changes, the production build of Angular applications now targets ES2020 instead of ES2015 as it was the case before.

Angular 13 now supports TypeScript 4.4

Angular 13 introduces support for TypeScript 4.4, which means that we can now use a ton of new wonderful language features. Also, support for TypeScript 4.2 and 4.3 has been dropped.

One notable improvement with TypeScript 4.4 that is particularly beneficial for Angular applications is the fact that it no longer enforces getters and setters to have the same type. Previously it was the case, and it was especially annoying for @Input.

Check out the release notes of TypeScript 4.4 to discover the cool new features that you'll be able to use in your projects.

Better API to create components dynamically

Angular no longer requires component factories to dynamically create components.

Previously, we had to create a ComponentFactory ourselves by invoking ComponentFactoryResolver. This is not needed anymore. The ViewContainerRef.createComponent now accepts a component instance of will take care of creating the factory transparently.

So, instead of this:

@Directive({ ... })
export class MyDirective {
  constructor(
    private viewContainerRef: ViewContainerRef,
    private componentFactoryResolver: ComponentFactoryResolver
  ) {}

  createMyComponent() {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(MyComponent);
    this.viewContainerRef.createComponent(componentFactory);
  }
}

We can now write this instead:

@Directive({ ... })
export class MyDirective {
  constructor(private viewContainerRef: ViewContainerRef) {}

  createMyComponent() {
    this.viewContainerRef.createComponent(MyComponent);
  }
}

Less boilerplate code is better, yay!

Angular CLI improvements

As you know, the Angular CLI is one of the key pieces of the Angular puzzle. Few developers in the world are able to deal with the build complexity of the modern Web development ecosystem. And luckily for us, the Angular CLI shields us from most of it.

With this release, Angular now uses the persistent build cache by default, as requested by the community. The persistent build cache is a feature that caches build results on disk (under the .angular/cache folder). This results in up to 68% improvement in build speed.

In order to enable this feature in existing Angular applications, you need to add the following configuration to your angular.json file:

{
    "$schema": "...",
    "cli": {
        "cache": {
            "enabled": true,
            "path": ".cache",
            "environment": "all" // other supported values: "ci" and "local"
        }
    }
    ...
}

You can also disable that feature (don't unless you really have no choice) using ng config cli.cache.enabled false. If you need to clear the cache, then just remove the .angular/cache folder.

You can find more information in the documentation.

Aside from that, ESBuild also sees some performance improvements in this release! The Angular team has introduced esbuild, which now works with terser to optimize global scripts. In addition, ESBuild supports CSS sourcemaps and can optimize global CSS, as well as optimizing all style sheets. I'm eager to see some benchmarks on this.

Interestingly, the Angular team is considering using tools like Vite in the future. It makes sense now that Angular supports ESM and is aligned with recent NodeJS versions. I'm curious to see what performance improvements and new features it will bring in the future.

RxJS 7.4 and NodeJS 16

Angular 13 now uses RxJS 7.4 by default for applications created using ng new.

I wrote an article about RxJS 7 a while ago, but haven't updated it recently. Check out the following page if you want to discover what changed between RxJS 6 and 7.

What’s new in RxJS 7
Exploring everything new with RxJS 7

Aside from that, Angular 13 has dropped support for NodeJS versions below v12.20.0 and has introduced support for NodeJS >=16.10.

Forms

Angular Forms have received a bit more attention in this release. Not yet as much as I would have hoped, but there are still notable improvements to the type safety of reactive forms. How cool is that?! 🚀. Let's explore the changes.

Improved type safety for form status

Form statuses now use a specific type. A new type called FormControlStatus has been introduced, which is a union of all possible status strings for form controls: 'VALID' | 'INVALID' | 'PENDING' | 'DISABLED'. That new type is used for AbstractControl.status.

Also, statusChanges has been narrowed from Observable<any> to Observable<FormControlStatus>. This is a breaking change, but it's really for the best. As mentioned in the release notes, it can break your application if you were using an invalid form status string (thus a bug fix!) or if you were using statusChanges events as if they were something else than strings.

New form methods

New form methods have been added to AbstractControl in this release:

  • addAsyncValidators: Add one or more asynchronous validator(s) to the control, without affecting other validators
  • addValidators: Add one or more synchronous validator(s) to the control, without affecting other validators
  • hasAsyncValidator: Check whether an asynchronous validator function is present on this control (must be the exact same function that was provided)
  • hasValidator: Determines whether a validator or validators array has a given validator
  • removeAsyncValidators: Remove asynchronous validators from this control
  • removeValidators: Remove synchronous validators from this control

Those provide us with more control over the validation process. We can now easily add/remove synchronous and asynchronous validators.

Dynamically enable/disable validators

The minLength and maxLength validators can now be bound to null. This allows to disable validation and preventing attributes from being added to the DOM. This fixes a regression that was introduced by those two validators. In addition, we can now disable those validators dynamically.

The ability to dynamically enable/disable validators is definitely going to be very useful for us all!

Templates

Angular now supports autocompletion for string literal union types.

The language service of Angular is now able to automatically insert the optional chaining operator (i.e., ?.) when property access (i.e., .) is done on a nullable symbol.

Here's an example:

Note that the fullTemplateTypeCheck compiler option has been deprecated. You should now use strictTemplates instead. Check out my article about Angular Template type Checking if you want to know more about that.

Angular Template Type Checking
Leverage strict template type checking to create bulletproof Angular applications

An older syntax for bindings has also been deprecated. I don't think that many Angular developers even knew about that, but if you are defining bindings using things like on-click, then you should stop doing so.

Router

A new output called isActiveChange has been added to the routerLinkActive directive. It emits whenever the link becomes active or inactive.

Control the router behavior after navigation cancellation

A new option has been added to the public API of the Angular Router: canceledNavigationResolution. It allows to configure the behavior of the router regarding the state restoration when a navigation is cancelled. Check out the sources for the possible values and meaning.

Observe activate/deactivate events when an outlet gets attached/detached

The router now provides new outputs allowing to subscribe for the attach and detach events on a RouterOutlet. Previously, there was no way to subscribe for those events when an outlet got attached/detached with RouteReuseStrategy. Those new outputs emit when an outlet gets attached/detached.

Define custom route reuse strategy through DI for RouterTestingModule

It is now possible to provide a custom route reuse strategy through dependency injection for the RouterTestingModule:

TestBed.configureTestingModule({
    imports: [RouterTestingModule],
    providers: [{provide: RouteReuseStrategy, useClass: AttachDetachReuseStrategy}]
});

Angular tests improvements

With Angular 13, the Angular TestBed has received some improvements.

It now better takes care of tearing down test modules and environments after each test. The DOM is now cleaned up after every test, avoiding side effects. Overall, tests should execute faster, use less memory, and be less interdependent.

The automatic teardown was an opt-in since Angular 12.1.0 and is now the default. As mentioned in the announcement, this behavior can be configured for the entire test suite via the TestBed.initTestEnvironment method:

beforeEach(() => {
    TestBed.resetTestEnvironment();
    TestBed.initTestEnvironment(
        BrowserDynamicTestingModule,
        platformBrowserDynamicTesting(),
        {
            teardown: { destroyAfterEach: true }
        }
    );
});

Or it can be configured per module by updating the TestBed.configureTestingModule method:

beforeEach(() => {
    TestBed.resetTestEnvironment();
    ...
    TestBed.configureTestingModule({
        declarations: [TestComp],
        teardown: { destroyAfterEach: true }
    });
});

This provides us with the flexibility to use a different approach depending on the situations. Lars Gyrup Brink Nielsen has written a blog post to explain this more in detail.

Accessibility improvements

With Angular 13, the accessibility of Angular Material MDC-based components has been drastically improved.

To me, it is always a pleasure to see large organizations put energy into improving accessibility. I wish more organizations across the world would do the same, given how important it is to ensure that all of our users have a great experience.

All the MDC-based components have been reviewed and adapted to meet more stringent accessibility standards. The improvements include things like contrast, touch targets, ARIA, and more.

In the announcement, the Angular team has highlighted the touch target size changes for components like checkbox and radio button:

As well as the better high contrast mode support for multiple components:

To learn more about those changes, check out the blog post about improving Angular Component's accessibility.

I'll tell you more about the other Angular Material changes further in the article.

And more...

There's a ton more minor changes in this release. Here's a quick overview.

Support for inlining Adobe Fonts

With the release of Angular 11, Angular became capable of inlining Google Fonts. With this release, Angular can now also inline Adobe Fonts. This new feature is enabled by default.

Font inlining is a very cool feature, as it helps improve application performance, and thus reach better Web Vitals scores. In particular, it helps with First Contentful Paint (FCP).

Check out the following video for more details:

Date pipe improvements

Previously, if we needed to specify a custom timezone/timezone offset for the date pipe, the only option was to use the second (optional) argument of the date pipe, like this:

{{ myDate | date: "shortTime":"+1200" }}

This was cumbersome because it had to be repeated all over the place. Angular 13 introduces a new way to customize this, using a specific provider:

providers: [
  { provide: DATE_PIPE_DEFAULT_TIMEZONE, useValue: '+1200' }
]

It's a small improvement, but it can be a big win for large projects. Less boilerplate is better for readability and developer experience (DX). You can learn more about this here.

Service worker (NGSW) changes

Small changes have been introduced for the Angular service workers. Previously, we had to check the activated observable in order to determine if the invocation resulted in an update of the service worker. This is no longer the case. The activated observable has been deprecated. As of Angular 13, the activateUpdate and checkForUpdate promises will return true if the update was activated.

Note that the available observable has also been deprecated. The versionUpdates observable replaces it. The new one provides more information and emits whenever a new version of the service worker is available and if an installation failed.

You can learn more about those changes here.

ng serve changes

The ng serve command now uses webpack-dev-server 4.x, which uses WebSockets to push changes to the browser. Check out the release notes of the package to discover other new features/improvements.

If you use a proxy, you will need to make sure that your proxy is also configure for WebSockets. Otherwise you'll encounter some issues.. ;-)

Localization changes

$localize is a fast and efficient method to implement internationalization (i18n) and tag messages for translation in code, but also in templates. Here's an example:

function hello(name) {
    return $localize `Hello ${name}`;
}

This translates into:

function hello(name) {
    return "Bonjour" + name;
}

Quite nice. Unfortunately, this new API is not compatible with third-party libraries like ngx-translate or transloco.

The documentation describing the localization API has been updated. Check out the guides to learn more. For instance, the $localize API has been documented.

Breaking changes

There are also a number of breaking changes with this release. A necessary evil! 😈

As the official release notes are pretty clear, I’m just copy/pasting those here:

Common:

  • The behavior of the SpyLocation used by the RouterTestingModule has changed to match the behavior of browsers. It no longer emits a 'popstate' event when Location.go is called. In addition, simulateHashChange now triggers both a hashchange and a popstate event. Tests which use location.go and expect the changes to be picked up by the Router should likely change to simulateHashChange instead. Each test is different in what it attempts to assert so there is no single change that works for all tests. Each test using the SpyLocation to simulate browser URL changes should be evaluated on a case-by-case basis.

Core:

  • TypeScript versions older than 4.4.2 are no longer supported.
  • NodeJS versions older than v12.20.0 are no longer supported due to the Angular packages using the NodeJS package exports feature with subpath patterns.
  • The WrappedValue class can no longer be imported from @angular/core, which may result in compile errors or failures at runtime if outdated libraries are used that are still using WrappedValue. The usage of WrappedValue should be removed as no replacement is available.

Forms:

  • A new type called FormControlStatus has been introduced, which is a union of all possible status strings for form controls. AbstractControl.status has been narrowed from string to FormControlStatus, and statusChanges has been narrowed from Observable<any> to Observable<FormControlStatus>. Most applications should consume the new types seamlessly. Any breakage caused by this change is likely due to one of the following two problems: (1) the app is comparing AbstractControl.status against a string which is not a valid status; or, (2) the app is using statusChanges events as if they were something other than strings.

Router:

  • The default url serializer would previously drop everything after and including a question mark in query parameters. That is, for a navigation to /path?q=hello?&other=123, the query params would be parsed to just {q: 'hello'}. This is incorrect because the URI spec allows for question mark characters in query data. This change will now correctly parse the params for the above example to be {v: 'hello?', other: '123'}.
  • Previously null and undefined inputs for routerLink were equivalent to empty string and there was no way to disable the link's navigation. In addition, the href is changed from a property HostBinding() to an attribute binding (HostBinding('attr.href')). The effect of this change is that DebugElement.properties['href'] will now return the href value returned by the native element which will be the full URL rather than the internal value of the RouterLink href property.
  • The router will no longer replace the browser URL when a new navigation cancels an ongoing navigation. This often causes URL flicker and was only in place to support some AngularJS hybrid applications. Hybrid applications which rely on the navigationId being present on initial navigations that were handled by the Angular router should instead subscribe to NavigationCancel events and perform the location.replaceState themselves to add navigationId to the Router state. In addition, tests which assert urlChanges on the SpyLocation may need to be adjusted to account for the replaceState which is no longer triggered.
  • It is no longer possible to use Route.loadChildren using a string value. The following supporting classes were removed from @angular/core: NgModuleFactoryLoader and SystemJsNgModuleFactoryLoader
  • The @angular/router package no longer exports these symbols: SpyNgModuleFactoryLoader and DeprecatedLoadChildren
  • The signature of the setupTestingRouter function from @angular/core/testing has been changed to drop its NgModuleFactoryLoader parameter, as an argument for that parameter can no longer be created.

Service worker:

  • The return type of SwUpdate#activateUpdate and SwUpdate#checkForUpdate changed to Promise<boolean>. Although unlikely, it is possible that this change will cause TypeScript type-checking to fail in some cases. If necessary, update your types to account for the new return type.

CDK:

  • CKD_COPY_TO_CLIPBOARD_CONFIG has been removed. Use CDK_COPY_TO_CLIPBOARD_CONFIG instead.
  • ConnectedPositionStrategy has been removed. Use FlexibleConnectedPositionStrategy instead.
  • OverlayPositionBuilder.connectedTo has been removed. Use OverlayPositionBuilder.flexibleConnectedTo instead.

Material:

  • CanColorCtor is no longer necessary and has been removed.
  • CanDisableRippleCtor is no longer necessary and has been removed.
  • CanDisableCtor is no longer necessary and has been removed.
  • CanUpdateErrorStateCtor is no longer necessary and has been removed.
  • HasInitializedCtor is no longer necessary and has been removed.
  • HasTabIndexCtor is no longer necessary and has been removed.
  • Material now requires at least version 1.34.0 of Sass. Version 1.38.0 is recommended.
  • The _document and _dialog parameters have been removed from the MatDatepicker and MatDateRangePicker constructors.
  • MatFormFieldHarness.getHarnessLoaderForPrefix has been removed. Use MatFormFieldHarness.getPrefixText instead.
  • MatFormFieldHarness.getHarnessLoaderForSuffix has been removed. Use MatFormFieldHarness.getSuffixText instead.
  • The _labelOptions parameter of the MatFormField constructor has been removed.
  • MatFormField.underlineRef has been removed.
  • matTextareaAutosize has been removed. Use cdkTextareaAutosize from the @angular/cdk/text-field module instead.
  • MatTabHarness.getHarnessLoaderForContent has been removed. Use MatTabHarness.getRootHarnessLoader instead.

Youtube player:

  • YouTubePlayer.createEventsBoundInZone has been removed.

Deprecations and bug fixes

Check out the release notes for the full list of deprecations and bug fixes.

Angular Material & Angular CDK

Date adapter for date-fns

Angular Material has included adapters for moment.js and luxon for a while. With Angular 13, a new date adapter has been added that supports date-fns. That's pretty cool!

Angular CDK changes

Angular 13 only includes a small change for the Angular CDK.

The cdkConnectedOverlayOrigin input of the CdkConnectedOverlay directive supports the additional types defined by FlexibleConnectedPositionStrategyOrigin

Angular Material changes

There are also a few changes for Angular Material.

It is now possible to define content sections in test harnesses for the Dialog component.

Also, a new injection token called MAT_PROGRESS_BAR_DEFAULT_OPTIONS has been added to the progress bar. It can be used to configure the default options as follows:

provide: MAT_PROGRESS_BAR_DEFAULT_OPTIONS,
useValue: {
  mode: 'buffer',
  color: 'warn'
}

The autoFocus option of dialogs was previously a boolean. It was used to specify whether the container element or the first tabbable element was to be focused on dialog open. As of Angular Material 13, it is now possible to configure it in other ways. For instance, we can now configure it to focus the first header element, or use a CSS selector and have it focus the first element that matches that selector. The new type of the autoFocus option is AutoFocusTarget | string | boolean = 'first-tabbable';. Check out the PR for usage examples.

The experimental version of Material has also a few new features. The most noteworthy one is the following:

A DI token called MAT_CARD_CONFIG has been added for MDC-based cards. It allows to set the default appearance: provide: MAT_CARD_CONFIG, useValue: {appearance: 'outlined'}.

Angular Universal

Nothing new for Angular Universal in this release, apart from the support for NodeJS 16.

Google Maps

As you know, Angular includes components for Google Maps and Youtube. Version 13 doesn't bring anything new. The only change is the fact that Angular's Google Map component now relies on more recent typings for the google.maps package: ^3.45.6.

Upgrading

As usual, there's a complete upgrade guide available, and ng update will help you out: https://update.angular.io/?l=3&v=12.0-13.0.

A number of automated migrations have been included in this release:

  • Remove the old IE-specific polyfills
  • Remove options like extractCss which have been removed in this release
  • Add .angular/cache to .gitignore
  • Update empty routerLink in templates
  • ...

If you're using Nrwl NX (hint: you should!), I'm sure that support for Angular 13 will be added real soon.

Conclusion

In this article, I've explored the new features of Angular 13 as well as all the nitty-gritty details. As usual, this is release is heroic (what else could it be?!).

Now that ViewEngine is gone, Ivy is king in Angular-land. Without a doubt, this will enable the Angular team to focus on forward-looking improvements like making NgModules optional and running type checks in the background, which could lead to huge developer experience improvements.

If you want to learn more about optional NgModules, then check out the Angular Nation Meetup video:

By the way, you can already prepare for the future of Angular by getting rid of shared modules.

I'm really optimistic about the next two major releases of Angular. Hopefully, Angular 14 will introduce much safer reactive forms, and Angular 15 is certainly going to be an EPIC release 😎.

Luckily for us, the Angular team now relies more and more on RFCs to discuss the future evolutions. This gives more room for the community to be listened to, and that's great!

That's it for today! ✨