Angular 5动画:当从DOM中删除组件时,如何使用:leave过渡

时间:2018-07-30 08:30:57

标签: angular angular-animations

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

我有一个模态服务和一个模态组件。服务通过ComponentFactoryResolver动态创建模式组件(详细信息可见in this SO post)并添加DOM。通过关闭模态,模态将只调用从服务中定义的回调函数,这只会破坏组件,从DOM中删除。

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

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

@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();
  }
}

模态HTML不是很相关,但是您可以想象这样的事:

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

       ...
  </div>
</div>

2 个答案:

答案 0 :(得分:0)

所以我已经找到了解决方法。但是,如果有更好的方法可以解决这个问题,我就开放一个问题。

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

所以我为模态父元素赋予了ngIf标志:

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

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

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

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

答案 1 :(得分:0)

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

@HostBinding('@modalSlideInOut')

这样,您就不必在动画时间上进行任何花招。当您调用destroy时,Angular知道该组件将要消失,因此它将为您处理该组件,就像您在该组件上调用ngIf一样。