days
-4
-4
hours
-1
-1
minutes
-1
-8
seconds
-3
-2
search
Taking a closer look at the new features!

What’s new in Angular v8?

Manfred Steyer
Angular
© Shutterstock / Ink Drop  

Angular 8 is finally here and includes a preview of Ivy, service worker support, differential loading, and a few other finishing touches. Manfred Steyer explains the most important changes in the newest Angular release.

Angular 8 was just released!

As planned, there were no surprises: the update of the framework and CLI can be done by using ng update and the new features are a welcome addition in line with the motto “evolution instead of revolution”.

In this article, I will cover the most important new features of Angular 8 and the corresponding Angular CLI 8. The examples I will use can be found on GitHub.

First glimpse of Ivy

The next big news the Angular world is waiting for is Ivy, which is the new Angular compiler as well as a new rendering pipeline. Ivy has the potential to generate considerably smaller bundles, it makes incremental compiling easier and is also the basis for future innovations in the Angular sphere.

Since many underlying parts of Angular have been changed here, the Angular team has paid special attention to compatibility with previous Angular versions: after switching to Ivy, existing applications should work just as before. In the best case, noticeably smaller bundles should suffice. That is not selfless, as more than 600 applications at Google are officially based on Angular – the real number, according to rumors, is much higher.

With Angular 8, a preview version of Ivy is now available for testing. The goal of this version is to receive early feedback. Therefore, the Angular team recommends not using Ivy for production just yet, but to rather continue using the classic view engine (Figure 1).

Bundle sizes of a hello world application with and without Ivy (source: ngconf 2019 Keynote by Brad Green and Igor Minar)

Thanks to differential loading (as seen below), the bundle sizes can already be optimized immediately.

As Brad Green, the technical director behind the Angular team at Google, mentions at ngconf 2019, Ivy will allow a noticeable improvement of bundle sizes in compatibility mode in combination with differential loading. Thrill-seekers can thus already try out the future Ivy API. This mode specifically contains a great amount of potential for optimization. The API is still marked as private though. You can tell by looking at its classes and functions: they start with the special character ɵ.

If you already want to try out Ivy, you can generate a new project through the switch enable-ivy:

ng new ivy-project --enable-ivy

This prompts the CLI to store the following configuration entry in the tsconfig.app.json:

"angularCompilerOptions": { 
        "enableIvy": true 
}

This entry may also be added manually after the update to Angular 8 in order to test an existing application with Ivy.
For running the application in debug mode, it is advisable to use AOT:

ng serve --aot

Additionally, it is worth taking a look at the size of the application that was created through ng build. With Angular 9, Ivy should finally be activated by default. Until then, the Angular team plans to work on further measures to ensure compatibility with older versions.

SEE ALSO: React steadily grows, while Angular maintains enterprise hold

Web workers

JavaScript is single threaded by definition. Because of this, it is common for larger tasks like data calls to take place asynchronously. Needless to say, this doesn’t help with elaborate calculations. Those especially are becoming more and more common with extensive JavaScript solutions, which is why we support almost all browser web workers by now. They are scripts that the browser runs in an own thread. Communication with the thread in the browser tab takes place via sending messages.

While web workers have nothing to do with Angular per se, they must be taken into consideration in the build. The goal is to provide one bundle for every web worker. This task is accomplished by the new Angular CLI.

To illustrate this new feature, I will show a JavaScript implementation of the so-called “n queens problem”. The idea is to place one queen per row on a chessboard, without them being able to threaten each other. This means there may be no other queen in the same row, column or diagonal.

A possible solution to the n queens problem

An algorithm to calculate all possible solutions on a chessboard is considered computationally intensive. While the calculation for a regular chessboard with eight rows and eight columns is rather quick, ordinary computers will reach their limits starting at 12 x 12 squares. Determining the solutions for a board with 27 x 27 squares holds the current record. Russian supercomputers were used for this task.

In order to outsource such a calculation to the background, we must first create a web worker using the Angular CLI:

ng generate worker n-queens

