Bootstrap 5 tooltip not working in Angular 11 project
Asked Answered
C

7

5

I try to use Bootstrap 5.0.2 in an Angular 11 project with this code:

index.html

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
  <meta http-equiv="Pragma" content="no-cache">
  <meta http-equiv="Expires" content="0">
  <title>My Project</title>
  <base href="/">
  <meta http-equiv="content-language" content="en-US" />

  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
  <script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</head>
<body>
  <app-root></app-root>
</body>
</html>

In a component:

<a class="btn btn-primary"
  data-bs-toggle="tooltip"
  data-bs-html="true"
  data-bs-placement="top"
  [title]="myTitle | toHtmlEntity"
  [href]="myUrl">

  {{ myButtonLabel }}
</a>

No error message, but the tooltip is not working. The title string showed up when the mouse hover the link.

Any idea what I missed?

Curd answered 23/6, 2021 at 13:39 Comment(0)
J
9

To add on @yatinsingla's answer, don't forget to add

declare var bootstrap: any

at the top of your component class, just below the imports.

So you have:

import {} from "....";
....
declare var bootstrap: any;

export class YourComponent implements OnInit, <other interfaces> {       
    ngOnInit(): void {
        // Bootstrap tooltip initialization
        var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
        var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
        return new bootstrap.Tooltip(tooltipTriggerEl)
        })       
    }
}

If you need to initizalize bootstrap 5's popovers, add the javascript initialization code (just copy it from the bootstrap's docs) in the ngOnInit() function the same was as for the tooltips.

Jeanette answered 8/10, 2021 at 10:24 Comment(1)
thanks it works for me but I got this error in angular cli terminal >>> ERROR ReferenceError: document is not defined at ExtensionComponent.ngOnInitQuadrature
U
2

Best way for this is to create a Attribute Directive.

Install Bootstrap

npm install bootstrap

Import Bootstrap SCSS in styles.scss

// For Setting Project Specific Bootstrap Variable. Like $primary Color
@import "assets/scss/variables.scss";

// Import Bootstrap
@import "../node_modules/bootstrap/scss/bootstrap.scss";

// App Specific components
@import "assets/scss/app.scss";

Import Bootstrap Bundle JS in angular.json -> architect -> build -> options

"scripts": [
  "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
]

Declare boostrap as global variable in src/typings.d.ts. Create this file if doesn't exists.

declare var bootstrap: any;

Create a directive Tooltip:

src/app/_components/tooltip.directive.ts

import { AfterViewInit, Directive, ElementRef, OnDestroy } from '@angular/core';

@Directive({
  selector: '[bTooltip]'
})
export class TooltipDirective implements AfterViewInit, OnDestroy {
  private tooltip: any;

  constructor(private elementRef: ElementRef) {}

  ngAfterViewInit() {
    const domElement: HTMLElement = this.elementRef.nativeElement;
    this.tooltip = new bootstrap.Tooltip(domElement);
  }

  ngOnDestroy(): void {
    this.tooltip.dispose();
  }
}

src/app/_components/tooltip.directive.spec.ts

import { ElementRef } from '@angular/core';
import { TooltipDirective } from './tooltip.directive';

describe('TooltipDirective', () => {
  it('should create an instance', () => {
    const elementRefMock: ElementRef = {} as ElementRef;
    const directive = new TooltipDirective(elementRefMock);
    expect(directive).toBeTruthy();
  });
});

Declare Tooltip directive in src/app/app.module.ts:

import { TooltipDirective } from './_components/tooltip.directive';

@NgModule({
  declarations: [
    ...
    TooltipDirective
  ]
})

Use the directive as below:

<a bTooltip title="Your Message"></a>

Now you will be able to use Tooltip with all it's attributes like data-bs-placement.

I think with similar approach you can use most of the Bootstrap components in Angular.

Unquestioned answered 6/6, 2023 at 15:3 Comment(0)
G
1

package.json

"@popperjs/core": "^2.10.2",    
"bootstrap": "^5.1.3",

angular.json

 "scripts": [
   "node_modules/@popperjs/core/dist/umd/popper.min.js",
   "node_modules/bootstrap/dist/js/bootstrap.min.js",
 ],

app.service.ts

declare var bootstrap: any;

private tooltipList = new Array<any>();

