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