Angular 5, what’s new?

After a release in March 2017 of Angular 4, it is already time to welcome Angular 5 pending Angular 6 scheduled for March 2018. For those who do not know, Angular is the name of a tool set to provided by Google that serves to give happiness to javascript application developers. Happiness because the tool is complete, the community is large and it allows us to focus with pleasure on customer issues.

Among all the changes, we invite you to discover the most significant ones.

Build optimization

As of version 5.0.0, production builds created with Angular CLI will apply the default optimization.

The optimization tool included in Angular CLI has two main objectives. The first is to improve the tree shaking (removing parts of the application that are not needed). The second is to remove the Angular decorators that only the compiler needs.

Each of these actions reduces the size of the build and speeds up the startup of applications.

Angular Universal State Transfer API and DOM Support

Small reminder, Angular Universal allows to have a server-side pre-rendering Angular applications, which allows to take into account the search engines that do not manage javascript.

With Angular 5.0.0, it’s easier to share data between the server version and the client version. This eliminates the need for a duplicate HTTP call to retrieve data when the client version regains control.

Improved transpiler

Improvement of the incremental build, which makes it possible to accelerate the rebuilds notably for the production builds and those with AOT (Ahead-of-Time compilation).

Ability to remove the whitespaces, which until then were kept in templates. It is possible to configure it either at the level of the decorator of each component or at the global level via the file tsconfig.json. The default behavior is “true”, but it is envisaged that in the future it will be “false”.

At the component level :

