0

In my app, on click of an element I am showing a tooltip that contains additional information. This is available for all elements in a list. I would like to be able to close the expansion on click of anything outside of the tooltip.

The concept is essentially what is covered in this answered question but I need it to work if there are multiple instances of the element. I have attempted using ViewChildren for the elements rather than ViewChild as there are multiple but that doesn't achieve what I'm looking for. I would like to avoid using any packages that are not already available from @angular/core, rxjs etc., if possible. See below a simplified and condensed version of what I've attempted with no success so far.

HTML

<li *ngFor="let listItem of listItems;">
  <p>
    <span *ngIf="showTooltip" class="item-tooltip" #itemTooltip>
      Tooltip with {{ listItem.detailed }}
      <span (click)="toggleTooltip()">Close</span>
    </span>
    <span (click)="toggleTooltip()" #tooltipTrigger>{{ listItem.blurb }}</span>
  </p>
</li>

TS

@ViewChildren('itemTooltip') itemTooltip: ElementRef;
@ViewChildren('tooltipTrigger') tooltipTrigger: ElementRef;
this.showTooltip = false;

constructor( private renderer: Renderer2) {
  this.renderer.listen('window', 'click', (e: Event) => {
    if (this.showTooltip && e.target !== this.itemTooltip.nativeElement) {
      // never gets to here 
      console.log(‘click detected’);
      this.toggleTooltip();
    }
  });
}

toggleTooltip() {
  const tooltipStatus = this.showTooltip;
  if (tooltipStatus = true) {
    this.showTooltip = false;
  } else if (tooltipStatus = false) {
    this.showTooltip = true;
  }
}

1 Answers1

0

Create a Directive in Common Place to access from anywhere, Add it to Module also for using Directive

import {Directive, ElementRef, Output, EventEmitter, HostListener} from '@angular/core';
 
@Directive({
    selector: '[clickOutside]'
})
export class ClickOutsideDirective {
    constructor(private _elementRef : ElementRef) {
    }
 
    @Output()
    public clickOutside = new EventEmitter();
 
    @HostListener('document:click', ['$event.target'])
    public onClick(targetElement) {
        const clickedInside = this._elementRef.nativeElement.contains(targetElement);
        if (!clickedInside) {
            this.clickOutside.emit(null);
        }
    }
}

HTML

<li *ngFor="let listItem of listItems;">
  <p>
    <span *ngIf="showTooltip" class="item-tooltip" #itemTooltip>
      Tooltip with {{ listItem.detailed }}
      <span (clickOutside )="closeTooltip()">Close</span>
    </span>
    <span (click)="openTooltip()" #tooltipTrigger>{{ listItem.blurb }}</span>
  </p>
</li>

TS

openTooltip = () => { showTooltip = true; }

clickOutside = () => { showTooltip = false; }
  • Thanks but that’s not the issue. That function isn’t even getting to the point of being called from the this.renderer.listen click event. I will update the code sample to make that clearer. – ChocolateOrange Aug 08 '20 at 16:57
  • I also updated that function just to clear up any confusion but the question isn't really regarding that function, that was just added for some more context. The question is regarding being able to actually get that function, or any function, to even get called from the click event. – ChocolateOrange Aug 08 '20 at 18:51
  • Whats about using Creating a directive and adding @HostListner to that directive – Subhojit Khara Aug 10 '20 at 09:56