如何通过更改Diff截获Angular中的:enter和:leave动画?

时间:2019-02-25 13:42:46

标签: angular angular-animations

我的模板中有一个列表,其中将显示前3个项目,其余项目位于可折叠状态,即在“显示更多”链接中。

  • item1
  • item2
  • item3

    显示更多

单击“显示更多”后,外观如下:

  • item1
  • item2
  • item3
  • item4
  • item5

    显示较少

该列表是高度动态的。 项目由组件保存在名为myList的单个列表中。。可折叠来自我们的模式库,其中的HTML结构是预定义的。因此,实际上我的模板中必须有 2个不同的列表。首先是前三个项目,第二个是可折叠项目中的其余项目。为了避免两次使用相同的HTML,我使用ng-template定义了列表,并将其重复使用了2次。这是模板的粗略结构:

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

       ...

这是模板的用法:

<!-- 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>

此外,一旦添加了新项目,它就会进入动画列表。同样,如果某个项目被删除,它将保持动画状态。这是我的动画触发器:

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
  ])
])

然后是问题,当我们有3个以上的项目并且可折叠对象处于打开状态时,所有项目都可以在视口中看到。在这种情况下,将新项目添加到列表的第一部分时,它会进入动画状态,但是由于列表的最后一项也离开了第一个列表并进入第二个列表,因此它也会被动画化,我们不会想要。

换句话说;如果我们有5个项目,并在顶部添加一个新项目,则将item3从第一个列表中删除,然后添加到第二个列表中,这也会触发动画

如何防止此动画?

今天我发现,我们可以使用以下注释为单个项目禁用动画:

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

但是我无法想象我应该如何实现此标志isAnimated的逻辑。将item3移至2.列表后,我必须检查它之前是否在第一个列表中,但我没有第一个列表的先前状态。

我认为唯一可能的选择是以某种方式截获动画,并查看列表的先前状态是否具有相同的项目,如果是,则中断动画。.

或者我应该从项目中删除动画,而是为我的主列表(myList)中的更改定义一些动画。但是我不知道如何在我的模板中实现这一目标。

感谢您的帮助。

2 个答案:

答案 0 :(得分:3)

您遇到的问题是,您无法区分是将项目添加到列表中还是只是将其转移到适当位置,因为在转移或添加之前模板中不存在该项目。如果该项目在转移到位之前就已经存在,那么您可以区分事件并相应地使用[@.disabled]

一个简单但效率不高的解决方案是将列表重复两次,并向模板传递一个指示符,通知它要么隐藏前三个元素,要么隐藏除后三个元素之外的所有元素。禁用动画应该具有相同的逻辑。

如果我们可以假设一次只能将一个元素添加到数组,那么一种更有效的方法是仅将一个额外的元素传递给模板。对于列表的顶部,传递前四个元素,对于底部的传递,从索引2开始的所有项目。

简化的工作示例代码

<ng-container *ngTemplateOutlet="listRef; context: {$implicit: items | slice:0:3, hideIndex: 3}"></ng-container>
<ng-container *ngTemplateOutlet="listRef; context: {$implicit: items | slice:2, hideIndex: 0}"></ng-container>

<ng-template #listRef let-list let-hideIndex="hideIndex">
  <ul>
    <ng-container *ngFor="let x of list; let index = index">
      <li @animation 
          [hidden]="hideIndex == index"
          [@.disabled]="hideIndex == index">{{x}}
      </li>
    </ng-container>
  </ul>
</ng-template>

正在发生的是,索引2和3的项目将同时出现在两个列表中,并且在其中一个适当的位置被隐藏并具有禁用的动画。如果发生轮班,则相应的项目现在将仅位于以前隐藏它们的列表中。动画不会发生,因为它们早已被添加到DOM中。

我本来只是尝试使用隐藏的,但是如果快速添加项目,则会显示动画的尾端。这就是为什么也使用[@.disabled]的原因。

答案 1 :(得分:0)

与您的方法略有不同。不确定它是否可以回答您的问题(拦截输入/离开动画事件),但我认为至少可以解决您的用例。

首先不维护2个数组如何?

<div *ngFor="let item of list; let i=index; trackBy: trackByFn;">
    <li @animation *ngIf="i < defaultCount">
        {{item.value}}
    </li>
</div>

<button (click)="showMore()" *ngIf="defaultCount === 3">Show More</button>
<button (click)="showLess()" *ngIf="defaultCount !== 3">Show Less</button>

并在组件打字稿文件中。

showMore(): void {
    this.defaultCount = this.list.count;
}

showLess(): void {
    this.defaultCount = 3;
}

您的动画将保持原样。现在,您的功能应与动画一起使用。而且由于没有项目从一个数组移出另一个数组的概念,因此您应该摆脱动画问题及其涉及的其他复杂性。

注意:

与“显示更多/更少”按钮相关的代码只是经过简化以表达想法。请相应地更改代码。

希望有帮助。