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 and

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

  "projects": {
    "ponyracer": {
      "projectType": "application",
      // ...
      "i18n": {
        "locales": {
          "fr": "src/locale/",
          "es": "src/locale/",
      "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 = => name.toUpperCase());
  // `names` now has ['CEDRIC', 'AGNES']
  // let's merge the `strings` and `names` arrays
  return, 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:

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

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 ${}! 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>

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 ${}: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>

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">

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';

  '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 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

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, 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 file has slightly changed. We went from a configuration that was including every TS file, and excluding the specs:

"files": [
"include": [
"exclude": [

to this configuration, which only lists the entry point:

"files": [
"include": [

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!


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!

What's new in Angular CLI 8.1?

Angular CLI 8.1.0 is out!✨

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

If you want to upgrade to 8.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 (7.2.1 for example), and the target version (8.1.0 for example), and it gives you a diff of all files created by the CLI: angular-cli-diff/compare/7.2.1…8.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, and start with new options for several commands.

Fun with flags

ng generate component –skip-selector

It is now possible to generate a component without a selector, by using the --skip-selector option. This can be handy when you are generating components that will only be instantiated dynamically, for example by the router. In that case, the component doesn’t really need a selector, so this option allows you to generate a component skeleton without one.

ng generate module –route

The module schematics can now take a --route option to indicate that you want to generate a lazy-loaded module. For example:

ng generate module admin --module users --route admin

will generate a new module AdminModule in its own directory, as it already previously did. But it will now also automatically add a route (here admin), to lazy-load this module in the (already existing in my example) users.module.ts:

export const USERS_ROUTES: Routes = [
  // automatically added by the schematic
  { path: 'admin', loadChildren: () => import('../admin/admin.module').then(m => m.AdminModule) }

  imports: [CommonModule, ..., RouterModule.forChild(USERS_ROUTES)],
  declarations: [...]
export class UsersModule {}

Note that it also creates a new component AdminComponent, already registered in the route configuration of the new AdminModule:

const routes: Routes = [
  { path: '', component: AdminComponent }

  declarations: [AdminComponent],
  imports: [CommonModule, RouterModule.forChild(routes)]
export class AdminModule { }

ng test –include

You can now specify a file or a directory when running ng test thanks to the new --include flag:

# all specs in src/app/admin/*
ng test --include app/admin
# just src/app/user.service.spec.ts
ng test --include app/user.service.spec.ts

You could already focus a test suite by changing the describe function to fdescribe or the it function to fit, but ng test was still rebuilding all your tests. By specifying --include, only the provided files are rebuilt, so it’s faster and doesn’t care if you have compilation errors in other files 🌈. And of course, you won’t forget to replace fdescribe by describe before committing anymore.

ng doc –version

ng doc is not very well-known but it is quite handy, as you can search the official documentation directly from your command line. For example ng doc component opens your browser to the search results for component on The command now allows to specify a specific version to search, for example ng doc --version 6 component. It is also possible to use ng doc --version next to search the docs of the next Angular version. And if you don’t specify a version, the command now searches the docs for the Angular version you are using in your project (whereas it was looking in the docs of the most recent Angular version until now). I’m really happy about this tiny new feature, because it was the first open-source contribution of a developer I mentored during the HackCommitPush event in Paris! 🚀

ng build –cross-origin

You can now define the crossorigin attribute setting of elements that provide CORS support. 3 values are currently possible, very similar to what the official specification allows:

  • none which doesn’t do anything and is the default. CORS will not be used at all.
  • anonymous which automatically adds crossorigin="anonymous" to your scripts. There will be no exchange of user credentials via cookies, client-side SSL certificates or HTTP authentication, unless it is in the same origin. That still checks for the origin though.
  • use-credentials which automatically adds crossorigin="use-credentials" to your scripts, meaning that user credentials will be needed even if the file is from the same origin.

ng upgrade –allow-dirty

ng upgrade has now a new --allow-dirty option to allow updating when the repository contains modified or untracked files. Previously, trying to run ng upgrade in a project with unsaved modifications was always resulting in an error.

AoT by default for Ivy

The framework team has been working hard at squashing bugs in Ivy, and also improved the build mechanisms, resulting in faster AoT builds. The CLI team now considers that the performances with AoT and Ivy enabled are good enough to always serve your application with AoT enabled. If you generate a new project with --enable-ivy, you’ll see that the aot option in angular.json is now true by default. In their experiments, the CLI team found that the AoT builds with Ivy enabled were much faster than with View Engine, and pretty much on par with JiT builds. I gave it a try on one of our projects, and here are the results of how much time rebuilds are taking after a modification:

Ivy disabled, JiT mode: 190-250ms (initial 11.5s)
Ivy disabled, AoT mode: 630-1700ms (initial 11.8s)
Ivy enabled, AoT mode: 210-270ms (initial 13.0s)

So I have a slightly slower initial compilation, but then the AoT rebuilds are roughly as fast as the JiT rebuilds without Ivy✨!

TypeScript configuration changes

A side-note about the TypeScript configuration to conclude. If you check out the differences between version 8.0 and 8.1, you’ll notice that the tsconfig.json file lost the emitDecoratorMetadata option. This option was only needed for JiT development for the dependency injection (you can read an old blog post of mine about that). As it was only useful for that use-case, the CLI is now handling it itself when building your application in dev mode. So you can now remove this option from your project!

The default configuration now includes downlevelIteration, a TS compiler option to enable iterating over iterators. Check out this article if you want to have a better understanding of this option.

You will also note that two angularCompilerOptions are now enabled by default:

"angularCompilerOptions": {
  "fullTemplateTypeCheck": true,
  "strictInjectionParameters": true

The first one is one of my favorites, as it allows a deeper check of your templates. I explained this option when it was introduced in Angular 5.0. strictInjectionParameters errors when an injection type can’t be determined.

As you can see, this 8.1 release was packed with new features!

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

Proxies in JavaScript

ES2015 introduced a new feature called Proxy, allowing us to do some cool meta-programming code in our applications 🤓.


JavaScript always had some meta-programming capabilities. The use of get and set are a good example. If you have a basic object:

const pony = { name: 'Rainbow Dash' };

You can use:

// logs 'Rainbow Dash'

But you can also define a getter in your object, and do something every time the property is accessed. Even if, from the outside, it still looks like a simple property access:

const pony = {
  get name() {
    console.log('get name');
    return 'Rainbow Dash';
// logs 'get name'
// logs 'Rainbow Dash'

But what if you want to do so on an existing object that you did not write yourself? Well, JavaScript offers Object.defineProperty since circa 2011:

const pony = { name: 'Rainbow Dash' };
Object.defineProperty(pony, 'name', {
  get() {
    console.log('get name');
    return 'Rainbow Dash';

This is pretty cool, and lots of libraries and frameworks rely on this. Vue.js, for example, relies on this mechanism to trigger a re-rendering of the components displayed on a page, every time the state of one of the components is updated (this is a bit of a simplification but this is roughly the gist of it, see the source code of defineReactive if you want to learn more). To do so, the framework rewrites every property with a setter, that just sets the initial property, but also “warns” the framework that the property changed.

Object.defineProperty(component, 'user', {
  set() {
    this.user = user;
    heyVueAPropertyChanged(); // 🔥

And it does this for every property declared when the component is initialized. This is cool, but doesn’t cover some very simple use cases. For example, adding a property to an existing object after initialization:

component.newProperty = 'hello';
// won't call heyVueAPropertyChanged() 😢

To support that case, Vue 2.x requires the usage of Vue.set(component.newProperty, 'hello'), which is not really intuitive, but does the job.

Object.defineProperty only works with objects, as the name says, and not with other types, like arrays. So again, in Vue 2.x, you can’t use myArray[3] = 'hello', because Vue won’t pick it up 😭 (see the official documentation).

Proxies to the rescue

This is where proxies, a new JavaScript feature adopted in the ES2015 specification, can be handy. 🌈

Proxies are a general computer-science concept that describes an intermediary between a caller and a callee. In ES2015, a proxy can target objects, but also arrays, functions, or… other proxies (but not other built-in types like Date).

const target = {};
const handler = {};
const uselessProxy = new Proxy(target, handler);

A nice thing here is that the type of uselessProxy is not Proxy but object (the type of the target). But there is no strict equality: uselessProxy === target; // false. The handler can do some smarter things, like ‘trapping’ properties:

const handler = {
  get(obj, prop) {
    console.log(`${prop} was accessed`);
    return obj[prop];
const target = { foo: 'bar' };
const proxy = new Proxy(target, handler);;
// logs 'foo was accessed'
// logs 'bar'

There are a lot of different traps available: get and set of course, but also has, apply, construct, defineProperty, deleteProperty, etc…

For Vue.js, this is very interesting, as proxies solve the issue of dynamic property added:

const handler = {
  set(obj, prop, value) {
    obj[prop] = value;
    console.log(`${prop} was updated with ${value}`);
const target = { };
const proxy = new Proxy(target, handler); = 'bar';
// logs 'foo was updated with bar'

That’s why Vue.js 3.0 will use Proxies instead of Object.defineProperty and will not need Vue.set (most probably, as Vue 3.0 is still a closed source prototype at the time of writing). And as mentioned, it also works for arrays, so myArray[3] = 'hello' will also be picked up by a proxy (and Vue 3).

Note that you can build revocable proxies with

const { proxy, revoke } = Proxy.revocable(target, handler);
// TypeError: illegal operation attempted on a revoked proxy

Also note that proxies come with a performance cost. It’s always a bit hard to measure and compare, but it is still several times slower than defineProperty for setting a property for example. Nevertheless it would be possible to imagine proxies getting popular, especially in UI frameworks. Someone can imagine building a library for React that would avoid calling setState (and of course someone has) or not using ZoneJS in Angular, and use a proxy-based reactivity system. This was even considered by the core team, but declined.

We’ll see if Vue 3.0 bets on this, and how it comes of!

What's new in Angular 8.0?

Angular 8.0.0 is here!

Angular logo

A personal announcement first: I’m now officially part of the Angular team as a collaborator, in an effort from the core team to include more developers from the community. Angular 8.0 has a little bit more code of mine than the other releases 😊.

This release is mostly about Ivy and the possibility to give it a try, but it also includes a few features and breaking changes. Hopefully the update should be very easy, as the Angular team wrote a bunch of schematics that will do the heavy lifting for you.

TypeScript 3.4

Angular 8.0 now supports TypeScript 3.4, and even requires it, so you’ll need to upgrade.

You can checkout out what TypeScript 3.3 and TypeScript 3.4 brings on the Microsoft blog.


Ivy is obviously a huge part of this release, and it took most of the effort from the team these last month. There is so much to say about Ivy that I wrote a dedicated article about it.

TL;DR: Ivy is the new compiler/runtime of Angular. It will enable very cool features in the future, but it is currently focused on not breaking existing applications.

Angular 8.0 is the first release to officially offer a switch to opt-in into Ivy. There are no real gains to do so right now, but you can give it a try to see if nothing breaks in your application. Because, at some point, probably in v9, Ivy will be the default. So the Angular team hopes the community will anticipate the switch and provide feedback, and that we’ll catch all the remaining issues before v9.

We tried it on several of our apps and already caught a few regressions, so we would strongly advise to not use it blindly in production 😄.

If you feel adventurous, you can add "enableIvy": true in your angularCompilerOptions, and restart your application: it now uses Ivy! Check our article and the official guide for more info.

Bazel support

As for Ivy, we wrote a dedicated article on how to build your Angular applications with the new Bazel support 🛠.




The AbstractControl class now offers a new method markAllAsTouched in addition to the existing markAsDirty, markAsTouched, markAsPending, etc. AbstractControl is the parent class of FormGroup, FormControl, FormArray, so the method is available on all reactive form entities.

Like markAsTouched, this new method marks a control as touched but also all its descendants.



The FormArray class now also offers a clear method, to quickly remove all the controls it contains. You previously had to loop over the controls to remove them one by one.

// `users` is initialized with 2 users
const users = fb.array([user1, user2]);
// users is now empty


Lazy-loading with import() syntax

A new syntax has been introduced to declare your lazy-loading routes, using the import() syntax from TypeScript (introduced in TypeScript 2.4.

This is now the preferred way to declare a lazy-loading route, and the string form has been deprecated. This syntax is similar to the ECMAScript standard and Ivy will only support this.

So you can change your loadChildren declarations from:

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


loadChildren: () => import('./races/races.module').then(m => m.RacesModule)

A schematic offered by the CLI will automatically migrate your declarations for you, so this should be painless if you run ng update @angular/cli. Check out our article about Angular CLI 8.0 to learn more about that.


To help people migrating from AngularJS, a bunch of things have been added to the location services in Angular.

PlatformLocation now offers access to the hostname, port and protocol, and a new getState() method allows to get the history.state. A MockPlatformLocation is also available to ease testing. All this is really useful if you are using ngUpgrade, otherwise you probably won’t need it.

Service worker

Registration strategy

The service worker registration has a new option that allows to specify when the registration should take place. Previously, the service worker was waiting for the application to be stable to register, to avoid slowing the start of the application. But if you were starting a recurring asynchronous task (like a polling process) on application bootstrap, the application was never stable as Angular considers an application to be stable if there is no pending task. So the service worker never registered, and you had to manually workaround it. With the new registrationStrategy option, you can now let Angular handle this. There are several values possible:

  • registerWhenStable, the default, as explained above
  • registerImmediately, which doesn’t wait for the app to be stable and registers the Service Worker right away
  • registerDelay:$TIMEOUT with $TIMEOUT being the number of milliseconds to wait before the registration
  • a function returning an Observable, to define a custom strategy. The Service Worker will then register when the Observable emits its first value.

For example, if you want to register the Service Worker after 2 seconds:

providers: [
  ServiceWorkerModule.register('/ngsw-worker.js', {
    enabled: environment.production,
    registrationStrategy: 'registerDelay:2000'
  // ...

Bypass a Service Worker

It is now also possible to bypass the Service Worker for a specific request by adding the ngsw-bypass header.

this.http.get('api/users', { headers: { 'ngsw-bypass': true } });

Multiple apps on sub-domains

Previously, it was not possible to have multiple applications using @angular/service-worker on different sub-paths of the same domain, because each Service Worker would overwrite the caches of the others… This is now fixed!

Notable and breaking changes

A few things have changed and require some work from your part. Some of the changes are driven by Ivy, and are there to prepare our applications. But the cool news is that the Angular team already wrote schematics to make our life easier.

Simply run ng update @angular/core and the schematics will update your code. What do these schematics do? Let’s find out!

Queries timing

The ViewChild and ContentChild decorators now must have a new option called static. Let me explain why with a very simple example using a ViewChild:

<div *ngIf="true">
  <div #dynamicDiv>dynamic</div>

Let’s get that element in our component and log it in the lifecycle hooks ngOnInit and ngAfterViewInit:

@ViewChild('dynamicDiv') dynamicDiv: ElementRef<HTMLDivElement>;

ngOnInit() {
  console.log('init dynamic', this.dynamicDiv); // undefined

ngAfterViewInit() {
  console.log('after view init dynamic', this.dynamicDiv); // div

Makes sense as AfterViewInit is called when the template initialization is done.

But in fact, if the queried element is static (not wrapped in an ngIf or an ngFor), then it is available in ngOnInit also:

<h1 #staticDiv>static</h1>


@ViewChild('staticDiv') staticDiv: ElementRef<HTMLDivElement>;

ngOnInit() {
  console.log('init static', this.staticDiv); // div

ngAfterViewInit() {
  console.log('after view init static', this.staticDiv); // div

This was not documented, or recommended, but that’s how it currently works.

With Ivy though, the behavior changes to be more consistent:

ngOnInit() {
  console.log('init static', this.staticDiv); // undefined (changed)

ngAfterViewInit() {
  console.log('after view init static', this.staticDiv); // div

A new static flag has been introduced to not break existing applications, so if you want to keep the old behavior even when you’ll switch to Ivy, you can write:

@ViewChild('static', { static: true }) static: ElementRef<HTMLDivElement>;

and the behavior will be the same as the current one (the element is also accessible in ngOnInit).

Note that if you add static: true on a dynamic element (wrapped in a condition or a loop), then it will not be accessible in ngOnInit nor in ngAfterViewInit!

static: false will be how Ivy behaves by default.

To not break existing applications and to ease the migration, the Angular team wrote a schematic that automatically analyzes your application, and adds the static flag. It even offers two strategies:

  • one based on your templates, which will make sure that your application works (so it tends to mark queries as static even when they aren’t). You are sure it works, but it exposes you to problems if you wrap your static element in a condition or a loop later.
  • one based on your usage of the query, which is more error-prone (as it is harder for the schematic to figure it out), but will not mark the queries as static if they don’t need to be. So most queries will have static: false, which will be the default in Ivy.

The first strategy is used by default when you run ng update because it is the safest, but you can try the usage strategy by using NG_STATIC_QUERY_USAGE_STRATEGY=true ng update.

You can check out the official guide for more information.

This is what the migration looks like (with a failure in one component):

------ Static Query Migration ------
With Angular version 8, developers need to
explicitly specify the timing of ViewChild and
ContentChild queries. Read more about this here:

Some queries could not be migrated automatically. Please go
those manually and apply the appropriate timing.
For more info on how to choose a flag, please see:
⮑   home/home.component.ts@43:3: undefined

Note that this only concerns ViewChild and ContentChild, not ViewChildren and ContentChildren (which will work the same way in Ivy and View Engine).

Template variable reassignment

Currently with View Engine, doing something like:

  *ngFor="let option of options"
  (click)="option = 'newButtonText'">{{ option }}</button>


In Ivy, that won’t be the case anymore: it will not be possible to reassign a value to a template variable (here option). To prepare the switch to Ivy, a schematic analyzes your templates when you upgrade to Angular 8.0 and warns you if that’s the case.

You then have to manually fix it:

  *ngFor="let option of options; index as index"
  (click)="options[index] = 'newButtonText'">{{ option }}</button>


The DOCUMENT token moved from @angular/platform-browser to @angular/common. You can manually change it in your application, but a provided schematic will take care of it for you.

Deprecated webworker package

The @angular/platform-webworker package enabled running your Angular application in a Web Worker. As this proved trickier than expected (for building the application, SEO…), and not that good performance-wise, the package has been deprecated and will be removed in the future.

Deprecated HTTP package removed

@angular/http has been removed from 8.0, after being replaced by @angular/common/http in 4.3 and officially deprecated in 5.0, 18 months ago. You have probably already migrated to @angular/common/http, but if you didn’t, now you have to: the provided schematic will only remove the dependency from your package.json.

You can also find all the deprecated APIs in the official deprecations guide.

That’s all for Angular 8.0! You can check out our other articles about Ivy, the CLI 8.0 release or the new Bazel support.

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.0?

Angular CLI 8.0.0 is out!✨

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

If you want to upgrade to 8.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 (7.2.1 for example), and the target version (8.0.0 for example), and it gives you a diff of all files created by the CLI: angular-cli-diff/compare/7.2.1…8.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!

Angular 8 and TypeScript 3.4 support

This was obviously expected: Angular 8.0 is now supported, and even required, by the CLI. Note that as Angular 8.0 now requires TypeScript 3.4, you will need to also update your TypeScript version. You can checkout out what TypeScript 3.3 and TypeScript 3.4 brings on the Microsoft blog.

The CLI now also supports the new style of lazy-loading declarations using TypeScript import, introduced in Angular 8.0. It is even required if you want to use Ivy (see below).

So you can change your loadChildren declarations from:

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


loadChildren: () => import('./races/races.module').then(m => m.RacesModule)

The import feature was introduced in TypeScript 2.4 and is similar to the ECMAScript standard. If you want to use import in your CLI project, you must enable it by using "module": "esnext" in the root tsconfig.json file.

If you updated your CLI version by running ng update @angular/cli you won’t even have to do it manually, as an update schematic will automatically take care of it for you!

Differential loading

This is one of the cool new features of the CLI 8.0.0 as it allows to specify which browsers you want to target, and the CLI will automatically build the necessary JS bundles with the required polyfills for your targets.

A first step was done in this direction with CLI 7.3 and its conditional ES5 browser polyfill loading. This release goes one step further.

The default target in tsconfig.json is now es2015 which means the default target of ng build is now the modern browsers that support ES6 features. But if you need to support older browsers like IE9, then you can specify it by using the browserslist file. This file already exists in your CLI project but was use for the CSS part only. It is now also used for the JS generation.

The default content is:

> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

You can check out the details on the browserslist repository, but a cool trick is to run:

npx browserslist

in your project to see what your current browserslist configuration actually means 🚀. Or you can also check and enter your query.

The CLI uses this configuration to generate only one “modern” build if you only target modern browsers, or build the application twice if you asked for IE 9-11 support for example. The dist directory then contains the same bundle twice after ng build --prod:

main-es2015.407919b7ee8dd339e9bc.js      // smaller version
main-es5.145b0a190c32187f268c.js         // slightly bigger one
polyfills-es2015.40c67a1b836fd165fb67.js // 43Kb
polyfills-es5.b96063cf2c012927fe18.js    // 110Kb
runtime-es2015.293c0dc955d5bcd5c818.js   // same size
runtime-es5.645890afb0d6a6596d07.js      // same size

The index.html file references all of them, but the es5 scripts are marked with the nomodule attribute, and the es2015 scripts have a type="module" attribute. The nomodule attribute indicates to modern browsers (that supports ECMAScript modules) to ignore this script, so they are not even fetched on modern browsers. And the older browsers will ignore the scripts with type="module". So each browser loads only what it really needs, with only older browsers downloading the extra JS needed.

The default configuration is the recommend one, and will trigger the two builds. You currently have to set a very restricted query to only have the es2015 build, like > 4% (to get rid of UC Browser for Android which is still used a lot).

Note that core-js has been updated to v3, and is now directly handled by the CLI itself, so it’s no longer needed as a dependency of your application. Also the property es5BrowserSupport introduced in CLI 7.3 is now unnecessary and has been deprecated.

dart-sass replaces node-sass

The CLI now uses dart-sass instead of node-sass to build your Sass files. The Dart implementation of Sass is now the reference implementation and has replaced the historic Ruby one. It is also notoriously faster.

It should not have an impact on the generated files, but there are some differences between the implementations and I heard that the compiler might be slightly stricter.

You can also install fibers if you want to speed things up (npm install --save-dev fibers). Vue CLI, which also recently migrated to dart-sass, mentions that the compilation can be twice as fast with fibers installed.

Note that all of this was already possible previously, but it is now the default, and is technically a breaking change. You can still use node-sass if you wish, by installing it explicitly.

Ivy support

You probably know that the main feature in Angular 8.0 is Ivy. You can checkout our dedicated article to Ivy to learn more The CLI provides a switch to give it a try. You can test it in a new project with:

ng new project --enable-ivy

Or in a existing project by adding the following option in your file:

"angularCompilerOptions": {
  "enableIvy": true

When using Ivy, you need to compile your third party Angular modules with ngcc (the Angular Compatibility Compiler). This tool generates the code necessary to compile your application with Ivy enabled (by generating the ngComponentDef field, ngModuleDef field, etc. for each dependency you use).

But no need to worry about it, as the CLI now has a Webpack plugin that takes care of it for you! Your workflow will not change, even if behind the scenes the CLI does an extra-step for you when necessary.

If you give it a try, remember that the Ivy support is still quite new 😋.

Web worker support

This version provides a new generate schematic to add a Web Worker to one of your component. This can be handy if you want to delegate a computational heavy task to a dedicated thread in the browser instead of blocking the main one.

Let’s say that your PictureComponent (in src/app/picture) needs to do some CPU intensive work, like applying filters to an image.

You can run:

ng generate web-worker picture/picture

This will generate a new file called picture.worker.ts in src/app/picture, containing the following boilerplate code:

addEventListener('message', ({ data }) => {
  const response = `worker response to ${data}`;

That’s where you would put your heavy code computation.

and adds this other boilerplate code to your PictureComponent:

if (typeof Worker !== 'undefined') {
  // Create a new
  const worker = new Worker('./picture.worker', { type: 'module' });
  worker.onmessage = ({ data }) => {
    console.log(`page got message: ${data}`);
} else {
  // Web Workers are not supported in this environment.
  // You should add a fallback so that your program still executes correctly.

to get you started.

The sample posts a simple string, but you can post an object or an array. It will be serialized and then deserialized so the worker receives a copy.

The schematic will also configure your CLI project if this is the first time you add a Web Worker. It will exclude the worker.ts files from your main TypeScript configuration, and add a new TypeScript configuration named tsconfig.worker.json that handles the worker.ts file. The angular.json file is also modified to add:

"webWorkerTsConfig": "tsconfig.worker.json"

Then, when you’ll run ng build, the CLI will package the Web Worker in a dedicated bundle (using googlechromelabs/worker-plugin).

Note that this is different from running Angular itself in a Web Worker via @angular/platform-webworker, which is not yet supported in Angular CLI.

Usage analytics data

The CLI can now collect usage analytics data. If you opt-in, some stats are collected and sent to the CLI team, to help them prioritize features and improvements. You can’t really miss this new feature, as you’ll be asked after installing the new CLI version globally 🧐.

You can opt-in with ng analytics on. A few metrics are collected if you opted in globally: command used, flags used, OS, Node version, CPU count and speed, RAM size, command initialization and execution time, and errors with their crash data if any occurs. If you opted-in in the project, it will even collect for build commands the number and size of your bundles (initial, lazy and total), the assets, polyfills and CSS sizes, and the number of ngOnInit in your code.

If you use ng update to update you CLI project, you will be asked about whether you want to collect and send analytics or not (or when you install the CLI globally).

Would you like to share anonymous usage data with the Angular Team at Google under
Google’s Privacy Policy at For more details and
how to change this setting, see

You can manually trigger the prompt again with ng analytics prompt. You can turn it off at any time with ng analytics off.

You can find more details on the official documentation.

I think this will be really helpful for the CLI team, as they’ll have a real insight about what’s going on in the real world (build time, test time, build sizes, etc.).

It is also possible to gather these usage analytics in your own Google Analytics, to see how your teams are using the CLI. This can be configured globally with ng config --global cli.analyticsSharing.tracking UA-123456-12. More information about that can be found here.

Project layout change

You’ll notice that the project layout changed quite a bit:

  • there is no more a dedicated project for e2e tests in the angular.json file
  • the tsconfig.*.json, karma.conf.js files have migrated to the root of the workspace, and the relative paths they contained were updated to reflect that.

SVG templates support

It is now possible to have a file with an svg extension as template (instead of only HTML previously). This was the first PR from @oocx and he detailed the feature himself in an article that you can check if you want to learn more.

Codelyzer 5.0

The default TSLint configuration loads additional rules from the excellent Codelyzer, which has recently been released in version 5.0. As this new version of Codelyzer renames some of its rules, the CLI offers a schematic to automatically update your TSLint configuration when you upgrade using ng update @angular/cli. Note that Codelyzer v5 also offers new rules and deprecated some, take a look at the changelog if you want to learn more.

The update schematic will also removes the es6 imports from your polyfills.ts file, as they are now added automatically if needed by the CLI (see our article about CLI 7.3).

NPM/PNPM support

After NPM and Yarn, The CLI now supports another package manager: PNPM.

Also note that the ng add command gained a new --registry flag, allowing to add packages from a private NPM registry (ng update already offered it).

New Architect API

This is just a note about the internals of the CLI. The Architect API, responsible for running pretty much everything under the hood, has been completely overhauled. Check out this blog post on the official Angular blog if you want to learn more about it.

As you can see, this 8.0 release was packed with new 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