What's new in Angular 9.1?

Angular 9.1.0 is here!

Angular logo

This is a small release in terms of features but a big release in terms of bug fixes. As more and more people are updating their applications to Ivy, the Angular team fixed a few remaining issues for corner cases. All in all, it looks like the upgrade has been fairly easy for most applications (which I still find unbelievable when you know how different the compilers/runtimes are!).

The team has also been digging into the opened issues on Github: it’s no secret that issues accumulated over these past months/years without much feedback from the Angular team, busy as they were working on Ivy. A lot of issues have been closed because they were already fixed by Ivy, and all should now be properly labeled, to allow the team to plan what should be worked on. This is exciting, and we should see long awaited requests answered in the next months.

Let’s see what version 9.1 brings us.

TypeScript 3.8

This release comes with the support of TypeScript 3.8. You can check out which new features TS 3.8 offers on the Microsoft blog.

Template compiler

The Ivy compiler introduced a new strictTemplates option in v9 (see our Angular 9.0 blog post for more info). This flag is a shortcut to enable a bunch of checks, that can be individually enabled/disabled. A new option called strictLiteralTypes has been introduced if you want to relax the check on literal types in templates (View Engine interpreted them as any whereas Ivy is stricter and infers their proper type by default).

i18n

The locale data now contain the writing direction of the locale (‘rtl’ or ‘ltr’). A utility function getLocaleDirection offered by the @angular/common package can help you retrieve this information in your application.

ngcc

The Angular compatibility compiler (the piece that compiles the dependencies of your application to make them compatible with Ivy) should now be a bit faster, a bit more reliable and a bit more accurate. This is good news as we need to run this compiler on every CI build, and, as you have probably witnessed, every time a dependency changes on your machine (the CLI runs it for you). It’s especially painful for those using Yarn, as Yarn blows away the whole node_modules directory every time a dependency changes, forcing ngcc to re-compile every dependency again. So every gain of speed on this compiler is welcome until we can get rid of it, once all the dependencies are directly released in an Ivy compatible format. But that’s going to take a while.

zone.js

Even if this is not really about Angular, zone.js was recently released in version 0.10.3 and it comes with a few new features.

Passive events

Passive events can be very handy if you want to do something on an event that happens very often, like scroll or mousemove, but don’t want to slow down the scrolling of your application: it’s here to tell the browser that it can proceed with scrolling without waiting for the listener function to return.

Angular doesn’t have a syntax to easily declare a passive event in an application, but zone.js now offers a global variable to configure it. For example, if your declare (window as any)['__zone_symbol__PASSIVE_EVENTS'] = ['scroll']; then all the scroll event listeners you’ll declare in your application will be passive.

Better Jest support

Until now, zone.js automatically added support for Jasmine and Mocha when importing zone-testing (the CLI imports it in the test.ts file of your project). It now also offers built-in support for Jest, a popular testing framework.

This support already existed via the community library jest-preset-angular, which is still needed, but the library now avoids to mingle with Zone.js itself and only focuses on the Jest part.

MessagePort

Zone.js can now handle the Channel Message API. If you listen to messages in your Angular application, the callback can now automatically run in the zone. You can see how to disable it, like every zone module, in this document.

tickOptions

The tick() function that we use in asynchronous tests can now receive an extra parameter tickOptions. tickOptions has for the moment only one property: processNewMacroTasksSynchronously. It has been introduced for a very specific use-case with nested timeouts. By default, this property is true and reflects the current behavior, where all timeouts, even the nested ones not yet installed, are resolved when calling tick. You can now give the extra option to tick to force it to resolve only the currently installed timeouts.

You can also check out our blog post about the CLI v9.1 to see what’s new there.

I’m not sure I can publicly talk about it yet, so all I will say is that long-awaited work should start soon, and hopefully land in the upcoming v10!

All our materials (ebook, online training and training) are up-to-date with these changes if you want to learn more!


What's new in Angular CLI 9.1?

Angular CLI 9.1.0 is out!✨

If you want to upgrade to 9.1.0 without pain (or to any other version, by the way), I have created a Github project to help: angular-cli-diff. Choose the version you’re currently using (8.3.8 for example), and the target version (9.1.0 for example), and it gives you a diff of all files created by the CLI: angular-cli-diff/compare/8.3.8…9.1.0. It can be a great help along the official ng update @angular/core @angular/cli command. You have no excuse for staying behind anymore!

Let’s see what we’ve got in this release.

TSLint 6.1

As Angular 9.1 now supports TypeScript 3.8, the CLI does support it as well. The CLI also supports TSLint 6.1 (which supports the new TypeScript syntaxes introduced in version 3.8). This is a bit weird, as TSLint is officially deprecated since version 6.0, in favor of typescript-eslint.

The CLI team plans to migrate to typescript-eslint as soon as possible, but, in the meantime, it still supports TSLint, and its new release 6.1.

Note that TSLint 6.0 introduced a lot of changes in the tslint:recommended config, that the CLI extends.

To avoid these breaking changes in our applications, the default tslint.json configuration has been updated, and the CLI 9.1 ships with a migration you can run to update your configuration. The migration will be run automatically in version 10, but until then, you can run it manually if you want with:

