components icon indicating copy to clipboard operation
components copied to clipboard

[Tooltip] Tooltip does not vanish after route navigation with reusable routes

Open nharrer opened this issue 6 years ago • 12 comments

Bug, feature request, or proposal:

Bug

What is the expected behavior?

A displayed tooltip should vanish, after navigating to a route with a different component.

What is the current behavior?

When a RouteReuseStrategy is implemented, then the tooltip doesn't vanish if the original route is detached while the tooltip was visible.

What are the steps to reproduce?

StackBlitz example: https://stackblitz.com/edit/angular-yvcjzp

A tooltip is displayed on button "Goto Page 2". Clicking on the button will navigate to page 2. This will causes the tooltip from page 1 to be stuck on the screen. It will only vanish once the user navigates back to page 1 again.

Disabling reusable routes by removing the custom RouteReuseStrategy will lead to the the expected behavior again:

providers: [
    // remove this, and the tooltip starts to work properly
    { provide: RouteReuseStrategy, useClass: CustomReuseStrategy }
  ],

What is the use-case or motivation for changing an existing behavior?

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

"dependencies": { "@angular/animations": "6.0.0", "@angular/cdk": "^6.0.0", "@angular/common": "6.0.0", "@angular/compiler": "6.0.0", "@angular/core": "6.0.0", "@angular/forms": "6.0.0", "@angular/http": "6.0.0", "@angular/material": "^6.0.0", "@angular/platform-browser": "6.0.0", "@angular/platform-browser-dynamic": "6.0.0", "@angular/router": "6.0.0", "core-js": "^2.4.1", "rxjs": "^6.1.0", "rxjs-compat": "^6.1.0", "zone.js": "^0.8.26" },

Is there anything else we should know?

nharrer avatar May 23 '18 16:05 nharrer

we have the same issue.

"dependencies": { "@angular/animations": "^6.0.5", "@angular/cdk": "^6.3.0", "@angular/common": "^6.0.5", "@angular/compiler": "^6.0.5", "@angular/core": "^6.0.5", "@angular/flex-layout": "6.0.0-beta.16", "@angular/forms": "^6.0.5", "@angular/http": "^6.0.5", "@angular/material": "^6.3.0", "@angular/platform-browser": "^6.0.5", "@angular/platform-browser-dynamic": "^6.0.5", "@angular/router": "^6.0.5", "core-js": "^2.5.7", "ng-http-loader": "^2.3.0", "rxjs": "6.2.1", "upgrade-angular": "^0.1.3", "zone.js": "^0.8.19" },

MrD0llaro avatar Jun 19 '18 08:06 MrD0llaro

Same issue, is it possible any workarounds for this issue?

Vizer avatar Aug 20 '18 11:08 Vizer

I also have same issue. Posted in StackOverflow and sample in stackblitz

vinothsubramanian avatar Sep 11 '18 05:09 vinothsubramanian

@Vizer Until this is fixed, I remove the tooltip manually with jquery from within the destination route component where the tooltip gets stuck:

public ngAfterViewInit() { $('mat-tooltip-component div:contains("Tooltiptext")').remove(); }

I know. Very ugly.

nharrer avatar Sep 11 '18 06:09 nharrer

@nharrer Thanks for the work around.

I am not very sure it is right way do native. But it worked for me inside RouteReuseStrategy -> store method

while(document.getElementsByTagName('mat-tooltip-component').length > 0) { document.getElementsByTagName('mat-tooltip-component')[0].remove(); }

Workaround StackBlitz

vinothsubramanian avatar Sep 11 '18 09:09 vinothsubramanian

@nharrer, thanks for message. I have came up with other solution using tooltip's methods. In my case it works like this:

  • related template
<a class="action" [routerLink]="[itemId, 'details']">
  <i class="material-icons" (click)="navigate($event)"
    [matTooltip]="'Details'">assignment</i>
</a>
  • related component code:
...
@ViewChild(MatTooltip)
tooltip: MatTooltip;
...
navigate(event: MouseEvent) {
    event.stopPropagation();
    event.stopImmediatePropagation(); // optional in my case
    event.preventDefault(); // optional in my case

    this.tooltip.hide();

    setTimeout(() => {
      this._router.navigate([this.itemId, 'details'], {
        relativeTo: this._route
      });
    }, 50);
  } 

Hope this helps someone :)

