首页 > 解决方案 > 在 Angular 中使用自定义组件作为工具提示

问题描述

是否可以在 Angular 8 中使用自定义组件作为工具提示?
如果不直接设置 mat-tooltip 样式,我想知道是否可以在使用指令时使用自定义组件显示为工具提示。就像“显示工具提示时,显示我的组件”。
可以做到吗?

标签: tooltipangular8

解决方案


是的,您可以创建自定义工具提示指令。

工具提示.directive.ts

import { Directive, Input, ElementRef, HostListener, Renderer2 } from '@angular/core';

@Directive({
  selector: '[tooltip]'
})
export class TooltipDirective {
  @Input('tooltip') tooltipTitle: string;
  @Input() placement: string;
  @Input() delay: string;
  tooltip: HTMLElement;
  offset = 10;

  constructor(private el: ElementRef, private renderer: Renderer2) { }

  @HostListener('mouseenter') onMouseEnter() {
    if (!this.tooltip) { this.show(); }
  }

  @HostListener('mouseleave') onMouseLeave() {
    if (this.tooltip) { this.hide(); }
  }

  show() {
    this.create();
    this.setPosition();
    this.renderer.addClass(this.tooltip, 'ng-tooltip-show');
  }

  hide() {
    this.renderer.removeClass(this.tooltip, 'ng-tooltip-show');
    window.setTimeout(() => {
      this.renderer.removeChild(document.body, this.tooltip);
      this.tooltip = null;
    }, this.delay);
  }

  create() {
    this.tooltip = this.renderer.createElement('span');

    this.renderer.appendChild(
      this.tooltip,
      this.renderer.createText(this.tooltipTitle) // Here is your text
    );

    this.renderer.appendChild(document.body, this.tooltip);

    this.renderer.addClass(this.tooltip, 'ng-tooltip');
    this.renderer.addClass(this.tooltip, `ng-tooltip-${this.placement}`);
    this.renderer.setStyle(this.tooltip, '-webkit-transition', `opacity ${this.delay}ms`);
    this.renderer.setStyle(this.tooltip, '-moz-transition', `opacity ${this.delay}ms`);
    this.renderer.setStyle(this.tooltip, '-o-transition', `opacity ${this.delay}ms`);
    this.renderer.setStyle(this.tooltip, 'transition', `opacity ${this.delay}ms`);
  }

  setPosition() {
    const hostPos = this.el.nativeElement.getBoundingClientRect();
    const tooltipPos = this.tooltip.getBoundingClientRect();

    const scrollPos = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;

    let top, left;

    if (this.placement === 'top') {
      top = hostPos.top - tooltipPos.height - this.offset;
      left = hostPos.left + (hostPos.width - tooltipPos.width) / 2;
    }

    if (this.placement === 'bottom') {
      top = hostPos.bottom + this.offset;
      left = hostPos.left + (hostPos.width - tooltipPos.width) / 2;
    }

    if (this.placement === 'left') {
      top = hostPos.top + (hostPos.height - tooltipPos.height) / 2;
      left = hostPos.left - tooltipPos.width - this.offset;
    }

    if (this.placement === 'right') {
      top = hostPos.top + (hostPos.height - tooltipPos.height) / 2;
      left = hostPos.right + this.offset;
    }

    this.renderer.setStyle(this.tooltip, 'top', `${top + scrollPos}px`);
    this.renderer.setStyle(this.tooltip, 'left', `${left}px`);
  }
}

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <div class="tooltip-example">
      <div tooltip="left description" placement="left" delay="500">tootip on left</div>
      <div tooltip="top description" placement="top" delay="500">tootip on top</div>
      <div tooltip="bottom description" placement="bottom" delay="500">tootip on bottom</div>
      <div tooltip="right description" placement="right" delay="500">tootip on right</div>
    </div>
  `,
  styles: [`
    .tooltip-example {
      text-align: center;
      padding: 0 50px;
    }
    .tooltip-example [tooltip] {
      display: inline-block;
      margin: 50px 20px;
      width: 180px;
      height: 50px;
      border: 1px solid gray;
      border-radius: 5px;
      line-height: 50px;
      text-align: center;
    }
    .ng-tooltip {
      position: absolute;
      max-width: 150px;
      font-size: 14px;
      text-align: center;
      color: #f8f8f2;
      padding: 3px 8px;
      background: #282a36;
      border-radius: 4px;
      z-index: 1000;
      opacity: 0;
    }
    .ng-tooltip:after {
      content: "";
      position: absolute;
      border-style: solid;
    }
    .ng-tooltip-top:after {
      top: 100%;
      left: 50%;
      margin-left: -5px;
      border-width: 5px;
      border-color: black transparent transparent transparent;
    }
    .ng-tooltip-bottom:after {
      bottom: 100%;
      left: 50%;
      margin-left: -5px;
      border-width: 5px;
      border-color: transparent transparent black transparent;
    }
    .ng-tooltip-left:after {
      top: 50%;
      left: 100%;
      margin-top: -5px;
      border-width: 5px;
      border-color: transparent transparent transparent black;
    }
    .ng-tooltip-right:after {
      top: 50%;
      right: 100%;
      margin-top: -5px;
      border-width: 5px;
      border-color: transparent black transparent transparent;
    }
    .ng-tooltip-show {
      opacity: 1;
    }
  `]
})
export class AppComponent {}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { TooltipDirective } from './tooltip.directive';

@NgModule({
  declarations: [
    AppComponent,
    TooltipDirective
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

推荐阅读