@Component(
{ 
    templateUrl: 'about.component.html', 
    preserveWhitespaces: false 
} 
export class AboutComponent {}

In the tsconfig.json file :

{ 
   "extends": "../tsconfig.json", 
   "compilerOptions": { 
   "outDir": "../out-tsc/app", 
   "baseUrl": "./", 
   "module": "es2015", 
   "types": [] 
   }, 
   "angularCompilerOptions": { "preserveWhitespaces": false }, 
   "exclude": [ "test.ts", "**/*.spec.ts" ] 
}

Replacing the ReflectiveInjector with the StaticInjector

Again, this eliminates even more polyfills, reducing the size of applications for most developers.

Before :

ReflectiveInjector.resolveAndCreate(providers);

After :

Injector.create(providers);

Improved Zone Speed

Zone is faster by default and it is now possible to bypass zone completely for applications that need performance.

To bypass zone, here is the procedure to follow :

platformBrowserDynamic().bootstrapModule(AppModule, {ngZone: 'noop'}).then( ref => {} );

exportAs

We can now give several names to our components and directives, which is very convenient in case of renaming, it avoids breaking the existing code.

This possibility has been used in the case of prefix change of Angular Material.

Exemple :

@Component(
{ 
   moduleId: module.id, 
   selector: 'a[mat-button], a[mat-raised-button], a[mat-icon-button], a[mat-fab], a[mat-mini-fab]', 
   exportAs: 'matButton, matAnchor', . . . 
}

HttpClient

The old @angular/http module is now officially deprecated and replaced by @angular/common/http, the new HttpClient introduced in 4.3. You can probably expect that @angular/http will be removed in Angular 6.0.

HttpClient has been slightly improved with Angular 5.0, as we are now able to directly use object literals as headers or parameters, whereas we had to use the classes HttpHeaders and HttpParams.

Exemple :

const headers = new HttpHeaders().set('Authorization', 'secret');
const params = new HttpParams().set('page', '1');
return this.http.get('/api/users', { headers, params });

simplified into :

const headers = { 'Authorization': 'secret' };
const params = { 'page': '1' };
return this.http.get('/api/users', { headers, params });

Forms

Forms have a tiny but really useful addition to their API: the ability to decide when the validity and value of a field or form is updated. This is something we already had in AngularJS 1.x, but not yet in Angular.

To do so, the FormControl allows to use an options object as the second parameter, to define the synchronous and asynchronous validators, and also the updateOn option. Its value can be:

  • change, it’s the default: the value and validity are updated on every change;
  • blur, the value and validity are then updated only when the field lose the focus;
  • submit, the value and validity are then updated only when the parent form is submitted.

Exemples :

this.passwordCtrl = new FormControl('', {
 validators: Validators.required,
 updateOn: 'blur'
});
<input [(ngModel)]="user.login" [ngModelOptions]="{ updateOn: 'blur' }">
<form [ngFormOptions]="{ updateOn: 'submit' }">

RxJS 5.5

improved import

Before :

import 'rxjs/add/operator/map'; 
import 'rxjs/add/operator/filter';

After :

import { map, filter } from 'rxjs/operators';

New events for the Router

  • ChildActivationStart
  • ChildActivationEnd

These new events can be used to control the display of a spinner or to measure the performance of a resolver.

Here’s an example of how to start and stop a spinner :

class MyComponent { 
   constructor(public router: Router, spinner: Spinner) { 
      router.events.subscribe(e => { 
         if (e instanceof ChildActivationStart) { 
            spinner.start(e.route); 
         } else if (e instanceof ChildActivationEnd) { 
           spinner.end(e.route); 
         }  
       ); 
     } 
}

I18n

The messages extracted from your application now include the interpolations used in the template.

Before :

<source>
 Welcome to Ponyracer
 <x id="INTERPOLATION"/>
 <x id="INTERPOLATION_1"/>!
</source>

After :

<source>
 Welcome to Ponyracer
 <x id="INTERPOLATION" equiv-text="{{ user.firstName }}"/>
 <x id="INTERPOLATION_1" equiv-text="{{ user.lastName }}"/>!
</source>

A notable change in i18n is that the i18n comments are now deprecated. In Angular 4, you could use:

<!--i18n: @@home.justText -->
 I don't output an element, just text
<!--/i18n-->

Starting with Angular 5, you are encouraged to use an already possible alternative with ng-container:

<ng-container i18n="@@home.justText">
 I don't output an element, just text
</ng-container>

Other breaking changes

Using a different locale than the default one (en-US) now requires to load additional locale data:

import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';

registerLocaleData(localeFr);

All the i18n pipes now take a locale as their last parameter, allowing to dynamically override it:

@Component({
 selector: 'ns-locale',
 template: `
 <p>The locale is {{ locale }}</p>
 <!-- will display 'en-US' -->

<p>{{ 1234.56 | number:'1.0-3':'fr-FR' }}</p>
 <!-- will display '1 234,56' --> 
 `
})
class DefaultLocaleComponentOverridden {
 constructor(@Inject(LOCALE_ID) public locale: string) { }
}

The currency pipe now takes a string as it second parameter, allowing to chose between ‘symbol’ (default), ‘symbol-narrow’ or ‘code’. For example, with canadian dollars:

<p>{{ 10.6 | currency:'CAD' }}</p>
<!-- will display 'CA$10.60' -->

<p>{{ 10.6 | currency:'CAD':'symbol-narrow' }}</p>
<!-- will display '$10.60' -->

<p>{{ 10.6 | currency:'CAD':'code':'.3' }}</p>
<!-- will display 'CAD10.600' -->

if you want to keep the “old” pipes for now, as they have been kept in a new module DeprecatedI18NPipesModule, that you can import if you want to still use them:

@NgModule({
 imports: [CommonModule, DeprecatedI18NPipesModule],
 // ...
})
export class AppModule {

If you have something like this :

platformBrowserDynamic([
 MyCustomProviderA,
 MyCustomProviderB // depends on MyCustomProviderA
]).bootstrapModule(AppModule);

It must now be in 5.0 :

platformBrowserDynamic([
 { provide: MyCustomProviderA, deps: [] },
 { provide: MyCustomProviderB, deps: [MyCustomProviderA] }
]).bootstrapModule(AppModule);

That’s it 🙂