Chris
Chris

Reputation: 55

Issue with Angular CDK overlay inside another

I am using the Angular CDK overlay to show a modal drawer on my page. I am also using the Angular CDK Overlay for tooltips. It's possible that I may have a control in my drawer with a tooltip. The drawer allows the user to close it by pressing Escape.

My issue is if I have the drawer open and the tooltip showing and I press Escape. The drawer closes but the tooltip remains visible.

This StackBlitz demonstrates the issue (open the drawer, hover to show the tooltip, and press the Escape key).

https://stackblitz.com/edit/angular-material-datepicker-sfpsg7?file=src%2Fapp%2Fapp.component.ts

How can I resolve this?

Upvotes: 2

Views: 474

Answers (1)

Naren Murali
Naren Murali

Reputation: 57886

Two way to solve this problem.

  1. Take a export of the directive using exportAs and then call the close method manually.

Full Code:

App.ts

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

@Component({
  selector: 'my-app',
  template: `
    <button (click)="drawer.open()">Open Drawer</button>

    <pro-drawer #drawer headerText="User Information" (dismissRequested)="drawer.close(); toolTip.hideTooltip();">
      <button style="margin: 150px 0 0 50px;" #toolTip="proTooltip" proTooltip="This is tooltip text">Hover for tooltip</button>
    </pro-drawer>
  `,
})
export class AppComponent {}

directive.ts

import {
  ComponentRef,
  Directive,
  ElementRef,
  HostListener,
  Input,
  OnDestroy,
  TemplateRef,
} from '@angular/core';
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { ProTooltipComponent } from './pro-tooltip.component';

@Directive({
  selector: '[proTooltip]',
  exportAs: 'proTooltip',
})
export class ProTooltipDirective implements OnDestroy {
  @Input('proTooltip') text = '';

  private overlayRef: OverlayRef;
  private tooltipVisible: boolean = false;

  constructor(private overlay: Overlay, private elementRef: ElementRef) {}

  ngOnDestroy() {
    this.hideTooltip();
  }

  @HostListener('mouseenter')
  onMouseEnter() {
    this.showTooltip();
  }

  @HostListener('mouseleave')
  onMouseLeave() {
    this.hideTooltip();
  }

  private showTooltip() {
    if (
      !this.tooltipVisible &&
      !this.overlayRef &&
      (!!this.text || !!this.template)
    ) {
      this.overlayRef = this.overlay.create({
        positionStrategy: this.overlay
          .position()
          .flexibleConnectedTo(this.elementRef)
          .withPositions([
            {
              panelClass: 'tooltip-location-top',
              originX: 'center',
              originY: 'top',
              overlayX: 'center',
              overlayY: 'bottom',
            },
          ]),
      });

      const tooltipRef: ComponentRef<ProTooltipComponent> =
        this.overlayRef.attach(new ComponentPortal(ProTooltipComponent));
      tooltipRef.instance.text = this.text;

      this.tooltipVisible = true;
    }
  }

  private hideTooltip() {
    if (this.tooltipVisible && this.overlayRef) {
      this.overlayRef.detach();
      if (this.overlayRef) {
        this.overlayRef.dispose();
        this.overlayRef = null;
      }
      this.tooltipVisible = false;
    }
  }
}

Stackblitz Demo

  1. Listen to window keydown event using Hostlistener and trigger the close on press of escape button.

Full Code:

directive.ts

import {
  ComponentRef,
  Directive,
  ElementRef,
  HostListener,
  Input,
  OnDestroy,
  TemplateRef,
} from '@angular/core';
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { ProTooltipComponent } from './pro-tooltip.component';

@Directive({
  selector: '[proTooltip]',
})
export class ProTooltipDirective implements OnDestroy {
  @Input('proTooltip') text = '';

  private overlayRef: OverlayRef;
  private tooltipVisible: boolean = false;

  constructor(private overlay: Overlay, private elementRef: ElementRef) {}

  ngOnDestroy() {
    this.hideTooltip();
  }

  @HostListener('mouseenter')
  onMouseEnter() {
    this.showTooltip();
  }

  @HostListener('mouseleave')
  onMouseLeave() {
    this.hideTooltip();
  }

  @HostListener('window:keydown.escape')
  onEscape() {
    console.log('window.keydown.escape');
    this.hideTooltip();
  }

  private showTooltip() {
    if (
      !this.tooltipVisible &&
      !this.overlayRef &&
      (!!this.text || !!this.template)
    ) {
      this.overlayRef = this.overlay.create({
        positionStrategy: this.overlay
          .position()
          .flexibleConnectedTo(this.elementRef)
          .withPositions([
            {
              panelClass: 'tooltip-location-top',
              originX: 'center',
              originY: 'top',
              overlayX: 'center',
              overlayY: 'bottom',
            },
          ]),
      });

      const tooltipRef: ComponentRef<ProTooltipComponent> =
        this.overlayRef.attach(new ComponentPortal(ProTooltipComponent));
      tooltipRef.instance.text = this.text;

      this.tooltipVisible = true;
    }
  }

  private hideTooltip() {
    if (this.tooltipVisible && this.overlayRef) {
      this.overlayRef.detach();
      if (this.overlayRef) {
        this.overlayRef.dispose();
        this.overlayRef = null;
      }
      this.tooltipVisible = false;
    }
  }
}

Stackblitz Demo

Upvotes: 1

Related Questions