Angular由一个bug说起之十五:自定义基于Overlay的Tooltip

背景

工具提示(tooltip)是一个常见的 UI 组件,用于在用户与页面元素交互时提供额外的信息。由于angular/material/tooltip的matTooltip只能显示纯文本,所以我们可以通过自定义Directive来实现一个灵活且功能丰富的tooltip

Overlay

OverlayRefattach()支持ComponentPortalTemplatePortal等,为了统一管理overlay的内容,我们需要创建一个OverlayToolTipComponent用来展示具体的tooltip

@Component({selector: 'overlay-tooltip-inner',template: `<div class="overlay-tooltip-inner">@if (text) {<div>{{ text }}</div>} @else {<ng-container *ngTemplateOutlet="contentTemplate"></ng-container>}</div>`,styles: [`.overlay-tooltip-inner {padding: 5px;background-color:rgb(207, 229, 248);border-radius: 4px;box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.2);}`],standalone: false
})
export class OverlayToolTipComponent {@Input()set overlayTooltip(tooltip: string | TemplateRef<any>) {if (_.isString(tooltip)) {this.text = tooltip;} else {this.contentTemplate = tooltip;}}text: string;contentTemplate: TemplateRef<any>;constructor() {//}
}

OverlayToolTipDirective

接下来创建OverlayToolTipDirective,它接受的tooltip参数类型是string | TemplateRef<any>

@Directive({selector: '[overlayTooltip]',standalone: false
})
export class OverlayToolTipDirective implements OnChanges, OnDestroy {private _overlayRef: OverlayRef = undefined;private _tooltip: string | TemplateRef<any> = '';@Input()set overlayTooltip(tooltip: string | TemplateRef<any>) {this._tooltip = tooltip ?? '';}private flexibleConnectedPositionStrategy: FlexibleConnectedPositionStrategy;constructor(private _overlay: Overlay,private _overlayPositionBuilder: OverlayPositionBuilder,private _elementRef: ElementRef) {//}ngOnChanges(changes: SimpleChanges): void {if (_.size(this._tooltip) > 0) {this.updateFlexibleConnectedPositionStrategy();this.bindingTriggers();}}private updateFlexibleConnectedPositionStrategy() {this.flexibleConnectedPositionStrategy = this._overlayPositionBuilder.flexibleConnectedTo(this._elementRef).withPositions([this.createPosition('center', 'top', 'center', 'bottom')]);}private generateOverlayRef() {if (!this.flexibleConnectedPositionStrategy) {this.updateFlexibleConnectedPositionStrategy();}this._overlayRef = this._overlay.create({ positionStrategy: this.flexibleConnectedPositionStrategy });}private createPosition(originX: HorizontalConnectionPos, originY: VerticalConnectionPos,overlayX: HorizontalConnectionPos, overlayY: VerticalConnectionPos): ConnectionPositionPair {return { originX, originY, overlayX, overlayY };}private bindingTriggers() {this._elementRef.nativeElement.addEventListener('mouseover', this.show());this._elementRef.nativeElement.addEventListener('mouseout', this.hide());}private show() {if (!this._overlayRef) {this.generateOverlayRef();}if (this._overlayRef && !this._overlayRef.hasAttached()) {const tooltipRef: ComponentRef<OverlayToolTipComponent> = this._overlayRef.attach(new ComponentPortal(OverlayToolTipComponent));tooltipRef.instance.overlayTooltip = this._tooltip;}}private hide() {if (!_.isEmpty(this._overlayRef) && this._overlayRef.hasAttached()) {this._overlayRef.detach();}}private cleanUpOverlayRef() {if (this._overlayRef?.dispose) {this._overlayRef.dispose();this._overlayRef = undefined;}}ngOnDestroy() {this.cleanUpOverlayRef();this.removeExistingListeners();}removeExistingListeners() {this._elementRef.nativeElement.removeEventListener('mouseover', this.show());this._elementRef.nativeElement.removeEventListener('mouseout', this.hide());}
}

效果如下:

位置自适应

由上图可以看出,当位置不够容纳tooltip时,目标元素会被遮挡。所以我们需要添加placementautoPosition允许用户指定tooltip的位置和tooltip是否可以自适应位置

通过OverlayPositionBuilderwithPositions()设置position数组。

class ConnectionPositionPairExt extends ConnectionPositionPair {sort: number;
}export class OverlayToolTipDirective implements OnChanges, OnDestroy {
...@Input() placement: 'top' | 'bottom' | 'left' | 'right' = 'top';@Input() autoPosition = true;// updateFlexibleConnectedPositionStrategy() 更改如下:private updateFlexibleConnectedPositionStrategy() {this.flexibleConnectedPositionStrategy = this._overlayPositionBuilder.flexibleConnectedTo(this._elementRef).withPositions(this.getAvailablePositions());}private getAvailablePositions(): ConnectionPositionPairExt[] {// 生成四个方向的默认位置配置const positions = [this.createPosition('center', 'top', 'center', 'bottom', 1), // topthis.createPosition('start', 'center', 'end', 'center', 2), // leftthis.createPosition('center', 'bottom', 'center', 'top', 3), // bottomthis.createPosition('end', 'center', 'start', 'center', 4), // right];// 根据当前 placement 设置优先级const priorityMap: { [key in string]: number } = {['bottom']: 2,['left']: 1,['right']: 3,};positions[priorityMap[this.placement] || 0].sort = 0;// 返回排序后的位置配置return this.autoPosition ? positions.sort((a, b) => a.sort - b.sort) : [positions[priorityMap[this.placement] || 0]];}
...
}

效果如下,string或者template

总结

这样我们就在不引入其他库的前提下完成了一个内容丰富位置灵活的tooltip组件啦。

要注意,在tooltip被触发时再创建OverlayRef以避免不必要的性能开销。当tooltip隐藏和Directive销毁时,删除事件监听并调用OverlayRef的detach()dispose()

另外,Overlay的ConnectedPosition还可以指定tooltip和目标元素之间的距离,也可以增加panelClass以便深度定制tooltip的内容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/899565.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件工程面试题(十五)

1、servlet 创建过程以及ruquest,response,session的生命周期? Servlet的创建过程: 第一步 public class AAA extends HttpServlet{ 实现对应的doxxx方法 } 第二步: 在web.xml中配置 <servlet> <servlet-name></servlet-name> <servlet-c…

搭建QNX Software Center的Docker环境

背景 本人使用 Ubuntu Server 22.04 服务器&#xff0c;所以没有图形界面&#xff0c;而 QNX Software Center 需要图形界面。为了保证服务器环境的整理&#xff0c;计划使用Docker部署QNX Software Center 一瓶安装图形界面。本方既是实现方案的记录。 资源 Dockerfile&…

C#/.NET/.NET Core技术前沿周刊 | 第 31 期(2025年3.17-3.23)

前言 C#/.NET/.NET Core技术前沿周刊&#xff0c;你的每周技术指南针&#xff01;记录、追踪C#/.NET/.NET Core领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿&#xff0c;助力技术成长与视野拓宽。 欢迎投稿、推荐…

粘包问题解决方案

粘包问题详解&#xff1a;TCP协议中的常见问题及Go语言解决方案 一、什么是粘包问题&#xff1f; 粘包问题是指在TCP通信中&#xff0c;发送方发送的多个独立消息在接收方被合并成一个消息接收的现象。换句话说&#xff0c;发送方发送的多条消息在接收方被“粘”在一起&#…

vue:突然发现onok无法使用

const that this;this.$confirm({title: "修改商品提示",content: "如果当前商品存在于商品活动库&#xff0c;则在商品活动库的状态会下架",onOk: function () {that.submitForm();}}); 突然发现 this.$confirm无法进入onok 最终发现是主题冲突&#x…

redis hashtable 的sizemask理解

在 Redis 的哈希表实现中&#xff0c;index hash & dict->ht[0].sizemask 是计算键值对应存储位置的核心操作。这个操作看起来简单&#xff0c;但背后涉及哈希表的内存布局和性能优化策略。我们通过以下步骤逐步解析其原理&#xff1a; 一、哈希表的设计目标 快速定位…

Ruby 命令行选项

Ruby 命令行选项 概述 Ruby 是一种广泛使用的编程语言,它拥有强大的命令行工具,可以帮助开发者进行各种任务。了解 Ruby 的命令行选项对于提高开发效率至关重要。本文将详细介绍 Ruby 的常用命令行选项,帮助开发者更好地利用 Ruby 的命令行功能。 Ruby 命令行选项概述 R…

【STM32】WDG看门狗(学习笔记)

学习来源----->江协科技STM32 WDG简介 WDG&#xff08;Watchdog&#xff09;看门狗看门狗可以监控程序的运行状态&#xff0c;当程序因为设计漏洞、硬件故障、电磁干扰等原因&#xff0c;出现卡死或跑飞现象时&#xff0c;看门狗能及时复位程序&#xff0c;避免程序陷入长…

Java 数据库连接池

HikariCP 老外开源的。 Spring Boot 2 之后默认选择的连接池。 号称性能最快的数据库连接池。 为什么性能好呢&#xff1f; ● 字节码级别的优化-尽量的利用 JIT 的内联手段 ● 字节码级别的优化-利用更容易被 JVM 优化的指令 ● 代码级别的优化-利用改造后的 FastList 代替…

Spring Boot中@Valid 与 @Validated 注解的详解

Spring Boot中Valid 与 Validated 注解的详解 引言Valid注解功能介绍使用场景代码样例 Validated注解功能介绍使用场景代码样例 Valid与Validated的区别结论 引言 在Spring Boot应用中&#xff0c;参数校验是确保数据完整性和一致性的重要手段。Valid和Validated注解是Spring …

C++搜索

功能扩展说明&#xff1a; 图类封装&#xff1a;将图数据结构封装为类&#xff0c;提高代码复用性 最短路径查找&#xff1a;基于BFS实现未加权图的最短路径查找 路径重构&#xff1a;通过parent数组回溯构建完整路径 异常处理&#xff1a;当路径不存在时返回空向量 复杂度分析…

2023第十四届蓝桥杯大赛软件赛国赛C/C++ 大学 B 组(真题题解)(C++/Java题解)

本来想刷省赛题呢&#xff0c;结果一不小心刷成国赛了 真是个小迷糊〒▽〒 但&#xff0c;又如何( •̀ ω •́ )✧ 记录刷题的过程、感悟、题解。 希望能帮到&#xff0c;那些与我一同前行的&#xff0c;来自远方的朋友&#x1f609; 大纲&#xff1a; 一、子2023-&#xff…

CSS学习笔记6——网页布局

目录 一、元素的浮动属性、清除浮动 清除浮动的其他方法 1、使用空标签清除浮动影响 2、使用overflow属性清除浮动 3、使用伪元素清除浮动影响 原理 overflow属性 二、元素的定位 1、相对定位 2、绝对定位 ​编辑 3、固定定位 z-index层叠等级属性 一、元素的浮动…

sqlalchemy:将mysql切换到OpenGauss

说明 之前python的项目使用的mysql&#xff0c;近期要切换到国产数据库OpenGauss。 之前的方案是fastapisqlalchemy&#xff0c;测试下来发现不用改代码&#xff0c;只要改下配置即可。 切换方案 安装openGauss-connector-python-psycopg2 其代码工程在&#xff1a;https:…

uniapp 获取dom信息(封装获取元素信息工具函数)

在uniapp开发中&#xff0c;需要获取到dom的信息&#xff0c;需要用到uniapp的指定方式 uni.createSelectorQuery()&#xff0c;但是每次需要用到的时候都需要很长一段的繁琐代码&#xff0c;本篇文章将呈现获取dom信息方法封装&#xff0c;话不多说&#xff0c;上菜&#xff1…

Linux之数据链路层

Linux之数据链路层 一.以太网1.1以太网帧格式1.2MAC地址1.3MTU 二.ARP协议2.1ARP协议工作流程2.2ARP协议格式 三.NAT技术四.代理服务4.1正向代理4.2反向代理 五.四大层的学习总结 一.以太网 在我们学习完了网络层后我们接下来就要进入数据链路层的学习了&#xff0c;在学习完网…

MySQL的基础语法2(函数-字符串函数、数值函数、日期函数和流程函数 )

目录 一、字符串函数 1.常见字符串函数 ​编辑 2.字符串函数的基本使用 3.字符串函数的数据库案例演示 二、数值函数 1.常见数值函数&#xff08;如下&#xff09;&#xff1a; 2.数值函数的基本使用 3.数值函数的数据库案例演示 三、日期函数 1.常见的日期函数 2.日…

全新版租赁商城小程序源码系统 源码开源支持二开+图文搭建教程

在互联网商业的浪潮中&#xff0c;租赁业务凭借其独特的优势&#xff0c;正逐渐成为市场的新宠。对于开发者而言&#xff0c;快速搭建一个功能完备的租赁商城小程序&#xff0c;不仅能满足市场需求&#xff0c;还能为自己的业务拓展带来新的机遇。分享一款全新版租赁商城小程序…

Cent OS7+Docker+Dify

由于我之前安装了Dify v1.0.0&#xff0c;出现了一些问题&#xff1a;无法删除&#xff0c;包括&#xff1a;知识库中的文件、应用、智能体、工作流&#xff0c;都无法删除。现在把服务器初始化&#xff0c;一步步重新安装&#xff0c;从0到有。 目录 1、服务器重装系统和配置…

OSI 七层模型和四层模型(TCP/IP 模型)

文章目录 前言一、OSI 七层模型二、TCP/IP 四层模型三、运行协议及设备1. OSI 七层模型2. TCP/IP 四层模型3. 运行协议4. 各类设备的作用 总结 前言 OSI 七层模型和四层模型&#xff08;TCP/IP 模型&#xff09;是两种常见的网络协议分层架构&#xff0c;它们的主要区别如下&a…