如何拦截:输入&:在Angular中保留Animations并改变Diff?

问题描述 投票:4回答:2

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

  • ITEM1
  • ITEM2
  • 项目3 显示更多

单击“显示更多”时的外观如下:

  • ITEM1
  • ITEM2
  • 项目3
  • ITEM4
  • ITEM5 显示较少

该列表非常动态。这些项目由组件保存在名为myList的单个列表中。可折叠来自我们的模式库,其中html结构是预定义的。所以我实际上必须在我的模板中有2个不同的列表。首先是前3项,第二项是可折叠的其余项目。为了不使用相同的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)中的更改定义一些动画。但我不知道如何在我的模板中实现这一点..

我感谢任何帮助。

angular angular-animations
2个回答
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]的原因。


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;
}

你的动画仍然像你实现的那样。现在,您的功能应与动画一起使用。并且由于没有项目的概念从1个数组输出并进入另一个数组,因此您应该摆脱动画问题及其涉及的其他复杂性。

注意:

与Show More / Less按钮相关的代码只是简化以表达想法。请相应更改代码。

希望能帮助到你。

© www.soinside.com 2019 - 2024. All rights reserved.