This statement not only creates a file for the worker but also for the configuration files required for the build process and entries in existing files. If the same folder contains a component of the same name with the common file extension .component.ts, the CLI even enriches it with code that communicates with the web worker.

The worker itself merely consists of an event listener for the message event:

import nQueens from './n-queens';

addEventListener('message', ({ data }) => {
  const result = nQueens(data.count);
  postMessage(result, undefined);
});

The event is executed when the main thread sends a message to the worker. The parameter contains the information sent from the main thread. In the case we are looking at, it is limited to the property count, which declares the chessboard size. After the calculation of the function nQueens, which is omitted here for lack of space, the event listener sends the result back to the main thread via postMessage. Thereby the browser triggers a message event there.

The class Worker is applied in the using component for interaction with this worker script:

const count = parseInt(this.count, 10);

const worker = new Worker('../logic/n-queens.worker', {
    type: 'module' // Worker uses EcmaScript modules
});

worker.postMessage({count});

worker.addEventListener('message', (event) => {
  // tslint:disable-next-line: no-console
  console.debug('worker result', event.data);

  // Update chessboard
  this.processResult(event.data);
});

The component sends a message with the desired chessboard size to the worker via postMessage and thereby triggers the calculation there. It receives the result via the message event.

In further consequence, the CLI takes care of the correct translation and bundling of the worker scripts. The TypeScript compiler, initiated for this reason, recognizes them by their ending .worker.ts, which is registered in the tsconfig.worker.json generated by ng generate worker. In order to ensure the CLI will not consider these files again when translating and bundling the main application, ng generate worker places the same file pattern in the section exclude of the tsconfig.app.json.

The complete implementation is included in the author’s sample collection [1]. For illustration purposes, the available n queens problem example can be solved in the main thread as well as in a web worker. When you request a solution for a 12 x 12 chessboard, for example, you will see that the UI freezes in the first case, while the background calculation in the worker on the other hand will not reduce operability.

Differential loading

Until now, it was common to compile applications to good old ECMAScript 5, since this “JavaScript of our fathers” runs almost anywhere today. This means that both IE 11 and the web crawler behind the Google search engine can execute the program code.

However, the new ECMAScript 2015 and its subsequent versions are more efficient: these versions allow more compact bundles and the browser can also interpret them more efficiently. Since it was previously common to retreat to ECMAScript 5 as smallest common denominator, other modern browsers unfortunately couldn’t use these advantages.

This ends now: starting with version 8, the CLI includes a feature that is named differential loading. The idea behind it is to provide two groups of bundles: one is based on ECMAScript 5 and addresses older browsers, the other is based on a younger ECMAScript version, e.g. ECMAScript 2015, and offers modern browsers the mentioned advantages.

There isn’t much you need to do in order to activate differential loading: all that is necessary is to set an upper and a lower bar for the ECMAScript versions to be supported. The upper bar is entered in the tsconfig.json as follows:

"target": "es2015"

The lower bar, on the other hand, is defined by a browserslist. It is a file that identifies many browsers to be supported, according to certain criteria like market shares. They can be stored e.g. in the file browserslist, which the CLI meanwhile creates in projectroot when generating a new project:

> 0.5%
last 2 versions
Firefox ESR
not dead
IE 9-11

In the illustrated case, the browserslist points to ECMAScript 5 browsers with the entry IE 9-11. Thereby, the CLI determines the lower bar as this version. If the CLI receives the instruction to create a build (ng build), compiling and bundling processes will take place for both versions:

Build for differential loading

The disadvantage of this process becomes obvious here: the time required for the build process is doubled.

The different browsers can now decide which version of the bundles to load. For this, they receive the script references in the index.html additions: those pointing to ECMAScript 5 bundles receive the addition nomodule. This prevents browsers with support for ECMAScript modules, and thereby ECMAScript 2015 and newer, from ignoring the reference. The ECMAScript 2015+ bundles, on the other hand, are implemented by the CLI via type=”module”. Thus, older browsers will ignore these script tags:

<script src="main-es2015.js" type="module"></script>

