Angular指令中获取ngTemplate innerHTML实现悬浮弹窗的方法
Angular实现类Bootstrap悬浮popover的可行方案
不需要手动提取ng-template的HTML代码拼接DOM,Angular原生提供了模板渲染的能力,根据你的场景选对应方案即可:
- 方案1:纯CSS+DOM层级调整(无弹窗裁剪场景首选)
你现在内容渲染在目标元素外部、位置不对,核心是DOM层级和定位父级没配对。给悬浮触发元素套一层position: relative的父容器,把popover内容节点放在这个父容器内部,不要放在组件最外层,再给弹层加绝对定位即可,甚至不需要写额外TS逻辑:/* 触发元素和弹层的共同父级 */ .popover-trigger-wrap { position: relative; display: inline-block; } .popover-content { display: none; position: absolute; /* 示例:弹层在触发元素上方居中 */ bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%); z-index: 1000; /* 补全弹层样式:背景、圆角、阴影、内边距等和Bootstrap popover一致即可 */ } /* 悬浮时展示弹层 */ .popover-trigger-wrap:hover .popover-content { display: block; } - 方案2:动态渲染到body层(解决父级裁剪问题)
如果触发元素的父级存在overflow: hidden、transform属性,会导致弹层被裁剪,这时候需要把弹层渲染到body根节点,不要手动拼HTML字符串,否则会丢失Angular的事件、双向绑定、指令能力,用Angular内置的视图容器API动态渲染模板即可:- 组件类代码:
import { Component, TemplateRef, ViewChild, ViewContainerRef, Renderer2 } from '@angular/core'; @Component({ selector: 'app-popover-demo', templateUrl: './popover-demo.component.html', styleUrls: ['./popover-demo.component.css'] }) export class PopoverDemoComponent { @ViewChild('popoverContentTpl') popoverContentTpl: TemplateRef<any>; private currentPopover: HTMLElement | null = null; constructor( private viewContainer: ViewContainerRef, private renderer: Renderer2 ) {} onTriggerEnter(triggerEl: HTMLElement) { // 先销毁已存在的弹层 this.onTriggerLeave(); // 把ng-template渲染为内嵌视图 const popoverView = this.viewContainer.createEmbeddedView(this.popoverContentTpl); this.currentPopover = popoverView.rootNodes[0] as HTMLElement; // 将弹层节点追加到body this.renderer.appendChild(document.body, this.currentPopover); // 计算弹层位置,示例为触发元素上方居中 const triggerRect = triggerEl.getBoundingClientRect(); const popRect = this.currentPopover.getBoundingClientRect(); this.renderer.setStyle(this.currentPopover, 'position', 'fixed'); this.renderer.setStyle(this.currentPopover, 'z-index', '1050'); this.renderer.setStyle(this.currentPopover, 'top', `${triggerRect.top - popRect.height - 8}px`); this.renderer.setStyle(this.currentPopover, 'left', `${triggerRect.left + triggerRect.width / 2 - popRect.width / 2}px`); this.renderer.setStyle(this.currentPopover, 'display', 'block'); } onTriggerLeave() { if (this.currentPopover) { this.renderer.removeChild(document.body, this.currentPopover); this.currentPopover = null; this.viewContainer.clear(); } } }- 模板代码:
<div class="popover-trigger-wrap"> <button (mouseenter)="onTriggerEnter($event.target as HTMLElement)" (mouseleave)="onTriggerLeave()" > 悬浮触发弹层 </button> <ng-template #popoverContentTpl> <div class="popover-content"> <!-- 这里放任意弹层内容,支持Angular绑定、指令、组件 --> 弹层内容,支持任意自定义内容 </div> </ng-template> </div>
注意:如果项目已经引入Angular CDK,也可以直接用CDK的
Overlay模块实现弹层定位、自动避让、滚动跟随能力,逻辑会更简洁,底层也是基于上述动态渲染模板的逻辑封装。
内容的提问来源于stack exchange,提问作者Alaksandar Jesus Gene
相关产品推荐
相关产品推荐