ng update @angular/cli --migrate-only tslint-version-6

Fun with flags 🤓

ng e2e

The Protractor builder now accepts two flags grep and invertGrep to only run the matching spec names. Not the file name, the spec name, i.e. what you specified in the describe() and it() functions:

describe('App', () => {
  it('should have a navbar', () => { });
  it('should say hello on home', () => { });
  it('should have a login button on home', () => { });
});

For example if you only want to run the specs whose name matches ‘home’, you can run ng e2e --grep home.

invertGrep can be used to run the specs that don’t match the grep regex: ng e2e --grep home --invert-grep runs all the specs except the ones containing “home”.

All our materials (ebook, online training and training) are up-to-date with these changes if you want to learn more!


What's new in Angular 9.0?

Angular 9.0.0 is here!

Angular logo

Ivy, sweet Ivy

This is a long awaited release for the community, as Ivy is now the default compiler/renderer in Angular 🌈.

If you want to learn more about Ivy itself, check out our dedicated blog post.

In a few words, if you’re in a hurry, Ivy is a complete rewrite of the underlying compiler and renderer. The main goals are:

  • a cleaner architecture of the framework, which is a stepping stone to optimizations and new features in the future
  • a faster and stricter compilation, to help developers and avoid slowing them down
  • smaller bundle sizes, especially in big applications right now, and all over the place in the future.

As you can probably imagine, Ivy is a very big change internally, and the team worked a lot to ensure that our applications are compatible. Bundle sizes should improve in the future, as well as runtime performances (which are currently roughly on par with View Engine, faster in some cases, slower in others).

As this is a big change, I strongly encourage you to thoroughly test this update, more thoroughly than the v5, v6, v7 or v8 updates. If you encounter a problem, open an issue with a reproduction, and stay on v8 or use v9 without Ivy for the moment (enableIvy: false).

The new design will allow some cool features in the future, like high order components, a better i18n, etc. For this last point, guess what? It’s already here!

Internationalization with $localize

A new package appears with this release: @angular/localize. This package is now in charge of all i18n concerns, and offers quite a few interesting things.

In fact there are so many topics to cover that we wrote a dedicated article about it: Angular i18n with $localize.

This article talks about compile-time i18n, the much awaited possibility to have translations in code, and how all this works under the hood. Check it out!

Note that even if you are not using i18n in your application, but that one of your dependencies does (like ng-bootstrap for example), then you have to add @angular/localize to your application. This is fairly straightforward:

ng add @angular/localize

But really you should check out the blog post we wrote or the updated chapter in our ebook.

Other i18n goodies

Unrelated to this new package, the locale data now include the directionality of the locale (ltr or rtl). You can get this information by using getLocaleDirection(locale).

Angular 9 also offers the possibility to configure the default currency! The currency pipe allows to specify which currency you want to use by providing an ISO string like USD, ‘EUR’… If you don’t provide one, it uses USD by default. So it is a bit cumbersome to have to specify the currency every time, if your application only uses EUR for example.

Angular 9 introduced the possibility to configure the default currency globally using the token DEFAULT_CURRENCY_CODE:

{ provide: DEFAULT_CURRENCY_CODE, useValue: 'EUR' },

And you can then use currency without specifying EUR in a component. You can also get the currency via dependency injection of course:

@Component({
  selector: 'ns-currency',
  template: `
    <p>The currency is {{ currency }}</p>
    <!-- will display 'EUR' -->
    <p>{{ 1234.56 | currency }}</p>
    <!-- will display '1 234,56 €' -->
  `
})
class DefaultCurrencyOverriddenComponent {
  constructor(@Inject(DEFAULT_CURRENCY_CODE) public currency: string) {}
}

It also offers a getLocaleCurrencyCode(locale) function to retrieve the default currency for a specific locale.

Note that in Angular 10 the behavior will change, and the currency pipe will always use the default currency of the configured locale, even you don’t specify the DEFAULT_CURRENCY_CODE!

Better type-checking

In the Ivy section, I was mentioning that the compilation is a bit stricter. And indeed it is smarter! If you use fullTemplateTypeCheck, you’ll have the same level of strictness that you had before. But if you want to go one step further, you can try the strictTemplates option in your tsconfig.json:

"angularCompilerOptions": {
  "strictTemplates": true
}

Until now, you could for example give a string to @Input() that was expecting a number: you now get a nice compilation error. ngFor elements were previously typed as any, and are now properly typed. So:

<div *ngFor="let user of users">{{ user.nam }}</div>

is now caught by the compiler if you typed nam instead of name. The $event parameter and #ref variables in templates are now also properly typed.

strictTemplates is just a shortcut to activate a bunch of strictness flags, so if one particular check bothers you, you can disable it. For example, if you don’t want to check the type of the inputs:

"angularCompilerOptions": {
  "strictTemplates": true,
  "strictInputTypes": false
}

Check out the official list of flags in the documentation.

As a component author, there is a convoluted way to accept other types than the declared one for your input. It is mostly useful for library maintainers, and you can check out the official documentation for an example.

