curchod
curchod

Reputation: 291

Angular2 ngClass docs & the Expression 'setClasses()’ has changed after it was checked exception

In the Angular2 documentation on template syntax the sample code for the preferred method for setting multiple styles at the same time is this:

setStyles() {
  let styles = {
    // CSS property names
    'font-style':  this.canSave      ? 'italic' : 'normal',  // italic
    'font-weight': !this.isUnchanged ? 'bold'  : 'normal',  // normal
    'font-size':  this.isSpecial    ? 'x-large': 'smaller', // larger
  }
  return styles;
}

Called like this:

<div [ngStyle]="setStyles()">
  This div is italic, normal weight, and x-large
</div>

However, trying out this code in the Tour of Heroes project causes the following error:

Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.
EXCEPTION: Error during evaluation of "click"
EXCEPTION: Expression 'setClasses() in AppComponent@11:10' has changed after it was checked.
Previous value: '[object Object]'. Current value: '[object Object]' in [setClasses() in AppComponent@11:10]

I understand this is part of Angular2’s drive to emphasise unidirectional data flow, but given this example as a best practice, what’s going on?

I found one suggested solution to this issue here, which has no accepted answer. The discussion takeaway: Anything that changes a binding needs to trigger a round of change detection when it does. In the notes there was a link to another answer which said this: manually run change detection:

Use ApplicationRef::tick() method.
Use NgZone::run() method to wrap you code which should be executed inside angular zone.

Trying to then use the method app.tick(); and NgZone::run() both break the entire application, or I’m just using it wrong.

I have created this Plunker to demonstrate this issue. When you select a hero, the styles on their name should change depending on the data they have available in their model. For example, the first five heroes have a standard style, but the next three, Dr IQ, SkyDog and J-Dragon have alter egos and powers. In development mode, you can only select one and the app breaks with the message: Expression 'setClasses() in AppComponent@11:10' has changed after it was checked.

If you un-comment out the //enableProdMode(); in boot.ts, then the app works fine.

So what’s up with that? I want to be able to use development mode and use ngClass in this way. What is the best practice for doing this?

Upvotes: 2

Views: 1106

Answers (1)

Langley
Langley

Reputation: 5344

Change:

<h4 [ngClass]="setClasses()">

for:

<h4 [ngClass]="classes">

and call the setClasses method in the onSelect method:

onSelect(hero: Hero) { 
    this.selectedHero = hero; 
    hero.power ? this.stylar1 = true: this.stylar1 = false;
    hero.alterEgo ? this.stylar2 = true: this.stylar2 = false;
    this.setClasses();
}

http://plnkr.co/edit/95H4YNtdfn3y2KTdX8Xu?p=preview

Upvotes: 1

Related Questions