Книга: Angular 2 Cookbook
Назад: 10. Performance and Advanced Concepts
Дальше: Working with zones outside Angular

Understanding and properly utilizing enableProdMode with pure and impure pipes

Angular 2's change detection process is an elegant but fickle beast that is challenging to understand at first. While it offers huge efficiency gains over the 1.x framework, the gains can come at a cost. The development mode of Angular is activated by default, which will alert you when your code is in danger of behaving in a way that defeats the change detection efficiency gains. In this recipe, you'll implement a feature that violates Angular's change detection schema, correct it, and safely use enableProdMode.

Note

The code, links, and a live example related to this recipe are available at .

Getting ready

Begin with a simple component:

[src/app/app.component.ts]      import {Component} from '@angular/core';      @Component({     selector: 'app-root',     template: `       <input #t>       <button (click)="update(t.value)">Save</button>       <h1>{{ title }}</h1>     `   })   export class AppComponent {     title:string = '';        update(newTitle:string) {       this.title = newTitle;     }   }   

When the button in this component is clicked, it grabs the value of the input and interpolates it to the header tag. As is, this implementation is perfectly suitable.

How to do it...

To demonstrate the relevance of enableProdMode, you'll need to introduce a behavior that enableProdMode would mask. More specifically, this means a piece of your application will behave differently when two sequential passes of change detection are run.

Note

There are a significant number of ways to implement this, but for the purpose of this recipe, you'll implement a nonsensical pipe that changes every time it's used.

Generating a consistency error

Create a nonsensical pipe, namely AddRandomPipe:

[src/app/add-random.pipe.ts]      import {Pipe, PipeTransform} from '@angular/core';      @Pipe({     name: 'addRandomPipe'   })   export class AddRandomPipe implements PipeTransform {     transform(value:string):string {       return value + Math.random();     }   }   

Next, take this pipe and introduce it to your component:

[src/app/app.component.ts]      import {Component} from '@angular/core';   import {AddRandomPipe} from './add-random.pipe';      @Component({     selector: 'app-root',     template: `       <input #t>       <button (click)="update(t.value)">Save</button>       <h1>{{ title | addRandomPipe }}</h1>     `   })   export class AppComponent {     title:string = '';        update(newTitle:string) {       this.title = newTitle;     }   }   

This won't create an error yet, though.

Note

Angular will indeed run the change detection process twice, so it might seem mysterious that it doesn't create an error. Even though the pipe will generate a new output every time its transform method is invoked, Angular is smart enough to recognize that the input of the interpolation aren't changing, and therefore, reevaluating the pipe is unnecessary. This is called a "pure" pipe, which is the Angular default.

In order to get Angular to evaluate the pipe during each change detection cycle, specify the pipe as "impure":

[src/app/add-random.pipe.ts]      import {Pipe, PipeTransform} from '@angular/core';      @Pipe({     name: 'addRandomPipe',     pure: false   })   export class AddRandomPipe implements PipeTransform {     transform(value:string):string {       return value + Math.random();     }   }   

Now the fun begins. When you run the application, you should see something similar to the following error message:

 EXCEPTION: Error in ./AppComponent class AppComponent - inline template:3:8 caused by: Expression has changed after it was checked. Previous value: '0.0495151713435904'. Current value: '0.9266277919907477'.    

Introducing change detection compliance

The error is being thrown as soon as the application starts up, before you're even given a chance to press the button. This means that Angular is detecting the binding mismatch as the component is being instantiated and rendered for the first time.

Note

You've created a pipe that intentionally changes each time, so your goal is to instruct Angular to not bother checking the pipe output twice against itself upon component instantiation.

At this point, you've managed to get Angular to throw a consistency error, which it does because it's running change detection checks twice and getting different results. Switching on enableProdMode at this point would stop the error since Angular would then only run change detection once and not bother to check for consistency. This is because it trusts you to have verified compliance before using enableProdMode. Turning on enableProdMode to mask consistency error messages is a bit like coming home to find your house is on fire and then deciding to go on a vacation.

Angular allows you to control this by specifying the change detection strategy for the component. The default is to always perform a change detection check, but this can be overridden with the OnPush configuration:

[src/app/app.component.ts]      import {Component, ChangeDetectionStrategy }      from '@angular/core';   import {AddRandomPipe} from './add-random.pipe';      @Component({     selector: 'app-root',     template: `       <input #t>       <button (click)="update(t.value)">Save</button>       <h1>{{ title | addRandomPipe }}</h1>     `,     changeDetection: ChangeDetectionStrategy.OnPush   })   export class AppComponent {     title:string = '';        update(newTitle:string) {       this.title = newTitle;     }   }   

Now when the component instance is initialized, you should no longer see the consistency error.

Switching on enableProdMode

Since your application is now compliant with Angular's change detection mechanism, you're free to use enableProdMode. This lets your application run change detection once each time. This is because it assumes the application will arrive in a stable state.

In your application's bootstrapping file, invoke enableProdMode before you start bootstrapping:

[src/main.ts]      import {platformBrowserDynamic}      from '@angular/platform-browser-dynamic';   import {enableProdMode} from '@angular/core';   import {AppModule} from './app/';      enableProdMode();      platformBrowserDynamic().bootstrapModule(AppModule);   

How it works...

enableProdMode will configure your application in a number of ways, including silencing error and informational error messages, among others; however, none of these ways is as visible as the suppression of the secondary change detection process.

There's more...

There are other ways to mitigate this consistency problem. For example, suppose you want to generate a pipe that will append a random number to the input. It doesn't necessarily need to be a different random number every single time; rather, you can have one for each unique input within a certain period of time. Such a situation could allow you to have a pipe that utilizes some sort of caching strategy. If the pipe caches results for a period of time longer than change detection takes to complete (which is not very long), then altering the change detection strategy of the component is not necessary. This is because multiple pipe invocations will yield an identical response. For example, refer to the following code:

[src/app/add-random.pipe.ts]      import {Pipe, PipeTransform} from '@angular/core';      @Pipe({     name: 'addRandomPipe',     pure: false   })   export class AddRandomPipe implements PipeTransform {     cache:Object = {};        transform(input:string):string {       let value = this.cache[input];       if (!value || value.expire < Date.now()) {         value = {           text: input + Math.random(),           // Expires in one second           expire: Date.now() + 1000         }         this.cache[input] = value;       }       return value.text;     }   }   

With this, you can safely strike changeDetection: ChangeDetectionStrategy.OnPush from ComponentMetadata of your AppComponent and you will not see any consistency errors.

See also

  • Configuring the Angular 2 renderer service to use web workers guides you through the process of setting up your application to render on a web worker
  • Configuring applications to use ahead-of-time compilation guides you through how to compile an application during the build
Назад: 10. Performance and Advanced Concepts
Дальше: Working with zones outside Angular

thank you
Flame
cant read the code since it is all on a single line. Also this comments section is russian
Rakuneque
DATA COLLECTION AND ANALYSIS Two reviewers extracted data and assessed methodological quality independently lasix torsemide conversion Many others were in that space already