garethdn
garethdn

Reputation: 12351

Change detection appears to occur on click event regardless of detached setting

This may be my own misunderstanding of how Angular 2 change detection works but I would have expected that if a component's ChangeDetectionStrategy was set to Checked, CheckOnce or Detached, that that component would only check for changes once when the component is instantiated. It appears to not happen that way.

import {Component, OnInit, ChangeDetectionStrategy} from 'angular2/core'

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div>
      <button 
        (click)="onClick($event)" 
        [class.thing]="hasThingClass()">Update</button>
    </div>
  `,
  changeDetection:ChangeDetectionStrategy.CheckOnce,
  styles: [`
    .thing { background-color: red }
  `]
})

export class App implements OnInit {

  public hasThing:Boolean = false;

  onClick() {
    this.hasThing = !this.hasThing;
  }

  hasThingClass(e) {
    return this.hasThing;
  }

}

On click I would have expected to toggle the hasThing property but I wouldn't have expected the view to update. As it happens, the view does update. The occurs when the ChangeDetectionStrategy is set to Detached also.

http://plnkr.co/edit/2hHdt2BDpj419Z32iPjJ?p=preview

What am I missing here? What exactly is causing the view to update? From what I can see, regardless of me updating the hasThing property, the view updates on click whether a value has changed or not.

Upvotes: 1

Views: 7756

Answers (2)

Mark Rajcok
Mark Rajcok

Reputation: 364677

Regardless of the ChangeDetectionStrategy setting, Angular will check the component for changes whenever

  • the component fires an event
  • an observable fires an event, and the async pipe is used in the view with that observable

The idea being... if the component triggered an event, or a bound observable value changed, most likely you want to update its view.

Upvotes: 8

igorzg
igorzg

Reputation: 1506

I hope this will help you understand it :)

http://plnkr.co/edit/sM8CCSl8hXyRVAaTZH4N?p=preview

Let's say we have an async event which i simulate in my service:

import { Injectable } from 'angular2/core';

@Injectable()
export class DataProvider {
  data = 1;
  constructor() {
    // async data change simulation
    setInterval(() => {
      this.data = this.data * 2;
    }, 500);
  }
}

When angular spot any async event change via zones api , zone notify an angular in that context an async event happened and it propagate it to angular change detection api to do check between actual view and component, and in example below we can see that since change detection behaviour is not changed angular is constantly updating view:

import {
  Component
} from 'angular2/core';
import { bootstrap } from 'angular2/platform/browser';
import { DataProvider } from './data-provider';
import { TriggerDataChange } from './data-change-trigger';

@Component({
  selector: 'app',
  template: `
    <div>Live Update: {{dataProvider.data}} </div>
    <data-change-trigger></data-change-trigger>
  `,
  providers: [DataProvider],
  directives: [TriggerDataChange]
})
class App {
  constructor(private dataProvider:DataProvider) {}
}

bootstrap(App);

Scenario two with changed behaviour of change detection, since ChangeDetectionStrategy is set to CheckOnce angular will trigger change detection only once at component initialization process and in that case you have to trigger change manually to update view which in this case is done by event binding:

import {
  Component,
  ChangeDetectionStrategy, 
  ChangeDetectorRef
} from 'angular2/core';
import { DataProvider } from './data-provider';

@Component({
  selector: 'data-change-trigger', 
  changeDetection: ChangeDetectionStrategy.CheckOnce,
  template: `
    Trigger update: 
    <button (click)="update($event)">Update</button>
    <div>{{dataProvider.data}}</div>
  `
})
export class TriggerDataChange {

  constructor(
    private ref: ChangeDetectorRef, 
    private dataProvider:DataProvider
  ) {}

  update($event) {
     console.log('event', $event);
  }
}

And finally to answer why is in your case view is updated ? It's because event binding in template:

(click)="onClick($event)" 

This is an native event which is triggering current component ChangeDetectorRef.detectChanges()

Upvotes: 4

Related Questions