1

I have a dead simple CSS class that toggles opacity on <tr> elements with a basic ease transition:

 tbody tr {
   transition: opacity 1s ease;
 }

 tbody.blur tr {
   opacity: .3;
 }

 tbody.blur tr.focus {
   opacity: 1;
 }

[ngClass] sets and removes the .blur and .focus classes. (The point is to blur all rows except one focused row.) The opacity works as expected, but the transition doesn't. If I type the classes directly into the tbody and tr tags with dev tools, the opacity transitions, but if the classes are set by ngClass, there's no transition.

I tried setting encapsulation: ViewEncapsulation.None, but that had no impact.

Am I missing something with transitions and ngClass?

Edit: ngClass code is basically

<tbody [ngClass]="{blur: focused > -1}">
  <tr [ngClass]="{focus: focused === item.id}" *ngFor="let item of items">

The component class has a focused variable that is set to -1 on blur and to the respective id on focus.

Edit - solution

A computed getter that continually returns a new Array instance prevents the CSS transition; example here. If you return the same array instance, the transition occurs. The getter should return the same, mutable property of the class instance like this. Note that the property in the latter example is made up of other getters, which is fine.

24
  • 1
    Please add the code. Where did you add the CSS? Commented Feb 3, 2018 at 17:34
  • 1
    Paste ngClass code please! Commented Feb 3, 2018 at 17:39
  • @GünterZöchbauer thanks, CSS is added to the component.css file created by the CLI. Commented Feb 3, 2018 at 17:51
  • 1
    You should prefer [class.blur]="focused > -1" if the class name is fixed (and [class.focus]="focused === item.id" Commented Feb 3, 2018 at 17:53
  • @GünterZöchbauer nice tip; thanks. Commented Feb 3, 2018 at 17:58

1 Answer 1

1

A getter like

  get tableData() {
    return [
        {id: 1, name: 'One', data: this.one},
        {id: 2, name: 'Two', data: this.two},
        {id: 3, name: 'Three', data: this.three},
        {id: 4, name: 'Four', data: this.four},
        {id: 5, name: 'Five', data: this.five},
        {id: 6, name: 'Six', data: this.six}
    ];
  }

returns a new array instance every time tableData is called. Angular calls the getter every time change detection runs and detects a change because it gets a different instance. This causes severe performance issues and breaks animation, because animation doesn't work if the items are constantly recreated.

A better approach is

class MyComponent {  
  data = [
    {id: 1, name: 'One', data: this.one},
    {id: 2, name: 'Two', data: this.two},
    {id: 3, name: 'Three', data: this.three},
    {id: 4, name: 'Four', data: this.four},
    {id: 5, name: 'Five', data: this.five},
    {id: 6, name: 'Six', data: this.six}
  ];

  get tableData() {
    return this.data;
  }
}

this way Angular change detection gets the same instance every time it calls tableData and Angular will be happy. If some event causes the content or the whole array to change, this is fine of course, but it shouldn't change just because Angular checks twice subsequently.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.