What's new in Angular CLI 1.6?

Angular CLI 1.6.0 is out with some nice new features!

If you want to upgrade to 1.6.0 without pain (or to any other version, BTW), I have created a Github project to help: angular-cli-diff. Choose the version you’re currently using (1.2.1 for example), and the target version (1.6.0 for example), and it gives you a diff of all files created by the CLI: angular-cli-diff/compare/1.2.1…1.6.0. You have no excuse for staying behind anymore!

Let’s see what new features we have!

Service Worker

A new flag has been added to the ng new command and allows to generate an application with service workers already enabled and configured. Service workers are a great new API in the browser, acting like a “client-side proxy”: when your application requests an asset, it will first “ask” the service worker if it is available, before really hitting the network if it isn’t. It can really speed up the second and following visits to your application, and can even be a step to working offline, and towards what’s called a Progressive Web Application (PWA).

This new flag uses the brand new @angular/service-worker package released in v5.0.0. Older versions of the CLI only work with the older version of this package: if you want to use the brand new one, you’ll have to update to the CLI v1.6.

The flag will add a file named ngsw-config.json which is the configuration file for the Angular Service Worker package.

The file looks like this:

{
  "index": "/index.html",
  "assetGroups": [{
    "name": "app",
    "installMode": "prefetch",
    "resources": {
      "files": [
        "/favicon.ico",
        "/index.html"
      ],
      "versionedFiles": [
        "/*.bundle.css",
        "/*.bundle.js",
        "/*.chunk.js"
      ]
    }
  }, {
    "name": "assets",
    "installMode": "lazy",
    "updateMode": "prefetch",
    "resources": {
      "files": [
        "/assets/**"
      ]
    }
  }]
}

This means that the main file is index.html, that you want to prefetch some resources, some of which have a fixed name, while some others are versioned. Some other resources are less critical assets and are loaded lazily (only when needed).

The flag will also add an import to your main module:

imports: [
  BrowserModule,
  ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production })
],

This will activate the ServiceWorkerModule if you are in “prod” mode, and register the service worker generated by the CLI based on the previous config. As the worker is only enabled in “prod” mode, you won’t see a difference using ng serve. It’s only when building your application with ng build --prod that you’ll see a few new files in your dist directory.

As this module is in @angular/service-worker, it will also add this package to your dependencies. And it will turn on the serviceWorker option in your .angular-cli.json.

This is pretty straightforward and gives a great basis. Even if you have an existing application, you can follow these steps and end up with the same result!

You can go much further, and add configuration for your API calls by selecting the strategy you want: always call the server (freshness), or always from cache (performance), etc.

"dataGroups": [
  {
    "name": "races-api",
    "urls": ["/api/races"],
    "cacheConfig": {
      "strategy": "performance",
      "maxSize": 10,
      "maxAge": "30m",
      "timeout": "5s"
    }
  }
]

Here I cache the calls to /api/races for 30 minutes, meaning all requests to this endpoint in the next half hour will return the same result as the first one, even if offline.

You can also add external resources like fonts to the configuration to also cache them.

To give it a try, build your app with ng build --prod, then serve your dist directory with a static HTTP server, like http-server. Open your application with Chrome, and go the Developer Tools in the Application tab. You should see the service worker registered in the Service Worker section! And you can see which parts are cached in the Cache Storage section.

Note that the service worker really caches your application, meaning that a new version deployed in production will not be seen immediately by your users. If you deploy a new version, the application will fetch the new NGSW file on reload, but will use the old one, until your user refreshes the page another time. That’s why you see on some websites a small popup “A new version is available, do you want to refresh?”. If you want to implement such a process, Angular can help you with its service SwUpdate:

export class AppComponent implements AfterViewInit {

  @ViewChild('modal')
  private modalContent: ElementRef;

  constructor(private swUpdate: SwUpdate, private modalService: NgbModal) {
  }

  ngAfterViewInit() {
    if (this.swUpdate.isEnabled) {
      this.swUpdate.available.subscribe(event =>
        // update available: ask the user to reload
        this.modalService.open(this.modalContent).result.then(
          () => this.swUpdate.activateUpdate()
            .then(() => document.location.reload()), // load the update
          () => {} // do nothing if the user wants to refresh later
        )
      );
    }
  }
}

Here I’m subscribing to the available observable exposed by SwUpdate. As soon as a new version of the application is available, the observable will emit an event. In the example above, I chose to open a modal with ng-bootstrap to ask the user to reload the application. If he/she chooses to do so, we load the new version with activateUpdate() and reload the page. If not, we do nothing. You can of course display what you want (just an alert, a toast…).

Universal

One of the main features of this release is the ability to add server side rendering capacity to your application with a simple command. Server-side rendering (called “universal” in Angular) is possible since quite some time, but the CLI had no automated way to do it.

This is now fixed: you just have to run ng generate universal server-app and Angular CLI automatically sets things up for you:

  • creates a new module app.server.module.ts, which uses ServerModule instead of BrowserModule;
  • creates a main file main.server.ts;
  • creates a TypeScript config tsconfig.server.ts
  • adds a dependency to your package.json @angular/platform-server;
  • updates .angular-cli.json to add a new “app”, called “server-app” in my example, in your project;
  • updates your main module to use the brand new (Angular 5) BrowserModule.withServerTransition() which ensures the client app has a smooth transition when it boots;
  • wraps the bootstrap call in a DOMContentLoaded event handler.

You can customize pretty much everything:

  • the application name, by default the one you gave in the command line
  • the clientApp name, by default 0, as you usually have only one app in your CLI project
  • the appId, used by withServerTransition(), by default serverApp
  • and the name of every files mentioned above!

Note that you’ll have to add the deprecated @angular/http package even if you don’t use it, because @angular/platform-server still depends on it.

Now you should be able to build the application!

ng build --prod --app=server-app

And you’ll get the bundles in dist-server/. Than it’s up to you to set up a server to use these, probably a NodeJS server, using something like Express and its ngExpressEngine (that’s the official example in the documentation). The job of the server will be to answer a request with an HTML generated using the JS bundles from dist-server.

Or you can continue reading, and discover another new feature of the CLI ;). You won’t be able to use ng serve with this app though (not yet, but I think it’s coming!).

Application Shell

The other new feature is the Application Shell. A new schematic has been added allowing to generate a “shell” - an index.html containing a static rendering of your application. This uses the universal part I mentioned above for the server-side rendering.

ng g app-shell appShell --universal-app=server-app

The command here uses the serverApp I created above. If you did not create it, the CLI will generate it for you ;)

The command will:

  • update .angular-cli.json to add the appShell key with its configuration "appShell": { "app": "server-app", "route": "shell" };
  • add an AppShellComponent to your application;
  • update the app.server.module.ts to add a route with the path specified pointing to the AppShellComponent.

Now when you’ll build the application with ng build --prod the dist and dist-server directories will be generated, and the index.html file in dist will contain the prerendered AppShellComponent. This means you’ll have a meaningful content served right away when deploying to production (as you can of course put whatever you want in the shell component). When the application is done starting, it will be displayed as usual.

The three features can be combined nicely: you can add a universal/server-side rendering capability to your application, add an application shell to have a fast first render using this capability, and use service worker for the following renderings!

These features are still very new (application shell was still quite buggy when I tested it…), but look promising for the applications that need to start faster.

Check out our ebook, online training (Pro Pack) and training if you want to learn more!



blog comments powered by Disqus