当前位置:首页 > 技术分析 > 正文内容

Angular 自定义指令 Tooltip(angular自定义属性)

ruisui883个月前 (02-03)技术分析11

#头条创作挑战赛#

本文同步本人掘金平台的文章:https://juejin.cn/post/7082241253819023397

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情。

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

Angular 是 Angular.js 的升版

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

线上效果图,如下:

目录结构

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

# 进入 directives 文件夹
$ cd directives

# 创建 tooltip 文件夹
$ mkdir tooltip

# 进入 tooltip 文件夹
$ cd tooltip

# 创建 tooltip 组件
$ ng generate component tooltip

# 创建 tooltip 指令
$ ng generate directive 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 elementRef: ElementRef
  ) { }

  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 injector: Injector
  ) { }

  // 创建 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 上进行声明,我们直接调用即可。目前的效果如下:

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

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

【完】

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/721.html

标签: spec.ts
分享给朋友:

“Angular 自定义指令 Tooltip(angular自定义属性)” 的相关文章

Vue组件通信之props深入详解!

props 是 Vue 组件中一个很重要的概念。它是用来从父组件向子组件传递数据的。为什么需要props?这是因为在Vue中,组件是相互隔离的。每个组件都有自己的作用域,子组件无法直接访问父组件的状态或值。通过props,父组件可以将数据传递给子组件。使用props的步骤:1. 在子组件中定义pro...

摄影后期必看 | PS插件camera raw 16.4教程 | 范围蒙版

范围蒙版Camera Raw 【蒙版】模块中提供了三个范围蒙版工具,可以通过特定的范围来创建蒙版。此次新增的【范围蒙版】大大加强了acr插件对局部调整的能力。点击下拉小箭头可以看到【颜色范围】,可用于快速选择想要编辑的颜色。快捷键:Shift + C【明亮度范围】,可用于快速选择想要调整的明亮度。快...

Acustica Audio 发布模拟Roland Jupiter 双声道合成器插件 TH2

福利: Acustica Audio 发布模拟Roland Jupiter 风格的双声道合成器插件 TH2 免费下载 意大利 Acustica Audio 公司发布布模拟Roland Jupiter 风格的双声道合成器插件 TH2 ,灵感来源于Acustica Audio的THING-8系列,它是...

vue-router是如何解析query参数呢? #前端

vue-router 中的 query 解析。1. 大家好,我是龙仔。今天来分享 vue-router 是如何解析快乐参数的,因为使用 vue 路由会传 query 参数和快乐参数,所以从 vue 的角度来看如何解析传递的快乐参数。2. 基础知识大家应知道,快乐参数结构如:a、b、c、a、b、c、a...

SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序

//xia仔のke:chaoxingit.com/208/全栈开发医疗小程序:利用Spring Boot 2.X、Vue和UniApp在当今数字化时代,医疗行业也在不断地迈向信息化和智能化的方向。开发一款医疗小程序,能够为用户提供便捷的医疗服务和信息查询,为医疗机构提供高效的管理和服务渠道。本文将介...

TDesign企业级开源设计系统越发成熟稳定,支持 Vue3 / 小程序

TDesing 发展越来越好了,出了好几套组件库,很成熟稳定了,新项目完全可以考虑使用。早在2021年,腾讯的 TDesing 刚发布不久,我就写了一篇简短的文章来介绍,当时主要关注的是 TDesign 的 Vue 组件库和用来搭建 admin 后台系统的实用性。虽然当时看起来不错,但还处于测试版,...