0
点赞
收藏
分享

微信扫一扫

Angular 自定义指令 Tooltip

Yeah,关注我的读者应该知道,上一篇文章了解 Angular 开发的内容​,我们已经概览了 ​​Angular​​ 的相关内容。在自定义指令的部分,我们已经能够实现编写,但是,在实际场景中,我们还需要标准化的管理。

Angular 是 Angular.js 的升版

So,本文,我们就以 ​​Tooltip​​ 来讲解下自定义指令的内容。

线上效果图,如下:

Angular 自定义指令 Tooltip_Angular.js

目录结构

在上一篇文章的实现的代码项目基础上,执行命令行:

# 进入 directives 文件夹
$ cd directives

# 创建 tooltip 文件夹
$ mkdir tooltip

# 进入 tooltip 文件夹
$ cd tooltip

# 创建 tooltip 组件
$ ng generate component tooltip

# 创建 tooltip 指令

执行完上面的命令行之后,你会得到 ​​app/directive/tooltip​​ 的文件目录结构如下:

tooltip
├── tooltip // tooltip 组件
│ ├── user-list.component.html // 页面骨架
│ ├── user-list.component.scss // 页面独有样式
│ ├── user-list.component.spec.ts // 测试文件
│ └── user-list.component.ts // javascript 文件
├── tooltip.directive.spec.ts // 测试文件
└── tooltip.directive.ts // 指令文件

嗯,这里我将组件放在 ​​tooltip​​​ 的同级,主要是方便管理。当然,这个因人而异,你可以放在公共组件 ​​components​​ 文件夹内。

编写 tooltip 组件

在 ​​html​​ 文件中,有:

<div class="caret"></div>
<div class="tooltip-content">{{data.content}}</div>

在样式文件 ​​.scss​​ 中,有:

$black: #000000;
$white: #ffffff;
$caret-size: 6px;
$tooltip-bg: transparentize($black, 0.25); // transparentize 是 sass 的语法
$grid-gutter-width: 30px;
$body-bg-color: $white;
$app-anim-time: 200ms;
$app-anim-curve: ease-out;
$std-border-radius: 5px;
$zindex-max: 100;

// :host 伪类选择器,给组件元素本身设置样式
:host {
position: fixed;
padding: $grid-gutter-width/3 $grid-gutter-width/2;
background-color: $tooltip-bg;
color: $body-bg-color;
opacity: 0;
transition: all $app-anim-time $app-anim-curve;
text-align: center;
border-radius: $std-border-radius;
z-index: $zindex-max;
}

.caret { // 脱字符
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid $tooltip-bg;
position: absolute;
top: -$caret-size;
left: 50%;
margin-left: -$caret-size/2;
border-bottom-color: $tooltip-bg;
}

嗯~,​​css​​​ 是一个神奇的东西,之后会安排一篇文章来讲解下 ​​sass​​ 相关的内容...

然后,在 ​​javascript​​​ 文件 ​​tooltip.component.ts​​ 内容如下:

import {
Component,
ElementRef, // 元素指向
HostBinding,
OnDestroy,
OnInit
} from '@angular/core';

@Component({
selector: 'app-tooltip', // 标识符,表明我这个组件叫做啥,这里是 app-tooltip
templateUrl: './tooltip.component.html', // 本组件的骨架
styleUrls: ['./tooltip.component.scss'] // 本组件的私有样式
})
export class TooltipComponent implements OnInit {

public data: any; // 在 directive 上赋值
private displayTimeOut:any;

// 组件本身 host 绑定相关的装饰器
@HostBinding('style.top') hostStyleTop!: string;
@HostBinding('style.left') hostStyleLeft!: string;
@HostBinding('style.opacity') hostStyleOpacity!: string;

constructor(private) { }

ngOnInit(): void {
this.hostStyleTop = this.data.elementPosition.bottom + 'px';

if(this.displayTimeOut) {
clearTimeout(this.displayTimeOut)
}

this.displayTimeOut = setTimeout((_: any) => {
// 这里计算 tooltip 距离左侧的距离,这里计算公式是:tooltip.left + 目标元素的.width - (tooltip.width/2)
this.hostStyleLeft = this.data.elementPosition.left + this.data.element.clientWidth / 2 - this.elementRef.nativeElement.clientWidth / 2 + 'px'
this.hostStyleOpacity = '1';
this.hostStyleTop = this.data.elementPosition.bottom + 10 + 'px'
}, 500)
}


// 组件销毁
ngOnDestroy() {
// 组件销毁后,清除定时器,防止内存泄露
if(this.displayTimeOut) {
clearTimeout(this.displayTimeOut)
}
}
}