Better auto-completion

The language service (the package responsible for the nice auto-completion we have in our IDEs) has been improved and now offers some really nice new features. For example if you hover on a component or a directive in template, you’ll see if it is the former or the latter and from which module it comes from. It should be way smarter and more robust overall.

TypeScript 3.7

This release also comes with the need to upgrade to at least TypeScript 3.6, and you can even use TypeScript 3.7. You can check out which new features TS 3.6 and TS 3.7 offers on the Microsoft blog posts (optional chaining, yay \o/).

Automatic migrations

If you are using the CLI ng update @angular/core command, you’ll notice that several automatic migrations will run.

If you are still using the deprecated Renderer, the schematic updates your code to use Renderer2. You can read more about that in the official deprecation guide. The Renderer class was deprecated for a long time and has been removed from Angular.

If you are using a base class for a component or a directive, that base class must be annotated with a simple @Directive() decorator for Ivy. For example:

class WithRouter {
  constructor(public router: Router) {}
}

@Component({
  // ...
})
export class MenuComponent extends WithRouter {
  // uses `this.router` from `WithRouter`

Then after the migration, WithRouter has a @Directive() decorator added on its class:

@Directive()
class WithRouter {
  constructor(public router: Router) {}
}

Note that this @Directive() decorator does not have a selector: this was not possible before v9, and it has been introduced for this use-case specifically.

The @Directive() decorator is also added to your base class if it has decorated fields, like @Input(), @Output(), @ViewChild(), @HostBinding(), etc..

Another migration adds an @Injectable() decorator on all services that don’t have one. It was not necessary for View Engine (check this blog post if you want to know why) but it is now with Ivy. So everything that is referenced as a service in your application must now have an @Injectable() decorator.

ModuleWithProvider must also provide its generic type, so a schematic adds it if it is missing in your code.

You may also remember the static flag added in Angular 8.0, to ease the migration to Ivy. If you don’t, check out what we wrote about it at that time in the Query timing section. This flag is now no longer required now that Angular uses Ivy. So a migration will remove every static: false you have in your codebase, as it is the default value.

All the migrations are properly documented in the official documentation for the v9 update.

Dependency injection

Until now, when using @Injectable(), you could give only two values: an Angular module or root (which is an alias for the root module of your application). It now also accepts platform and any. The latter provides a unique service instance in every module (including lazy modules) that injects the token, and the former could be useful if you have several Angular applications or Angular Elements on the same page, and want them to share a service instance.

Deprecations

entryComponents is no longer necessary

Until now you had to declare the component dynamically loaded, typically in a modal, in the entryComponents of a module. This is no longer necessary with Ivy! You can safely remove entryComponents from your modules.

TestBed.inject

The TestBed.get method that you probably use all over your unit tests has been deprecated and replaced by TestBed.inject. TestBed.get returns any, so you had to manually type the value returned. It is now no longer necessary with TestBed.inject. You can then replace:

const service = TestBed.get(UserService) as UserService;

by

const service = TestBed.inject(UserService);

Breaking changes

NgForm

The directive NgForm used to have ngForm as a possible selector and it’s now no longer the case. It had already been deprecated in Angular 7.0.

Hammer

Until v9, an Angular application was offering Hammer.JS support by default, even if you were not using it. To gain a few kB in our bundles, this is no longer the case, and you’ll have to import HammerModule explicitly in your root module if you want to keep using Hammer.

Intl API

The i18n pipes (number, percent, currency, date) were using the Intl API, and were rewritten to not depend on it in Angular 5.0. The current version of these pipes works without the Intl API. If you really wanted to keep the old version, you had to import DeprecatedI18NPipesModule in your application when migrating to Angular 5.0. This is no longer possible as all the deprecated pipes have been removed.

This is a big update, and I’m sure you’re dying to try it 😎. You can also check out our blog post about the CLI v9 to see what’s new there.

All our materials (ebook, online training and training) are up-to-date with these changes if you want to learn more!


What's new in Angular CLI 9.0?

Angular CLI 9.0.0 is out!✨

If you want to upgrade to 9.0.0 without pain (or to any other version, by the way), I have created a Github project to help: angular-cli-diff. Choose the version you’re currently using (8.3.8 for example), and the target version (9.0.0 for example), and it gives you a diff of all files created by the CLI: angular-cli-diff/compare/8.3.8…9.0.0. It can be a great help along the official ng update @angular/core @angular/cli command. You have no excuse for staying behind anymore!

Let’s see what we’ve got in this release.

Ivy by default

This is probably the most awaited feature in the Angular community: Ivy is now production ready, and the CLI flag enableIvy is now true by default! You can still switch it back to false if you encounter an issue.

You’ll notice that the first build/serve of your application is slightly longer: this is because ngcc needs to compile your dependencies. Once this is done, the dependencies are “Ivy compatible”, and the following builds don’t need to run ngcc again (except if you add a new dependency for example). This should be mostly transparent, as the Angular team did its best to support the most popular libraries. If you encounter an issue with one of your dependencies though, open an issue on the Angular repository, and someone will take a look.

The build and serve commands now use the AoT compiler by default, and you should have re-build times close to what we had in JiT mode previously. In our applications, we even noticed that the tests were way faster, which is super cool! 🚀

Automatic migrations in v9

When running ng update @angular/cli, some schematics are going to update your code:

