Thursday 28 February 2019

How to intercept :enter & :leave Animations in Angular with a changes Diff?

I have a list in my template, where the first 3 items will be shown and the rest is in a collapsible, i.e. within a 'show more' link.

  • item1
  • item2
  • item3

    Show More

Here is how it looks when you click Show More:

  • item1
  • item2
  • item3
  • item4
  • item5

    Show Less

The list is highly dynamic. The items are held by component in a single list called myList. The collapsible is from our pattern library where the html structure is predefined. So i actually had to have 2 different lists in my template. First is for the first 3 items, second is for the rest within a collapsible. In order not to use the same HTML twice, i define the list with ng-template and reuse it 2 times. Here is the template's rough structure:

<ng-template #listRef let-list>
  <ul>
    <ng-container *ngFor="let item of list; trackBy: trackByFn;">
      <li @animation>

       ...

Here is the usage with template:

<!-- first part, max 3 items -->

<ng-container *ngTemplateOutlet="listRef; context: {$implicit: myList?.slice(0,3)}"></ng-container>


<!-- second part, rest in collapsible -->

<ng-container *ngIf="lebenslaufEintraege?.length > 3">
  <div class="my-collapsible">
    <a>...</a>
    <div>..
      <ng-container
          *ngTemplateOutlet="listRef; context: {$implicit: myList?.slice(3,myList.length)}">
      </ng-container>

Also, as soon as a new item is added it enters to the list animated. Also if an item is removed, it leaves animated. Here is my animation trigger:

trigger('animation', [
  transition(':enter', [
    style({ height: '0px', 'padding-top': '0', 'padding-bottom': '0'}),  // initial
    animate('0.5s',
      style({ height: '*', 'padding-top': '*', 'padding-bottom': '*'}))  // final
  ]),
  transition(':leave', [
    style({ height: '*', 'padding-top': '*', 'padding-bottom': '*', opacity: 1}),  // initial
    animate('0.5s',
      style({ height: '0px', 'padding-top': '0', 'padding-bottom': '0', opacity: 0}))  // final
  ])
])

The problem is then, when we have more than 3 items and the collapsible is open, so all items are seen in viewport. In this case, when a new item is added to the first part of the list, it enters animated, but since the last item of the list also leaves the first list and enters to second list, it is also animated, which we do not want.

In another words; if we have 5 items, and add a new one to the top, item3 is removed from the first list, and added to the second list, which also triggers the animation.

How can i prevent this animation?

Today i have found out, that we can disable the animation for the individual items with this annotation:

<li [@.disabled]="item.isAnimated" @animation>

But i cannot imagine how i should implement the logic for this flag isAnimated. As soon as the item3 is shifted to the 2. list, i have to check if it was in the first list before, but i don't have the previous status of the first list.

I think the only possible option is to somehow intercept the animation, and look if the previous state of the list had the same item, and if yes interrupt the animation..

Or should i remove the animation from the item, and instead define some animation for the changes in my main-list (myList). But i do not know how to achieve this in my case with my template..

I appreciate any help.



from How to intercept :enter & :leave Animations in Angular with a changes Diff?

No comments:

Post a Comment