编写 tooltip 指令

这是本文的重点,具体的说明,我在代码上标注出来~

相关的文件 ​​tooltip.directive.ts​​ 内容如下:

import {
ApplicationRef, // 全局性调用检测
ComponentFactoryResolver, // 创建组件对象
ComponentRef, // 组件实例的关联和指引,指向 ComponentFactory 创建的元素
Directive, ElementRef,
EmbeddedViewRef, // EmbeddedViewRef 继承于 ViewRef,用于表示模板元素中定义的 UI 元素。
HostListener, // DOM 事件监听
Injector, // 依赖注入
Input
} from '@angular/core';

import { TooltipComponent } from './tooltip/tooltip.component';

@Directive({
selector: '[appTooltip]'
})
export class TooltipDirective {
@Input("appTooltip") appTooltip!: string;

private componentRef!: ComponentRef<TooltipComponent>;

// 获取目标元素的相关位置,比如 left, right, top, bottom
get elementPosition() {
return this.elementRef.nativeElement.getBoundingClientRect();
}

constructor(protected elementRef: ElementRef,
protected appRef: ApplicationRef,
protected componentFactoryResolver: ComponentFactoryResolver,
protected) { }

// 创建 tooltip
protected createTooltip() {
this.componentRef = this.componentFactoryResolver
.resolveComponentFactory(TooltipComponent) // 绑定 tooltip 组件
.create(this.injector);

this.componentRef.instance.data = { // 绑定 data 数据
content: this.appTooltip,
element: this.elementRef.nativeElement,
elementPosition: this.elementPosition
}

this.appRef.attachView(this.componentRef.hostView); // 添加视图
const domElem = (this.componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;
document.body.appendChild(domElem);
}

// 删除 tooltip
protected destroyTooltip() {
if(this.componentRef) {
this.appRef.detachView(this.componentRef.hostView); // 移除视图
this.componentRef.destroy();
}
}

// 监听鼠标移入
@HostListener('mouseover')
OnEnter() {
this.createTooltip();
}

// 监听鼠标移出
@HostListener('mouseout')
OnOut() {
this.destroyTooltip();
}

}

到这里,已经完成了 ​​99%​​ 的功能了,下面我们在页面上调用即可。

页面上调用

我们在 ​​user-list.component.html​​ 上添加下面的内容:

<p style="margin-top: 100px;">
<!-- [appTooltip]="'Hello Jimmy'" 是重点 -->
<span
[appTooltip]="'Hello Jimmy'"
style="margin-left: 200px; width: 160px; text-align: center; padding: 20px 0; display: inline-block; border: 1px solid #999;"Jimmy</span>
</p>

​TooltipDirective​​​ 这个指令我们已经在 ​​app.module.ts​​ 上进行声明,我们直接调用即可。目前的效果如下:

Angular 自定义指令 Tooltip_前端_02

我们实现的 ​​tooltip​​​ 是底部居中展示,也就是我们平常使用框架,比如 ​​angular ant design​​​ 中 ​​tooltip​​​ 的 ​​bottom​​ 属性。对于其他属性,读者感兴趣的话,可以进行扩展。

至此,我们可以很好的维护自己编写的指令文件了。

【完】✅

举报

相关推荐

0 条评论