  • aot: true is added to the default configuration in angular.json. The CLI team is now confident that always working in AoT mode is possible and fast enough with Ivy.
  • enableIvy: true is removed (if present) from your TS config, and the include section is updated to only include the files needed.
  • the anyComponentStyle budget, added in CLI 8.2, is automatically added to your project configuration with a 6kb limit warning.
  • (if you are developing a PWA) the ngsw-config.json file is updated to include manifest.webmanifest which is now necessary in modern Chrome versions.
  • the styles and scripts option lazy in angular.json has been deprecated and replaced by a new inject property which has the opposite meaning (lazy: true = inject: false if you don’t want to inject the style or script in your HTML page). The schematic automatically handles the migration.

If your project is a library and not an application, it is also automatically updated. The migration:

  • adds enableIvy: false to the TS config, to ensure compatibility with previous Angular versions.
  • removes the now useless tsickle dependency and the annotateForClosureCompiler from the TS config.

Multiple configurations support

The ng build command now allows to use several configurations! Previously you had to use only one, which was cumbersome because you had to duplicate every property in every configuration.

Now you can use:

ng build --configuration=production,fr

The command then uses the production configuration, merged with the fr configuration. The fr configuration can re-declares a property of the production configuration, to overwrite it.

Generate interceptors

The CLI offers a new generator to easily create interceptors!

ng generate interceptor auth

This creates an AuthInterceptor class in auth.interceptor.ts. But it does not register the interceptor for you. You have to do it manually as you usually do with { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor }. Still nice to have though!

Fun with flags

ng new

The enable-ivy flag has been removed (as Ivy is now enabled by default). A new --strict flag has been added. You can use it to generate a project with a stricter TypeScript configuration (with noImplicitAny, noImplicitReturns, noImplicitThis, noFallthroughCasesInSwitch, and strictNullChecks enabled).

A new option named package-manager has also been added, allowing to specify which package manager you want to use when creating the project. The supported values are npm, yarn, pnpm and cnpm. If you specify a value, then the value is stored in your angular.json file.

ng generate component

You can now specify a type when generating a component:

ng generate component --type container hello

This generates files with a container suffix (hello.container.ts, hello.container.html…), and a component named HelloContainer instead of HelloComponent.

You can also now add a displayBlock flag if you want to generate a style containing :host { display: block; }.

Also note that the styleext and spec flags have been definitely removed, so you must now use style and skipTests instead.

ng generate guard

You can now specify that you want a CanDeactivate guard in addition to the already supported CanActivate, CanActivateChild, and CanLoad. I realized it was missing when working on an application for a customer, so I added it 🤓.

ng serve

The serve command now accepts a --allowed-hosts flag, to specify the hosts allowed to access your application.

ng build

The build command now has an --experimental-rollup-pass flag. The name is explicit enough: this is an experiment! The build leverages Rollup to, hopefully, generate smaller bundles than a raw Webpack build. This is highly experimental and does not work with various dependencies, as they need to be packaged in a format that Rollup can work with. You can give it a try and see by yourself on your project though.

ng update

It is now possible to ask the CLI to create one commit per migration done by using ng update --create-commits.

i18n - internationalization support with @angular/localize

As Angular v9 brings a re-written support for i18n, the CLI also includes some i18n-related changes. In fact there is so much to tell, that the best is to check our dedicated article about internationalization.

TL;DR: it’s now much faster to generate the N localized versions of your application, as the CLI first compiles it, and then just generates the localized versions by replacing the messages by theirs translations in the compiled output. It is even generated in parallel if possible!

As the options to give the serve and build commands changed, the CLI offers an automatic migration from the old to the new options.

Note also a tiny but useful feature: when building an application with i18n, the document locale is now set using the lang attribute on the html root element. For example, if you build your application in French, then the index.html file contains <html lang='fr'>. Until now, you could of course do it by hand, but it was cumbersome. It’s now handled automatically!

ng add for library authors

You may know that the CLI supports a ng add command to add dependencies to your project:

  • if the dependency has an “add” schematic, it runs it.
  • if it does not, ng add simply adds the dependency to your package.json.

So, as a library author, you probably want to offer such a schematic, to simplify the setup of your library. The weird thing was that, if you wrote such a schematic, you had to handle the addition to the package.json file yourself. This has now been simplified with a save option, that you can configure for ng-add in the package.json of the library. save accepts:

