Angular 5动画:如何在组件从DOM中移除时使用:leave过渡

5
我们在项目中使用@ng-bootstrap/ng-bootstrap库来实现弹窗等一些行为和组件。最近我想要消除这个依赖,并用Angular实现bootstrap弹窗的行为。事实上,这很容易。让我简单地告诉你它是如何工作的:
我有一个modal-service和一个modal-component。服务通过ComponentFactoryResolver动态创建模态组件(详细信息可以参见此SO帖子)并添加到DOM中。通过关闭模态框,模态框只调用从服务中定义的回调函数,然后销毁组件并从DOM中删除。
所以:我为这个模态组件有两个动画状态,进入和离开。进入正常工作。当组件出现在DOM中时,预定义的:enter状态被触发,我的动画就会起作用。但是:leave状态没有起作用。
这正是关闭模态框的工作原理:模态框打开,您点击关闭按钮或模态背景的任何其他地方。这只是调用关闭函数,该函数作为输入定义,并在创建期间从服务中给出。
@Input() closeCallback: Function;

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

由于组件在关闭按钮被点击后立即被移除,我认为动画没有足够的时间。因此,:leave不起作用。

我想在关闭时设置一个延迟(延迟),并手动触发动画,但由于我想使用预定义的行为:enter和: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();
  }
}

Modal-Component.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>

请提供一个最小化、完整、可复现的示例(MCVE)。您已经很好地解释了您的问题,但是现在,您的问题属于“寻求调试帮助(“为什么这段代码不起作用?”)”类型的问题(而这些问题只会被关闭)。 - user4676340
@trichetriche 好的,我会尝试。 - akcasoy
2个回答

5

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

@HostBinding('@modalSlideInOut')

这样,您就不必在动画时间上做任何巧妙的处理。当您调用destroy时,Angular知道该组件即将消失,因此会为您处理它,就像您在组件上调用ngIf一样。


2
我知道这已经过时了,但这是对我看到的许多问题的一个很好的答案。谢谢! - Everett Glovier

1
我已经找到了一个解决方法。但是我仍然保留这个问题,如果有更好的方法,请告诉我。
据我理解,:leave动画是(* => void)的一种快捷方式。*代表"任何状态",void代表"标签不可见"。因此,当组件从DOM中移除时,它是不可见的,但动画仍然无法正常工作,因为元素不再存在(我的假设)。
所以我给模态框父元素设置了一个ngIf标志:
<div *ngIf="showModal" [@modalSlideInOut] role="document" class="modal-dialog">

showModal默认为true,因为我们希望模态框在DOM中出现时就显示。close函数首先将标志设置为false,使模态框不可见。然后经过一段时间延迟,调用回调函数,将组件从DOM中移除。这就是close函数:

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接