【问题标题】:Angular 5 Animation: How to work with :leave transition while the component is removed from DOMAngular 5 动画:如何在组件从 DOM 中删除时使用 :leave 过渡
【发布时间】:2023-03-13 11:40:01
【问题描述】:

我们一直在我们的项目中使用 @ng-bootstrap/ng-bootstrap 库来处理一些行为/组件,例如 Modal。最近我想消除这种依赖并使用 Angular 实现引导模式行为。这实际上很容易。让我简短地告诉你它是如何工作的:

我有一个模态服务和一个模态组件。 Service 通过 ComponentFactoryResolver 动态创建模态组件(详情可见in this SO post)并添加到DOM中。通过关闭 modal,modal 只是调用了 service 中定义的回调函数,这只是破坏了组件,从 DOM 中移除。

所以:这个模态组件有 2 个动画状态,进入和离开。输入效果很好。一旦组件出现在 dom 中,预定义的 :enter 状态就会被触发,我的动画就会起作用。但是 :leave 没有。

这正是关闭模态的工作原理:模态打开,您单击关闭按钮或模态背景上的任何其他位置。这只是调用 close 函数,该函数被定义为输入,并在创建期间由服务提供。

@Input() closeCallback: Function;

服务只是从 DOM 中移除组件。

由于单击关闭按钮后组件就被删除,我认为动画没有它需要的时间。所以 :leave 不起作用。

我想设置一个超时(延迟)关闭,并手动触发动画,但由于我想使用预定义的行为:进入和:离开,我无法弄清楚它是怎么可能的。那么我怎样才能让我的离开动画工作呢? (带或不带:leave)

服务代码:

@Injectable()
export class ModalService implements OnDestroy {

  private renderer: Renderer2;
  private componentRef: ComponentRef<ModalComponent>;

  constructor(private rendererFactory: RendererFactory2,
              private componentFactoryResolver: ComponentFactoryResolver,
              private appRef: ApplicationRef,
              private injector: Injector) {
    this.renderer = rendererFactory.createRenderer(null, null);
  }

  ngOnDestroy() {
    this.componentRef.destroy();
  }

  open(content: string, titel: string, primaryButtonLabel: string, secondaryButtonLabel?: string, primaryButtonCallback?: Function, secondaryButtonCallback?: Function) {
    // 1. Create a component reference from the component
    this.componentRef = this.componentFactoryResolver
      .resolveComponentFactory(ModalComponent)
      .create(this.injector);

    this.componentRef.instance.content = content;
    this.componentRef.instance.titel = titel;
    this.componentRef.instance.primaryButtonLabel = primaryButtonLabel;
    this.componentRef.instance.secondaryButtonLabel = secondaryButtonLabel;
    this.componentRef.instance.primaryButtonCallback = primaryButtonCallback;
    this.componentRef.instance.secondaryButtonCallback = secondaryButtonCallback;
    this.componentRef.instance.closeCallback = (() => {
      this.close();
    });

    // 2. Attach component to the appRef so that it's inside the ng component tree
    this.appRef.attachView(this.componentRef.hostView);

    // 3. Get DOM element from component
    const domElem = (this.componentRef.hostView as EmbeddedViewRef<any>)
      .rootNodes[0] as HTMLElement;

    // 4. Append DOM element to the body
    this.renderer.appendChild(document.body, domElem);
    this.renderer.addClass(document.body, 'modal-open');
  }

  close() {
    this.renderer.removeClass(document.body, 'modal-open');
    this.appRef.detachView(this.componentRef.hostView);
    this.componentRef.destroy();
  }
}

模态组件.ts:

@Component({
  selector: '[modal]',
  templateUrl: './modal.component.html',
  styleUrls: ['./modal.component.scss'],
  animations: [
    trigger('modalSlideInOut', [
      transition(':enter', [
        style({opacity: 0, transform: 'translateY(-100%)'}),
        animate('0.3s ease-in', style({'opacity': '1', transform: 'translateY(0%)'}))
      ]) ,
      transition(':leave', [
        style({opacity: 1, transform: 'translateY(0%)'}),
        animate('0.3s ease-out', style({'opacity': '0', transform: 'translateY(-100%)'}))
      ])
    ])
  ]
})
export class ModalComponent implements AfterViewInit {

  ....
  @Input() closeCallback: Function;

  constructor() { }

  close() {
    this.closeCallback();
  }
}

Modal-HTML 不是很相关,但你可以这样想象:

<div [@modalSlideInOut] role="document" class="modal-dialog">
  <div ....
      <button (click)="close()">
        CLOSE
      </button>

       ...
  </div>
</div>

【问题讨论】:

  • 请提供minimal reproducible example。您很好地解释了您的问题,但现在,您的问题是 Questions seeking debugging help ("why isn't this code working?") 类型的问题(这些问题可能会结束)。
  • @trichetriche 好的,我会尝试..

标签: angular angular-animations


【解决方案1】:

我今天遇到了类似的问题,我找到的解决方案是通过以下方式简单地将动画绑定到主机组件本身:

@HostBinding('@modalSlideInOut')

这样,您就不必对动画时间进行任何欺骗。当你调用 destroy 时,Angular 知道组件正在消失,所以它会为你处理它,就像你在组件上调用 ngIf 一样。

【讨论】:

  • 我知道这已经过时了,但这是我所见过的许多问题的一个很好的答案。谢谢!
【解决方案2】:

所以我已经找到了解决方法。但是,如果有更好的方法可以做到这一点,我会提出问题。

据我了解,:leave 动画是 (* => void) 的快捷方式。 * 是“任何状态”,void 是“标签不可见”。所以当一个组件从 DOM 中移除时,它是不可见的,但动画仍然不起作用,因为该元素不再存在(我的假设)。

所以我为模态父元素提供了一个 ngIf 标志:

<div *ngIf="showModal" [@modalSlideInOut] role="document" class="modal-dialog">

showModal 默认为 true,因为我们希望模态框在 DOM 中立即显示。 close 函数首先将标志 auf 设置为 false,使模态不可见。并在超时后调用回调函数,从 DOM 中删除组件。这是关闭功能:

  close() {
    this.showModal = false;
    setTimeout(() => {
      this.closeCallback();
    }, 300);
  }

300 是等待组件被移除,因为我的动画需要 0.3 秒。

【讨论】:

    猜你喜欢
    • 2019-08-19
    • 2020-03-04
    • 2019-10-03
    • 1970-01-01
    • 2018-10-05
    • 1970-01-01
    • 1970-01-01
    • 2018-05-17
    • 2021-09-21
    相关资源
    最近更新 更多