enableTooltip() {
  // Bootstrap tooltip initialization
  const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
  const tooltipListNewTooltips = tooltipTriggerList.map(tooltipTriggerEl => {
    return new bootstrap.Tooltip(tooltipTriggerEl);
  });
  this.tooltipList.push(...tooltipListNewTooltips);
}

hideAllTooltips() {
  this.tooltipList;
  for (const tooltip of this.tooltipList) {
    tooltip.dispose();
  }
  this.tooltipList = new Array<any>();
}
Griffis answered 8/11, 2021 at 13:21 Comment(2)
Exactly what i needed to fix the "stucked" behaviour from Bootstrap tooltip :)Lentha
Beautiful solution. Is there a optimised way to automatically invoke this service when a component is loaded and destroyed ? I am currently calling enableTooltip and hideAllTooltips from every component's ngOnInit and ngOnDestroy method.Unquestioned
E
0

try writing this code in your ts file:

var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
  return new bootstrap.Tooltip(tooltipTriggerEl)
})

source: https://getbootstrap.com/docs/5.0/components/tooltips/#example-enable-tooltips-everywhere

Ellipsis answered 23/6, 2021 at 14:1 Comment(4)
Is there any solution to run this code once to working general in every component after every rendering event?Curd
I'll have to check the best way! But 1 way could be to create a service and add the code there. And in each component, instantiate the service in constructor to enable this. If that makes sense?Ellipsis
It make sense, but still not sounds as a best practice...Curd
since you are using angular project, have you tried using ng-bootstrap? I guess then you don't need any javascript/jquery code to enable tooltip. It should work out of box.Ellipsis
K
0

You could check if your <button> or <a> tag is inside an *ngIf condition. That happened in my case. Example:

<a *ngIf="conditionVariable" [routerLink]="['/home']" class="btn btn-primary btn-sm">BUTTON</a>

or

<div *ngIf="conditionVariable">
    <a [routerLink]="['/home']" class="btn btn-primary btn-sm">BUTTON</a>
</div>

In case you have it like this, you should replace it with something like:

<a [hidden]="conditionVariable" [routerLink]="['/home']" class="btn btn-primary btn-sm">BUTTON</a>

or

<div [hidden]="ConditionVariable">
    <a [routerLink]="['/home']" class="btn btn-primary btn-sm">BUTTON</a>
</div>
Knickers answered 16/2, 2022 at 14:30 Comment(0)
T
0

People here are asking whether we need to add that method in all the classes where we implement the bootstrap tooltip. There are a couple of solutions that I think will work

  1. We could use Router Events in the app component and subscribe to that and once we are finished rendering child components then run this logic. e.g in the ngOnInit app component use the below code

    this.routerEvents.subscribe((event: Event) => { if(event instanceOf NavigationEnd) { Array.from(document.querySelectorAll('button[data-bs- toggle="tooltip"]')) .forEach(tooltipNode => new bootstrap.Tooltip(tooltipNode)) }

    })

  2. use directive and use logic there using renderer2 and ElementRef, but make sure instead of using document and Array just use renderer2 and ElementRef

example here with directive

       <button type="button" appTooltip="Tooltip Data" class="btn btn-secondary me-2">
                  Hover on me
       </button>
    
      import { AfterViewInit, Directive, ElementRef, Input, Renderer2 } from '@angular/core';
declare var bootstrap: any;


@Directive({
  selector: '[appTooltip]'
})
export class BootstrapTooltipInitDirective implements AfterViewInit {

  constructor(private el: ElementRef, private renderer: Renderer2) { }

  @Input() placement = 'top';
  @Input() appTooltip = '';

  ngAfterViewInit(): void {
    this.renderer.setAttribute(this.el.nativeElement, 'data-bs-toggle', 'tooltip');
    this.renderer.setAttribute(this.el.nativeElement, 'data-bs-placement', this.placement);
    this.renderer.setAttribute(this.el.nativeElement, 'title', this.appTooltip);
    new bootstrap.Tooltip(this.el.nativeElement);
  }

}
Tristram answered 24/1, 2023 at 7:17 Comment(0)
B
0

You can just use this in ngAfterViewInit. It works for me:

const exampleEl = document.getElementById('tool') as HTMLDivElement;
const tooltip = new bootstrap.Tooltip(exampleEl, {
    animation: true,
    trigger:"hover",
    title:"Need Help?",
})
Burier answered 22/5 at 11:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.