  • false, if you don’t want to save it;
  • true or dependencies if you want to save it as a dependency;
  • devDependencies if you want to save it as a dev dependency.

Our own open-source libraries ngx-valdemort and ngx-speculoos are now up-to-date with Angular v9 and offer a ng-add schematics. You can easily give them a try with ng add ngx-valdemort or ng add ngx-speculoos 🙌.

As you can see, this 9.0 release has some interesting new features!

All our materials (ebook, online training and training) are up-to-date with these changes if you want to learn more!


Internationalization with @angular/localize

Some great progress has been made on the i18n front! A new package called @angular/localize has been introduced in Angular 9.0.

It is used under the hood to give us the same features we had previously: translations in templates at compile time.

But it lets us hope for more in the future, with undocumented features already available, like translations in code, or runtime translations instead of compilation translations only 😎.

Let’s start by seeing what we can do with the help of @angular/localize and the CLI in v9.

i18n in templates

The new @angular/localize package offers a function called $localize.

The existing i18n support in Angular now uses $localize, meaning that templates like:

<h1 i18n>Hello</h1>

will be compiled to $localize calls.

If you run ng serve with such a template, you’ll run into a runtime error:

Error: It looks like your application or one of its dependencies is using i18n.
Angular 9 introduced a global `$localize()` function that needs to be loaded.
Please run `ng add @angular/localize` from the Angular CLI.
(For non-CLI projects, add `import '@angular/localize/init';` to your `polyfills.ts` file.
For server-side rendering applications add the import to your `main.server.ts` file.)

The error is self-explanatory: as the i18n attributes are now converted to $localize calls in the generated code, we need to load the $localize function. It could be done by default, but as it is only required if you are using internationalization, it would not really make sense.

That’s why if your application, or one of its dependencies, uses i18n attributes in its templates, then you’ll have to add import '@angular/localize/init' to your polyfills!

The CLI offers a schematic to do this for you. Simply run:

ng add @angular/localize

and the CLI adds the package to your dependencies and the necessary import to your polyfills.

Then when you run your application with a simple ng serve, $localize simply displays the original message.

Now how do you translate these messages?

The process is very similar to what we had previously. First you run ng xi18n to extract the messages in a messages.xlf file. Then you translate the file for your locales, for example messages.fr.xlf and messages.es.xlf.

Then you need to configure the CLI, in angular.json

{
  "projects": {
    "ponyracer": {
      "projectType": "application",
      // ...
      "i18n": {
        "locales": {
          "fr": "src/locale/messages.fr.xlf",
          "es": "src/locale/messages.es.xlf",
        }
      },
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          // ...
          "configurations": {
            "production": {
              // ...
            },
            "fr": {
              "localize": ["fr"]
            },
            "es": {
              "localize": ["es"]
            }
          }
        },
        "serve": {
          // ...
          "configurations": {
            "production": {
              // ...
            },
            "fr": {
              "browserTarget": "ponyracer:build:fr"
            },
            "es": {
              "browserTarget": "ponyracer:build:es"
            }
          }
        }
        // ...
}

Now, the es or fr configurations allow to run:

ng serve --configuration=fr

And the app served is now in French!

You can also build the app with a specific locale:

ng build --configuration=production,es

or with all the locales at once:

ng build --prod --localize

This is a big progress compared to previous Angular versions. We used to have to build the same application for every locale, as the translation was part of the compilation. Now, when Angular compiles the application, it generates $localize calls. Then, when this is done, a tool takes the compiled application and replaces all the $localize calls with the proper translations. This is super fast. You then have a bundle containing no calls to $localize and all i18n strings have been translated.

Until now, you had to build your application once per locale, and this was a full build. So let’s say it’s a 30s build, and you wanted 4 locales, then you were in for 2 minutes.

With the new approach, the compilation is done once, and then the various i18n versions are generated in a few seconds (it is even generated in parallel if possible). So you go from 2 minutes to ~40 seconds! 🌈

You then have several bundles, one per locale, and you can serve the appropriate one to your users depending on their preference as you used to.

This strategy is called compile-time inlining as you inline the translations directly, and then there is nothing left to do at runtime.

Now let’s talk about something still undocumented, that may change in the future, but still interesting to know: we can now also translate messages in our TypeScript code!

The $localize function I’ve been talking about can be used directly. It is a peculiar function that you can use to tag a template string for localization.

But maybe we should start by explaining what a tagged template string is?

Template strings and tag functions

When using template strings, you can define a tag function, and apply it to a template string. Here askQuestion adds an interrogation point at the end of the string:

const askQuestion = strings => strings + '?';
const template = askQuestion`Is anyone here`;

So what’s the difference with a simple function? The tag function in fact receives several arguments:

  • an array of the static parts of the string
  • the values resulting of the evaluation of the expressions

For example if we have a template string containing expressions:

const person1 = 'Cedric';
const person2 = 'Agnes';
const template = `Hello ${person1}! Where is ${person2}?`;

then the tag function will receive the various static and dynamic parts. Here we have a tag function to uppercase the names of the protagonists:

const uppercaseNames = (strings, ...values) => {
  // `strings` is an array with the static parts ['Hello ', '! How are you', '?']
  // `values` is an array with the evaluated expressions ['Cedric', 'Agnes']
  const names = values.map(name => name.toUpperCase());
  // `names` now has ['CEDRIC', 'AGNES']
  // let's merge the `strings` and `names` arrays
  return strings.map((string, i) => `${string}${names[i] ? names[i] : ''}`).join('');
};
const result = uppercaseNames`Hello ${person1}! Where is ${person2}?`;
// returns 'Hello CEDRIC! Where is AGNES?'

i18n with $localize in TypeScript code

$localize uses this mechanic to let us write:

@Component({
  template: '{{ title }}'
})
export class HomeComponent {
  title = $localize`You have 10 users`;
}

Note that you don’t have to import the function. As long as you add import '@angular/localize/init' once in your application, $localize will be added to the global object.

You can then translate the message the same way you would for a template. But, right now (v9.0.0), the CLI does not extract these messages with the xi18n command as it does for templates.

If you serve the application and no translation is found, $localize simply displays the original string, and logs a warning in the console:

No translation found for "6480943972743237078" ("You have 10 users").

So you have to manually add it to your messages.fr.xlf with the given ID if you want to try:

<trans-unit id="6480943972743237078">
  <source>You have 10 users</source>
  <target>Vous avez 10 utilisateurs</target>
</trans-unit>

The template of my HomeComponent then displays Vous avez 10 utilisateurs!

What happens if you have some dynamic expression in your template string?

title = $localize`Hi ${this.name}! You have ${this.users.length} users.`;

The expressions will automatically be named PH and PH_1 (PH is for placeholder). Then you can use these placeholders wherever you want in the translations:

<trans-unit id="4469665017544794242">
  <source>Hi <x id="PH"/>! You have <x id="PH_1"/> users.</source>
  <target>Bonjour <x id="PH"/>&nbsp;! Vous avez <x id="PH_1"/> utilisateurs.</target>
</trans-unit>

But the best practice is to give a meaningful placeholder name to the expression yourself, and you can do so by using the ${expression}:placeholder: syntax.

title = $localize`Hi ${this.name}:name:! You have ${this.users.length}:userCount: users.`;

Then you can use this placeholder wherever you want in the translations:

<trans-unit id="1815172606781074132">
  <source>Hi <x id="name"/>! You have <x id="userCount"/> users.</source>
  <target>Bonjour <x id="name"/>&nbsp;! Vous avez  <x id="userCount"/> utilisateurs.</target>
</trans-unit>

Custom IDs

Note that if you have translations with custom IDs, they are used by $localize (as it was the case previously):

<h1 i18n="@@home.greetings">Hello</h1>

Then your translation looks like:

<trans-unit id="home.greetings">
  <source>Hello</source>
  <target>Bonjour</target>
</trans-unit>

which is obviously nicer to use.

How about for translations in code? $localize also understands a syntax allowing to specify an ID:

title = $localize`:@@home.users:You have 10 users`;

The syntax for the custom ID is the same as in the templates, and the ID is surrounded by colons to separate it from the content of the translation.

As for the template syntax, you can also specify a description and a meaning, to help translators with a bit of context: :meaning|description@@id:message.

For example:

title = $localize`:greeting message with the number of users currently logged in@@home.users:You have 10 users`;

Keep in mind that this is a low level, undocumented API. The Angular team or community will probably offer higher level functions with a better developer experience (well, I hope so!). Locl from Olivier Combe, the author of ngx-translate is probably worth keeping an eye on 🧐.

Runtime translations

As I was mentioning, if you use the CLI commands above (ng serve --configuration=fr or ng build --localize) then the application is compiled and then translated before hitting the browser, so there are no $localize calls at runtime.

But $localize has been designed to offer another possibility: runtime translations. What does it mean? Well, we would be able to ship only one application, containing $localize calls, and before the application starts, we could load the translations we want. No more N builds and N bundles for N locales \o/

Without diving too much into the details, this is already possible with v9, by using the loadTranslations function offered by @angular/localize. But this has to be done before the application starts.

You can load your translations in polyfills.ts with:

import '@angular/localize/init';
import { loadTranslations } from '@angular/localize';

loadTranslations({
  '1815172606781074132': 'Bonjour {$name}\xa0! Vous avez {$userCount} utilisateurs.'
});

As you can see there is no locale consideration: you just load your translation as an object, whose keys are the strings to translate, and the values, their translations.

Now if you run a simple ng serve, the title is displayed in French! And no more need for ng xi18n, or messages.fr.xlf or specific configuration for each locale in angular.json. In the long term, when this will be properly supported and documented, we should be able to load JSON files at runtime, like most i18n libraries do. You could even achieve it in v9, it’s just a bit of manual work, but it’s doable.

What about changing the locale on the fly then? Can we load another set of translations when the application is started? Well, no. The current way $localize calls are generated make them impossible to change after: you have to restart the application. But if you don’t mind refreshing your browser, it’s possible. I tested a simple strategy that works:

  • a user selects a new language (for example Spanish).
  • we store the language in the browser (for example in the localStorage)
  • we reload the page, which restarts the application
  • in polyfills.ts, we start by reading the language stored
  • we load the proper set of translations for Spanish with loadTranslations.

Of course, this will be smoother in the future, either in a future version of Angular, or via a library from the eco-system. Anyway, we’re getting closer to only ship one version of our application, and just load the translations at runtime \o/

All our materials (ebook, online training and training) are up-to-date with these changes if you want to learn more!


Buy more Angular online trainings for less money

We added a nice little feature to our ebook/online training sales platform: you can now buy more licenses at the discounted price you would have paid if you bought all of them at once!

For example, you bought 5 licenses at 686€ (because this is the discounted price for 5 licenses, instead of 995€). Now the rest of the team also wants to learn Angular, so you need to buy 5 more licenses to have 10. Until now it would also have cost you 686€ for these 5 extra licenses. But now it only costs 509€! Why 509? Because the discounted price for 10 licenses is 1195€, minus 686€, what you already paid, gives 509€!

You just have to buy with the same email you used previously, and the discount will be automatically applied. Or you can add licenses from the team management section you usually use to give licenses to your team members.

And it works even if you bought only one license until now: the second one you buy will cost you only 122€ instead of 199€!

Check out our ebook and online training to lean more.


What's new in Angular CLI 8.3?

Angular CLI 8.3.0 is out!✨

If you want to upgrade to 8.3.0 without pain (or to any other version, by the way), I have created a Github project to help: angular-cli-diff. Choose the version you’re currently using (7.2.1 for example), and the target version (8.3.0 for example), and it gives you a diff of all files created by the CLI: angular-cli-diff/compare/7.2.1…8.3.0. It can be a great help along the official ng update @angular/core @angular/cli command. You have no excuse for staying behind anymore!

Let’s see what we’ve got in this release.

Deploy command

Th CLI has gained a new deploy command! In fact it’s a simple alias to ng run MY_PROJECT:deploy, and does really nothing out of the box. If you try it in your project, you’ll see the following message:

Cannot find "deploy" target for the specified project.
You should add a package that implements deployment capabilities for your
favorite platform.
For example:
  ng add @angular/fire
  ng add @azure/ng-deploy
  ng add @zeit/ng-deploy
Find more packages on npm https://www.npmjs.com/search?q=ng%20deploy

As you can see, you need to add a builder to your project, depending on the platform you are targeting. For example if you want to deploy to https://zeit.co/now, you can run ng add @zeit/ng-deploy which will automatically configure your project by adding the necessary configuration to your angular.json file and also create a now.json file. You can then simply run ng deploy when you want to build and deploy your application 🚀.

npm lists a lot of different builders, for example to deploy on Github. The builder builds your application and then pushes the result on the gh-pages branch of your repository.

Faster production builds

You have probably noticed that since the differential loading feature was introduced in Angular CLI 8.0 the production build now runs twice (once for the modern browsers, targeting ES2015, and once for the legacy browser, targeting ES5). The feature itself is really cool, but ng build --prod was effectively taking twice the time. Angular CLI 8.3 changes how the command runs:

  • the ES2015 version is built first
  • than the resulting bundles are directly downleved to ES5, instead of rebuilt from scratch

The larger your application is, the biggest gain you’ll see. I tried it in one of our projects:

  • with CLI 8.2: ng build --prod ran in 31,7s for ES2015 and 28,8s for ES5, for a total of 138s.
  • with CLI 8.3: ng build --prod ran in 32,0s for ES2015 and 🔥~10s🔥 to downlevel to ES5, for a total of 🔥98s🔥.

As this is brand new, if you experience an issue, you can still fall back to the previous behavior with NG_BUILD_DIFFERENTIAL_FULL=true ng build --prod for the moment.

Home page redesign

The home page of a newly generated project has completely changed, and it looks much nicer. It also includes helpful links to begin, and features some common commands.

Look at that 😍:

Angular CLI new home page

As you can see, this 8.3 release has some interesting new features!

All our materials (ebook, online training and training) are up-to-date with these changes if you want to learn more!


What's new in Angular 8.2?

Angular 8.2.0 is here!

Angular logo

All hands are on deck to fix Ivy remaining issues, so not a lot to say for this 8.2 release 🙃. But let’s try nevertheless!

TypeScript 3.5

Angular 8.2 now supports TypeScript 3.5! 🌈

You can check out out what TypeScript 3.5 brings on the Microsoft blog.

ZoneJS 0.10.0

ZoneJS is now part of the main Angular repository, and has also been released in version 0.10.0. You can check out the changelog here.

Ivy Template Type Checker

Ivy has a more accurate template type checker, able to catch much more errors in your templates. But the first iteration was generating errors looking like:

__ng_typecheck__.ts:930:14 - error TS2551: Property 'scoe' does not exist on type 'ScoreComponent'. Did you mean 'score'?

__ng_typecheck__.ts is not part of my application, it was just a big file generated by the compiler to check all the templates. The problem is that the error didn’t give any clue about where the error was…

Now, in version 8.2, we do have proper source location ✨!

src/app/shared/score/score.component.html(1,29): Property 'scoe' does not exist on type 'ScoreComponent'. Did you mean 'score'?

Which is obviously wayyyy better. If you encountered this in a previous attempt to use Ivy, you can give it another go.

All our materials (ebook, online training and training) are up-to-date with these changes if you want to learn more!


What's new in Angular CLI 8.2?

Angular CLI 8.2.0 is out!✨

Of course this brings us the support of the brand new Angular 8.2 version, but also a lot of new features.

If you want to upgrade to 8.2.0 without pain (or to any other version, by the way), I have created a Github project to help: angular-cli-diff. Choose the version you’re currently using (7.2.1 for example), and the target version (8.2.0 for example), and it gives you a diff of all files created by the CLI: angular-cli-diff/compare/7.2.1…8.2.0. It can be a great help along the official ng update @angular/core @angular/cli command. You have no excuse for staying behind anymore!

Let’s see what we’ve got in this release.

Component style budget

Angular CLI supports budgets, a feature to check that your generated bundles are not over a certain size, since version 1.7 (check out our explanation here).

A new budget, anyComponentStyle, has been added in version 8.2, allowing to check your component CSS sizes. This is a very nice feature because I’ve audited quite a few projects where there was no problem in the JS bundles, but where bad practices with CSS imports resulted in bloated CSS files. As component CSS are self-contained, it’s quite easy to make a mistake and import the whole Material or Bootstrap CSS into each component…

You can add this check to your existing budgets by adding the following configuration into angular.json:

"budgets": [
  {
    "type": "anyComponentStyle",
    "maximumWarning": "6kb",
    "maximumError": "10kb"
  }

You can of course adjust the thresholds, but this is the default configuration if you generate a new project. Or you can wait for the v9 release, and the automatic migration will add this configuration for you.

TypeScript configuration changes and build times

If you generate a new application with Ivy enabled, you’ll notice that the tsconfig.app.json file has slightly changed. We went from a configuration that was including every TS file, and excluding the specs:

"files": [
  "src/main.ts",
  "src/polyfills.ts"
],
"include": [
  "src/**/*.ts"
],
"exclude": [
  "src/test.ts",
  "src/**/*.spec.ts"
]

to this configuration, which only lists the entry point:

"files": [
  "src/main.ts",
  "src/polyfills.ts"
],
"include": [
  "src/**/*.d.ts"
]

You may wonder why this is interesting 😀. Well, with the former configuration and under certain conditions, the TypeScript compiler would sometimes pick up redundant files and cause slower builds! The new configuration might speed up the build/rebuild times for large projects!

So why is it not enabled for every project, but only in Ivy ones then? Because View Engine projects allow to lazy load modules with the “magic syntax”

loadChildren: './admin/admin.module#AdminModule'

that TS does not understand, whereas Ivy projects must use the new lazy-loading syntax

loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)

that TS understands. The compiler will pick up changes in the files of the lazy-loaded module as well in an Ivy project or a View Engine (VE) project using the new syntax, but would not be able to catch them in a VE project using the old syntax. You can thus give it try on your project, if you migrated to the new lazy-loading syntax introduced in 8.0.

Note that with this configuration, the compiler now warns you if a file is not used in the build. I spotted a few files that were not used anymore in some of our projects thanks to that new configuration 😅.

Speaking of build time improvements, the CLI is keeping up with the latest Webpack releases, and Webpack 4.38.0 has some really good improvements for rebuild times involving lazy-loaded chunks. If you have a large application with many lazy-loaded modules, you should definitely give a try to CLI 8.2.

index HTML file input/output

The angular.json config lets us configure where the index.html file is in our application. But before 8.2, the same name was used for the output file, which was not super flexible if you wanted to output a file not named index.html or in a different path. This is now possible by using:

index: { input: 'src/index.html', output: 'main.html' }

deferred scripts

The scripts generated by your application are now added to index.html with the defer attribute. This is not the case in the “modern” build, which uses script type="module" that are already deferred by default.

As you can see, this 8.2 release has some interesting new features!

All our materials (ebook, online training and training) are up-to-date with these changes if you want to learn more!


What's new in Angular 8.1?

Angular 8.1.0 is here!

Angular logo

To be honest, not much to say for this 8.1 release 🙃. But let’s try nevertheless!

Ivy

As you may have understood if you read our last blog posts, Ivy is still a big part of the work done by the Angular team for this release. The plan is still to enable it by default in v9, with no regression if possible. As some users have been trying it since v8, the team is squashing bugs in corner cases, and tries to squeeze some perf improvements here and there. Some instructions were redesigned, the styles and animations APIs are getting a makeover: overall it looks good.

And I’m amazed at the amount of work the team has to do to make sure that none of the (thousands of) Google applications using Angular are broken. Which is good news for everybody in the community, because Ivy will have been thoroughly tested when it goes live!

Indexing API

A new package called indexer has been added to the Angular repository. Behind this mysterious name is an API to allow for generation of semantic analysis of components and their templates. So far the scope of the API is very limited, but the idea is to allow language analysis tools to more easily parse Angular templates.

Right now the indexer is targeted for internal Google usage if I understand correctly (with the language analysis tooling called Kythe). But we can hope to see tools built by the community that would use it and allows us to analyze our applications.

That’s all for Angular 8.1 🙂 You can check out our other article about the CLI 8.1 release which contains much more features!

All our materials (ebook, online training and training) are up-to-date with these changes if you want to learn more!


Posts plus anciens