首页 > 解决方案 > 有没有办法让 Angular 9+ 生命周期钩子在每次从 DOM 中添加或删除组件时触发?

问题描述

我有一个扩展面板组件。该组件单独运行良好。这是它的代码。

HTML

<div class="container">
  <div class="header" (click)="toggleSelf()">
    <div class="header--content">
      {{ header }}
    </div>
    <div class="header--action" [ngClass]="{ rotated: !isClosed }">
      <mat-icon svgIcon="chevron-down-filled"></mat-icon>
    </div>
  </div>

  <div class="content" *ngIf="!isClosed" [@expand]>
    <div class="content-wrapper">
      <ng-content></ng-content>
    </div>
  </div>
</div>

打字稿

import { Component, Input } from '@angular/core';
import { trigger, transition, style, animate } from '@angular/animations';

@Component({
  selector: 'sky-expansion-panel',
  templateUrl: './expansion-panel.component.html',
  styleUrls: ['../../../../scss/components/accordion.scss'],
  animations: [
    trigger('expand', [
      transition(':enter', [
        style({
          height: '0',
          opacity: 0,
          overflow: 'hidden'
        }),
        animate(
          '500ms cubic-bezier(1, .14, .69, .05)',
          style({
            height: '*',
            opacity: 1,
            overflow: 'visible'
          })
        )
      ]),
       transition(':leave', [
         style({
           height: '*',
           opacity: 1,
           overflow: 'hidden'
         }),
          animate(
            '500ms cubic-bezier(1, .14, .69, .05)',
            style({
              height: '0',
              opacity: 0,
              overflow: 'hidden'
          })
        )
      ])
    ])
  ]
})

export class SkyExpansionPanelComponent {
  @Input() header: string;
  @Input() isClosed = true;

  public toggleSelf(): void {
    this.isClosed = !this.isClosed;
  }
}

实现 HTML 示例

<filter-panel *ngIf="toggleLogic()"
  <sky-accordion>
    <sky-expansion-panel header="General" [isClosed]="filterStatus.generalTabOpen">
      //irrelevant content
    </sky-expansion-panel>
  </sky-accordion>
</filter-panel>

当我将这个动画引入一个过滤器面板时,我开始遇到问题,这个过滤器面板也是用 *ngIf 隐藏和显示的。

我发现当父组件隐藏扩展面板(通过隐藏过滤器面板)时,它在技术上会触发 :leave 动画并在组件被销毁之前快速应用内联样式。

通常,如果我们希望扩展面板返回其关闭状态,这不会成为问题。我们跟踪它是打开还是关闭的状态。当过滤器面板重新打开时,扩展面板会记住正确的状态(通过 isClosed 输入)。

问题是即使内容是在 DOM 上呈现的,出于某种原因,来自关闭状态的内联样式也会应用于展开面板。更奇怪的是通常你可以在开发工具中看到这样的东西 -

<div class="content" style="height: 0; opacity: 0; overflow: hidden;">

如果您在扩展面板处于打开状态的情况下关闭过滤器,然后重新打开它们,它实际上在开发工具中看起来像这样

<div class="content" style>

基本上,除了动画是内联样式之外,一切都正常工作,它不应该内联。

为了纠正这个问题,我尝试从状态方法制作动画。下面的语法可能搞砸了,因为我是从记忆中输入的,但你明白了它的要点。

状态方法

animations: [
    trigger('expand', [
      state('closed', [
        style({
          height: '0',
          opacity: 0,
          overflow: 'hidden'
        }),
      ]),
      state('open', [
        style({
         height: '*',
         opacity: 1,
         overflow: 'visible'
       })
     ]),
       transition('closed => open', [
         animate( '500ms cubic-bezier(1, .14, .69, .05)')
      ]),
       transition('open => void', [
         animate('500ms cubic-bezier(1, .14, .69, .05)')
      ]),
       transition('void => open', [
         animate('500ms cubic-bezier(1, .14, .69, .05)')
      ])
    ])
  ]

状态方法的表达

<div class="content" *ngIf="!isClosed" [@expand]="isClosed ? closed : open">

这也没有产生任何不同的结果。此时我将尝试在 ngOnInit 中设置 isClosed 的值,希望触发器会改变,但发现扩展面板中的 ngOnInit 和其他生命周期挂钩会在页面加载时触发一次,即使过滤器甚至没有打开DOM。有没有办法在每次将它们添加或删除到 DOM 时强制它们触发,以便我可以尝试更新此动画的状态?

或者有人对我的问题有其他建议吗?现在我只是删除离开动画,但理想情况下我会保留动画。

除了页面加载时,扩展面板不会触发生命周期挂钩。

标签: javascripthtmlangulartypescriptanimation

解决方案


奇怪的是,这种方法有什么不同吗?

<ng-container *ngIf="!isClosed">
  <div class="content" [@expand]>
    <div class="content-wrapper">
      <ng-content></ng-content>
    </div>
  </div>
<ng-container>

ngOnInitngOnDestroy

import { Component, Input, OnInit, OnDestroy } from '@angular/core';

export class SkyExpansionPanelComponent implements onInit, onDestroy{
  @Input() header: string;
  @Input() isClosed = true;

  public toggleSelf(): void {
    this.isClosed = !this.isClosed;
  }

  ngOnInit() {
    // gets fired every time the component is added 
  } 

  ngOnDestroy() {
    // gets fired every time the component is removed
  }
}

推荐阅读