javascript - 有没有办法让 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 时强制它们触发,以便我可以尝试更新此动画的状态?
或者有人对我的问题有其他建议吗?现在我只是删除离开动画,但理想情况下我会保留动画。
除了页面加载时,扩展面板不会触发生命周期挂钩。
解决方案
奇怪的是,这种方法有什么不同吗?
<ng-container *ngIf="!isClosed">
<div class="content" [@expand]>
<div class="content-wrapper">
<ng-content></ng-content>
</div>
</div>
<ng-container>
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
}
}
推荐阅读
- java - 两个 UI Thread 的 SWT Dialog
- angular - 错误 TS2307:找不到模块“vectormap-data/world.js”
- testing - 在 Elm 中测试去抖动
- vba - VBA AddDataField CubeFields - 错误 1004
- postgresql - 如何使用 Sequelize 将 id 传递给其关联记录
- reactjs - 如何将道具传递给反应处理程序组件
- elastic-stack - 在 ELK 堆栈中进行 pcap 文件分析的适当工具?
- css - 如何在 egde 浏览器上使用 ime-mode css
- jenkins - Jenkins 配置面临的问题
- date - 从 hive 中的当前日期计算过去 5 年