0

My component's template renders a list:

<div class="row">
  <div *ngFor="let m of activeModules" [@enterAnimation]>
    {{ m.name }}
  </div>
</div>

The component animates it

@Component({
  selector: 'app-client-detail',
  templateUrl: './client-detail.component.html',
  styleUrls: ['./client-detail.component.scss'],
  animations: [

    trigger(
      'enterAnimation', [
        transition(':enter', [
          style({ opacity: 0 }),
          animate('1000ms', style({ opacity: 1}))
        ]),
        transition(':leave', [
          style({ opacity: 1 }),
          animate('1000ms', style({ opacity: 0}))
        ])
      ]
    )
  ]
})
export class ClientDetailComponent implements OnInit {
   activeModules = [{ name: 'modulo 1'}, { name: 'modulo 2'}];
}

This works fine, the 'divs' have the transition from the opacity. If I do something like this:

ngOnInit() {
  Observable.timer(2000).subscribe(() => {
    this.activeModules =  [{ name: 'modulo 1'}, { name: 'modulo 3'}, { name: 'modulo 2'}];
}

the activeModules' array is updated and the templates renders again the list with the transition. My problem is that I need only the 'new' items from the array (in this case, the one in the middle) to have the transition. Is it possible?

Christian Benseler
  • 7,907
  • 8
  • 40
  • 71

1 Answers1

1

Sure, you just need to push your item instead of creating your array again.

Let's make a function for that :

pushAt(arr, index, item) {
  arr.splice(index, 0, item); 
}

Now you can just

ngOnInit() {
  Observable.timer(2000).subscribe(() => this.pushAt(this.activeModules, 1, { name: 'Modulo 2' }));
}

EDIT maybe you can try with a custom trackby function :

customTrackBy(index, item) { return index; }

In your HTML

<div *ngFor="let m of activeModules; trackBy: customTrackBy;" [@enterAnimation]>
  • So I cannot use a new array, I have to mutate the original one? – Christian Benseler Jan 16 '18 at 15:36
  • 1
    Well if you want your animations to happen only on the new items, yes you have to use the first one. Otherwise, Angular will destroy the first array, then put the second one, meaning he will withdraw actually displayed elements, thus animating all of your elements. (I don't know if that made sense to you, so if not, feel free to say so) –  Jan 16 '18 at 15:39
  • Yes, this makes sense for me. The problem is that I'm using a redux approach, so all my data is immutable. I guess I will have to do some 'trick' in my component that renders this list, maybe storing the original/first data in this array and then comparing with the data that comes from the store. – Christian Benseler Jan 16 '18 at 15:41
  • You could try using my edit, maybe this will work, but I'm not sure, hence I didn't post it first. Keep me updated on this one so that I remove it to avoid confusion if it doesn't work. –  Jan 16 '18 at 15:44
  • It worked with the trackBy and the customTrack method. Awesome :-) – Christian Benseler Jan 16 '18 at 15:49