简体   繁体   English

Accessing an external SVG File and adding an angular material Tooltip to a specific SVG element inside the whole SVG from a typescript file: Angular

[英]Accessing an external SVG File and adding an angular material Tooltip to a specific SVG element inside the whole SVG from a typescript file: Angular

Specific parts of the angular code angular代码的具体部分

|SVG File| |SVG 文件|

<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
     width="950" height="450" viewBox="0 0 1280.000000 1119.000000"
     preserveAspectRatio="xMidYMid meet">
<circle r="50px" cx="650" cy="968" fill="red" stroke="black" visibility="visible"/>
  <rect x="650.117" y="968.413" width="1000" height="800" fill="green" visibility="visible"/>
</svg>

|HTML File| |HTML 文件|

<object #svgmyimage>
</object>

|Type Script File| |键入脚本文件|

 @ViewChild('svgmyimage') imageSvg?:ElementRef;

 ngOnInit() {
    this.reset();
    this.httpClient
          .get(`assets/svgimages/image1.svg`, { responseType: 'text' })
          .subscribe((value: string) => {
            this.svgStr = value;
//             console.log(this.svgStr);
            this.drawImage();
          });

  }

  drawImage() {
    if (this.imageSvg && this.svgStr) {

      this.imageSvg.nativeElement.innerHTML = this.svgStr;

      if(this.imageSvg.nativeElement.children[0])
      {
          this.imageSvg.nativeElement.children[0]setAttribute('visibility', 'visible');
          this.imageSvg.nativeElement.children[1]setAttribute('fill', 'yellow');
      }

    }
  }

Like this.像这样。 I am able to access and change the attributes to add a Angular Material Tooltip.我可以访问和更改属性以添加 Angular 材质工具提示。 I could use tooltip property in the HTML file itself and it works provided the whole svg code is inside my HTML rather than linked as an external file.我可以在 HTML 文件本身中使用工具提示属性,并且只要整个 svg 代码在我的 HTML 内部而不是作为外部文件链接,它就可以工作。 Could someone help me on this.有人可以帮我解决这个问题。
As作为

this.imageSvg.nativeElement.children[0]setAttribute('matTooltip', 'information');

makes no sense as Tooltip is not a property of the SVG.没有意义,因为 Tooltip 不是 SVG 的属性。 So, how to add tooltip to an externally linked svg file in html from the typescript fil.那么,如何从 typescript 文件的 html 中将工具提示添加到外部链接的 svg 文件。 I wanna add tooltip only to the circle in the svg!我只想将工具提示添加到 svg 中的圆圈!

To create a "tooltip" we can take two approach: use popperjs (like use ng-bootstrap or the own bootstrap) or use the own cdkoverlay (like use material angular).要创建“工具提示”,我们可以采用两种方法:使用popperjs (如使用 ng-bootstrap 或自己的引导程序)或使用自己的cdkoverlay (如使用材料角度)。

To use ckdoverlay we need inject in constructor Overlay, create a Portal and attach when mouseenter happends and deattach when mouseleave.要使用ckdoverlay,我们需要在构造函数Overlay中注入,创建一个Portal并在mouseenter发生时附加并在mouseleave时解除附加。 If we want to use HTMLElements we can create a class to encapsulate all the logic.如果我们想使用 HTMLElements,我们可以创建一个 class 来封装所有逻辑。

export class TooltipOverLay {
  overlayRef: OverlayRef;
  domPortal:DomPortal
  showSubscription: Subscription = null;
  hideSubscription: Subscription = null;

  //in constructor we received the OverLay, and two htmlElements:
  //one (element) where is attached the another one (tooltip)

  constructor(overlay:Overlay,element: HTMLElement,
              tooltip:HTMLElement, options: string[]) {

    //we create a portal
    this.domPortal=tooltip instanceof HTMLElement?new DomPortal(tooltip):null;

     //we create a reference 
   this.overlayRef=this.createOverLay(overlay,element,
                    options?options.map(x=>POS[x]):null)

    //subscribe to "mouseenter" and "focus"
    this.showSubscription = merge(
      fromEvent(element, 'mouseenter'),
      fromEvent(tooltip, 'focus')
    ).subscribe(() => {

      //we attach the "portal"
      if (!this.overlayRef.hasAttached()) 
            this.overlayRef.attach(this.domPortal)
            setTimeout(()=>{
              this.domPortal.element.setAttribute('data-show','')

            })
    });

    //subscribe to "mouseleave" and "blur"
    this.hideSubscription = merge(
      fromEvent(element, 'mouseleave'),
      fromEvent(tooltip, 'blur')
    ).subscribe(() => {

      //we "deattach" the element
      if (this.overlayRef.hasAttached()) 
      {
        this.domPortal.element.removeAttribute('data-show')
        this.overlayRef.detach();
      }
    });
  }