<script src="main-es5.js" nomodule></script>

In contrast to ng build, all other CLI commands use only (!) the upper bar. In the illustrated case, that is ECMAScript 2015. This happens, among others, for efficiency reasons: especially during debugging and testing, developers want to see a result as soon as possible without being required to wait for a second build.

Lazy loading

Since the first days of Angular, the router has supported lazy loading. Until now, this was accomplished by identifying a magic value of the loading module:

{
    path: 'lazy',
    loadChildren: () => './lazy/lazy.module#LayzModule'
}

The value before the hash sign represents the path which leads to the file with the module implementation; the value afterwards stands for the therein contained class. This style of writing also works with Angular 8, but has been deprecated in favor of dynamic ECMAScript imports:

{
{
    path: 'lazy',
    loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)
}

The new writing style still contains the file name as magic value. However, since import is supported by many IDEs, invalid values will immediately return an error.

Breaking change in ViewChild and ContentChild

There is a breaking change in the usage of ViewChild and ContentChild, which unfortunately didn’t always show a consistent behavior in the past. While they were used in earlier versions for a component to request an element not inside a structural directive like ngIf or ngFor, the query result was already available in ngOnInit. Otherwise, the program code could access it at the earliest in ngAfterViewInit (or ngAfterContentInit for ContentChild). For elements that were only loaded into the DOM at a later time due to data binding, the program code had to insert ngAfterViewChecked or, respectively, ngAfterContentChecked.

As this behavior was confusing, the component must now specify when the resolution should take place:

@ViewChild('info', { static: false })
  paragraph: ElementRef;

If static has the value true, Angular tries to find the element when initializing the component. This only works if it is not in a structural directive. When using static: false, the resolution takes places after initiating or refreshing the view.

The command ng update automatically tries to enter the correct value here. If that is not possible, it adds a comment with a TODO in its place.

Queries with the related decorators ViewChildren and ContentChildren are not affected by this change. They have always exhibited a dynamic behavior in the sense of static: false.

New features for ngUpgrade

Up until now, one problem in the hybrid operation of AngularJS 1.x and Angular with ngUpgrade has been that the routers of both frameworks have at times been dueling over the URL. This led to side effects that were difficult to comprehend. In order to avoid this, the possibility of using the identical Location service for accessing the URL in both framework versions has been added.

To achieve this, the Angular team has extended the possibilities of Angular’s Location services and thereby provided a substitute for $location in AngularJS.

For this reason, the new method onUrlChange for monitoring URL changes, among other modifications, has been added in the Location service:

export class AppComponent {
  constructor(loc: Location, pLoc: PlatformLocation) {
    loc.onUrlChange((url) => console.debug('url change', url));
    console.debug('hostname: ', pLoc.hostname);
  }
}

The PlatformLocation service offers additional access to the individual parts of the URL. A detailed description on how the $location substitute, which is based on it, is used for a better interlacing of both frameworks, can be found here. Additionally, there you can now find an idea for lazy loading of AngularJS, which is based on the aforementioned dynamic ECMAScript imports.

Conclusion

Once again, the Angular team has kept its word: migrating to the new Angular version is easy and doesn’t include large changes. Instead, some edges were rounded, which makes working with Google’s SPA framework even more comfortable. Differential loading creates a consciousness for the possibility of further optimization of bundle sizes if older browsers are either not supported or supported with separate bundles. The web worker support shows that computationally intensive tasks are increasingly finding their way into the browser. Enthusiasts can now try taking their first steps with Ivy.

Author
Manfred Steyer
Manfred Steyer (https://www.softwarearchitekt.at/) is a trainer and consultant who supports software teams throughout the German-speaking world in the development of Angular. He is Trusted Collaborator in the Angular Team, was awarded as Google Developer Expert and writes for O'Reilly, the German Java Magazine and Heise Developer. In his latest book on Angular, he covers the many sides of the popular JavaScript framework written by Google.

Leave a Reply

Be the First to Comment!

avatar
400
  Subscribe  
Notify of