Angular show a list as a tooltip in mat date picker with bootstrap?

Viewed 2104

I need to show a tooltip which is a Html list on hover.

I can show the date highlighted and a simple message but the list wont show at all. It has to do with the bootstrap. If I remove bootstrap then it shows but I need bootstrap in the app. Is there any way to show a list in a tooltip on hover with bootstrap?

Here is the code

export class DatepickerDateClassExample {
 list: string =
"Date - (3)<br/>" +
"Names - (4)<br/>" +
"Addresses - (25)<br>" +
"Values - (30)";

  constructor(private renderer: Renderer2) {}
  dates = [
       { date: "2020-04-20", text: this.list }
  ];
  dateClass = (d: Date) => {
    const dateSearch = this.dateToString(d);
    return this.dates.find(f => f.date == dateSearch)
      ? "example-custom-date-class"
      : undefined;
  };


  displayMonth() {
    let elements = document.querySelectorAll(".endDate");
    let x = elements[0].querySelectorAll(".mat-calendar-body-cell");
    x.forEach(y => {
      const dateSearch = this.dateToString(
        new Date(y.getAttribute("aria-label"))
      );
      const data = this.dates.find(f => f.date == dateSearch);
      if (data) y.setAttribute("aria-label", data.text);
    });
  }
  streamOpened(event) {
    setTimeout(() => {
      let buttons = document.querySelectorAll("mat-calendar .mat-icon-button");

      buttons.forEach(btn =>
        this.renderer.listen(btn, "click", () => {
          setTimeout(() => {
            //debugger
            this.displayMonth();
          });
        })
      );
      this.displayMonth();
    });
  }
  dateToString(date: any) {
    return (
      date.getFullYear() +
      "-" +
      ("0" + (date.getMonth() + 1)).slice(-2) +
      "-" +
      ("0" + date.getDate()).slice(-2)
    );
  }

Css:

::ng-deep .example-custom-date-class {
          background: orange;
          border-radius: 100%;
        }

  .tooltip 
    {
      display: none;
      position: absolute;
      text-align: center;
      margin-top: 30px;
      width: 155px;
      padding: 10px;
      z-index: 2000;
      box-shadow: 0px 0px 4px #222;  
      border-radius: 10px;  
      background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #eeeeee),color-stop(1, #cccccc));
      background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);  
      color: gray;   
    }


.tooltip:before {
  content: '';
  display: block;  
    position: absolute;
    left: 10px;
    top: -20px;
    width: 0;
    height: 0;
    border: 9px solid transparent;
    border-bottom-color: #cccccc;
}

Html:

<mat-form-field class="example-full-width" >
  <input matInput [matDatepicker]="picker" placeholder="Choose a date">
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker (opened)="streamOpened($event)" [dateClass]="dateClass" #picker panelClass="endDate" ></mat-datepicker>
</mat-form-field>


<div #toolTip class="tooltip" (mouseover)="onTool=true"
(mouseout)="onTool=false"
 [style.display]="show || onTool?'inline-block':'none'" [innerHTML]="toolTipMessage">
 </div>

Here is the stackblitz

2 Answers

I'm afraid that you can not use a simple css, because content only admit \A to make a break line but it's not possible using attr -it's possible indicate que \A as string in the content- (and don't allow .html)

Well, we can take another aproach that is use a div that make as tooltip and use renderer to listen mouseout and mouseover

If our div is like

<div #toolTip class="tooltip" 
   (mouseover)="onTool=true"
   (mouseout)="onTool=false"
   [style.display]="show || onTool?'inline-block':'none'" 
   [innerHTML]="toolTipMessage">
</div>

And has two variables and get the div using ViewChild

@ViewChild('toolTip') tool:ElementRef
show:boolean=false;
onTool:boolean=false;

We can change our function displayMonth to add the mouseover and mouse out

  displayMonth() {
    let elements = document.querySelectorAll(".endDate");
    let x = elements[0].querySelectorAll(".mat-calendar-body-cell");
    x.forEach(y => {
      const dateSearch = this.dateToString(
        new Date(y.getAttribute("aria-label"))
      );
      const data = this.dates.find(f => f.date == dateSearch);
      if (data) {
        this.renderer.listen(y, "mouseover", () => this.showTool(y, data.text));
        this.renderer.listen(y, "mouseout", () => this.hideTool());
      }
    });
  }
  showTool(y, data) {
    this.show = true;
    this.toolTipMessage = data;
    this.renderer.setStyle(
      this.tool.nativeElement,
      "top",
      y.getBoundingClientRect().y+ "px"
    );
    this.renderer.setStyle(
      this.tool.nativeElement,
      "left",
      y.getBoundingClientRect().x + "px"
    );
  }
  hideTool() {
    this.show = false;
  }

See the stackblitz

You just need to set the tooltip's opacity to 1. Bootstrap sets tooltip's classes opacity to 0 by default.

.tooltip 
{
 /*.... */  
 opacity: 1;
}

You could also just change your class name and not use tooltip.

Modified stackblitz

Explanation

If you use native bootstrap js, you normally need to call the .tooltip('show') method on the element for which the tooltip should appear. This will in turn change the opacity of the tooltip element.

Here, it looks like you are just using the css part of bootstrap, not the js lib, so you need to change the opacity yourself.

Note: using bootstrap's js requires jquery, which is not recommended in angular projects. There are some like libraries out there which provide bootstrap components for angular without requiring jquery, like ngx-bootstrap

Edit

Just for info, if you really wanted to use jquery and bootstrap for your tooltip, have a look at this stackblitz example.

Basically, you just call tooltip on the calendar dates and pass the text to display. Bootstrap will handle hiding/showing and positionning of the tooltip.

$(y).tooltip({'title': data.text, html: true});
Related