Vizer avatar Sep 14 '18 11:09 Vizer

Any update on this?

vsmonteiro avatar Nov 06 '18 15:11 vsmonteiro

There's the problem also in nebular 5.0.

My current workaround:

Not a real solution but i made my own tooltip using like this. adjust margin-top for distance from item.

>component.html

<div class="myTooltip">
    <span class="myTooltipText"> <!--Tooltip content --> </span>
    ...
</div>

>component.scss

.myTooltip {
  position: relative;
  display: inline-block;
}

.myTooltip .myTooltipText {
  opacity: 0;
  visibility: hidden;
  font-size: 12px;
  width: 400px;
  pointer-events: none;
  border: 1px solid;
  padding: 0 15px;
  background: #b2d3e4;
  color: #00639e;
  text-align: justify;
  border-radius: 6px;
  margin-top: -70px;
  position: fixed;
  z-index: 999;
  overflow: visible;
  min-height: 54px;
  vertical-align: middle;
  display: inline-flex;
  transition: opacity .1s ease-in-out;
  -webkit-transition: opacity .1s ease-in-out;
}

.myTooltip .myTooltipText::after {
  content: " ";
  left: 10%;
  bottom: -10px;
  overflow: visible;
  font-size: 12px;
  width: 12px;
  color: #00639e;
  z-index: 99999;
  position: absolute;
  margin-left: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: #b2d3e4 transparent transparent transparent;
}

.myTooltip:hover .myTooltipText {
  visibility: visible;
  opacity: 1;
}

mashedrooms avatar Apr 04 '20 20:04 mashedrooms

Another take on this one... On navigation start, detach the tooltip overlay. I tried to hide it via MatTooltip.hide() but for some reason the tooltip just doesn't go away.

import {Directive, Injectable, OnDestroy, OnInit} from '@angular/core';
import {MatTooltip} from '@angular/material/tooltip';
import {NavigationStart, Router} from '@angular/router';

import {filter} from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class TooltipCollector {
  collection = new Set<MatTooltip>();

  constructor(router: Router) {
    router.events.pipe(filter(e => e instanceof NavigationStart)).subscribe(() => {
      for (let mt of this.collection) {
        if (mt._tooltipInstance?.isVisible()) {
          mt._overlayRef?.detach();
          break;
        }
      }
    });
  }
}

@Directive({
  selector: '[matTooltip]',
})
export class HideTooltipDirective implements OnInit, OnDestroy {
  constructor(private mt: MatTooltip, private tc: TooltipCollector) {}

  ngOnInit() {
    this.tc.collection.add(this.mt);
  }

  ngOnDestroy() {
    this.tc.collection.delete(this.mt);
  }
}

princemaple avatar Aug 10 '21 04:08 princemaple

Here is how I had to handle it for any overlays:

window.onpopstate = function(event) {
  let overlay = document.getElementsByClassName('cdk-overlay-backdrop-showing')[0];
  overlay && overlay.click();
};

or in the AppComponent

constructor(location: PlatformLocation) {
  location.onPopState(() => {
    let overlay = document.getElementsByClassName('cdk-overlay-backdrop-showing')[0] as HTMLElement;
    overlay && overlay.click();
});

seth-broweleit avatar Mar 01 '22 15:03 seth-broweleit

@setbro-prg Your solution doesn't work for me, cdk-overlay-backdrop-showing does not exists (Angular 13.2.5).

I found a solution using Router events to empty the overlay container on each NavigationStart event:

router.events.subscribe((event) => {
  if (event instanceof NavigationStart) {
    let overlay = document.getElementsByClassName('cdk-overlay-container')[0];
    if (overlay) {
      overlay.innerHTML = '';
    }
  }
});

Elvynia avatar Mar 10 '22 19:03 Elvynia

I ran across this issue while debugging something similar related to matMenuTrigger, but my issue was calling an async function outside of the Angular zone.

What fixed it for me was running closeMenu() within NgZone.

    constructor(...ngZone: NgZone) { ... }

hideFunction() {
  setTimeout(() => { // or any async function that causes the issue
     this.ngZone.run(() => this.matMenuTrigger.closeMenu());   // make sure it runs with ngZone
  });
}

Without this, Angular was unaware that any changes had occured and wasn't removing the CDK overlay.

dereekb avatar Aug 26 '22 02:08 dereekb