  //we create the overLayRef in a private function
  private createOverLay(overlay:Overlay,element:HTMLElement,positions:any=null)
  {
    positions=positions?[...positions,POS.top,POS.bottom]:[POS.top,POS.bottom]
    const scrollStrategy = overlay.scrollStrategies.reposition();
    const positionStrategy = overlay
      .position()
      .flexibleConnectedTo(element)
      
      .withPositions(positions)
      .withPush(false);
    return  overlay.create({
      positionStrategy,
      scrollStrategy,
      hasBackdrop: false,
    });
  }

  //a function "destroy" help us to unsubscribe
  destroy() {
    this.showSubscription && this.showSubscription.unsubscribe();
    this.hideSubscription && this.hideSubscription.unsubscribe();
    if (this.overlayRef.hasAttached()) {
      this.overlayRef.detach();
    }
    this.overlayRef.dispose();
  }
}

A important note when we use a DomPortal is that, when is not attached it is in the.html -we can also create a TemplatePortal, but for this it's necesary inject also a ViewContainerRef-.当我们使用 DomPortal 时,一个重要的注意事项是,当未附加时,它位于.html - 我们也可以创建一个 TemplatePortal,但为此它还需要注入一个 ViewContainerRef-。 To "hide" when is not attach we can use a typical.css:要在未附加时“隐藏”,我们可以使用典型的.css:

.tooltip {
  ...
  display:none;
}

.tooltip[data-show] {
  display:block
}

So, if we add an attribute "data-show" the element is showed, and if we remove the element is not showed因此,如果我们添加一个属性“data-show”,则显示该元素,如果我们删除该元素,则不显示该元素

The another important note is use a "confortable way" to indicate if the position is bottom, down, left or rigth.另一个重要注意事项是使用“舒适的方式”来指示 position 是底部、向下、左侧还是右侧。 To do it we define as const an object like为此,我们将 object 定义为 const

export const POS = {
  top: {
    originX: 'center',
    originY: 'top',
    overlayX: 'center',
    overlayY: 'bottom',
    offsetY: -5,
  },
  right: {
    originX: 'end',
    originY: 'center',
    overlayX: 'start',
    overlayY: 'center',
    offsetX: 5,
  },
  bottom: {
    originX: 'center',
    originY: 'bottom',
    overlayX: 'center',
    overlayY: 'top',
    offsetY: 5,
  },
  left: {
    originX: 'start',
    originY: 'center',
    overlayX: 'end',
    overlayY: 'center',
    offsetX: -5,
  },
};

Well, with this class we can do two things: A directive好吧,有了这个 class 我们可以做两件事:一个指令

@Directive({
  selector: '[appTooltipOverlay]',
})
export class TooltipOverlayDirective implements OnDestroy {
  tooltip: TooltipOverLay = null;
  options: any = null;
  @Input() set appTooltipOverlay(value: HTMLElement) {
    this.tooltip = new TooltipOverLay(
      this.overlay,
      this.elementRef.nativeElement,
      value,
      this.options
    );
  }

  @Input('positions') set _options(value: string[]) {
    const positions = value
      .map((x) => (POS[x] ? POS[x] : null))
      .filter((x) => x);
    const positionStrategy: PositionStrategy = this.overlay
      .position()
      .flexibleConnectedTo(this.elementRef.nativeElement)
      .withPositions(positions)
      .withPush(false);
    this.tooltip.overlayRef.updatePositionStrategy(positionStrategy);
  }

  constructor(private overlay: Overlay, private elementRef: ElementRef) {}
  ngOnDestroy() {
    this.tooltip.destroy();
  }
}

That we can use like我们可以像这样使用

<button
  [appTooltipOverlay]="tooltip"
  [positions]="['top', 'bottom']"
  aria-describedby="tooltip"
>
  My button
</button>

<div #tooltip class="tooltip">My tooltip overlay</div>

Or we can use the library in.ts in a component that inject the OverLay或者我们可以在注入 OverLay 的组件中使用库 in.ts

tooltipOverLay:TooltipoverLay=null
@ViewChild('tooltip',{static:true}) tooltip?:ElementRef;
@ViewChild('button',{static:true}) button?:ElementRef;
constructor(private overlay:Overlay){}

ngOnInit()
{
    this.tooltipOverLay=new TooltipOverLay(
          this.overlay,this.button.nativeElement,this.tooltip.nativeElement)
}

//Don't forget destroy!!!!
ngOnDestroy()
{
      this.tooltipOverLay && this.tooltipOverLay.destroy()
}

A eg of.html例如.html

   <button #bt aria-describedby="tooltip">My button</button>
   <div #tooltip class="tooltip" role="tooltip">
       My tooltip overlay in .ts
   </div>

In this stackblitz there're a example of both and the same tecnica using popperjs to create the tooltip在这个stackblitz中,有一个使用 popperjs 创建工具提示的相同技术的示例

In this another stackblitz I forked the code of GRM to use the library to attach a tooltip to a inner element of the.svg (NOTE: really I don't understand too much the advantage of use httpClient to add the.svg (we can use a <img src="your.svg"> )另一个堆栈闪电战中,我分叉了 GRM 的代码以使用库将工具提示附加到.svg 的内部元素(注意:我真的不太了解使用 httpClient 添加.svg 的优势(我们可以使用<img src="your.svg"> )

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM