12

I am currently adding some bootstrap tooltips in my application.

All of the "normal" tooltips are ok, but when I want to use tooltip-html-unsafe, all I got is an empty tooltip.

My tooltip:

<a><span tooltip-placement="right" tooltip-html-safe="{{myHTMLText}}"> Help </span></a>

In the DOM, I have:

<div class="tooltip-inner" ng-bind-html-unsafe="content"></div>

The div's content seems empty, so there is nothing to show in the tooltip. I tried to put directly in the DOM some HTML text like:

<div class="tooltip-inner" ng-bind-html-unsafe="content"><b>test</b></div> and it works.

Do you have an idea?

    3 Answers 3

    23

    The html-unsafe directive is designed to point at it's content. What about this:

    <div data-ng-controller="SomeCtrl"> <span data-tooltip-html-unsafe="{{yourContent}}" data-tooltip-placement="right"> Help </span> </div> 

    Then, in SomeCtrl, make a variable to hold the html:

    $scope.yourContent = "<b>my html, yay</b> 

    IF you would like to modify bootstrap to take the content from a element, it can be done like this. First, you must change the tooltip template so that it calls a function to get the html:

    angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("template/tooltip/tooltip-html-unsafe-popup.html", "<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" + " <div class=\"tooltip-arrow\"></div>\n" + " <div class=\"tooltip-inner\" ng-bind-html-unsafe=\"getToolTipHtml()\"></div>\n" + "</div>\n" + ""); }]); 

    Then, make a link function for the tooltipHtmlUnsafePopup:

    .directive( 'tooltipHtmlUnsafePopup', function () { return { restrict: 'E', replace: true, scope: { content: '@', placement: '@', animation: '&', isOpen: '&' }, templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html', link: function(scope, element, attrs) { scope.getTooltipHtml = function() { var elemId = '#' + scope.content; var htmlContent = $rootElement.find(elemId).html(); return htmlContent; }; } }; }) 

    EDIT: Later I extracted the customized code from ui-bootstrap, which is good since you do not have to modify ui-bootstrap to use it now. Here is the extracted code, in a module called "bootstrapx". This is just for popovers (as I was not really using tooltips) but I feel like this should be easily adaptable for tooltips too.

    angular.module("bootstrapx", ["bootstrapx.tpls","bootstrapx.popover","bootstrapx.popover.dismisser"]); angular.module("bootstrapx.tpls", ["template/popover/popover-html.html","template/popover/popover-html-unsafe.html","template/popover/popover-template.html"]); angular.module( 'bootstrapx.popover', [ 'ui.bootstrap.tooltip' ] ) .directive('popover', [ function() { return { restrict: 'EA', priority: -1000, link: function(scope, element) { element.addClass('popover-link'); } }; }]) .directive('popoverHtml', [ function() { return { restrict: 'EA', priority: -1000, link: function(scope, element) { element.addClass('popover-link'); } }; }]) .directive('popoverHtmlUnsafe', [ function() { return { restrict: 'EA', priority: -1000, link: function(scope, element) { element.addClass('popover-link'); } }; }]) .directive('popoverTemplate', [ function() { return { restrict: 'EA', priority: -1000, link: function(scope, element) { element.addClass('popover-link'); } }; }]) .directive( 'popoverHtmlPopup', [ function() { return { restrict: 'EA', replace: true, scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' }, templateUrl: 'template/popover/popover-html.html' }; }]) .directive( 'popoverHtml', [ '$compile', '$timeout', '$parse', '$window', '$tooltip', function ( $compile, $timeout, $parse, $window, $tooltip ) { return $tooltip( 'popoverHtml', 'popover', 'click' ); }]) .directive( 'popoverHtmlUnsafePopup', [ '$rootElement', function ( $rootElement ) { return { restrict: 'EA', replace: true, scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' }, templateUrl: 'template/popover/popover-html-unsafe.html', link: function(scope, element) { var htmlContent = ''; scope.$watch('content', function(value) { if (!value) { return; } var elemId = '#' + value; htmlContent = $rootElement.find(elemId).html(); }); scope.getPopoverHtml = function() { return htmlContent; }; } }; }]) .directive( 'popoverHtmlUnsafe', [ '$compile', '$timeout', '$parse', '$window', '$tooltip', function ( $compile, $timeout, $parse, $window, $tooltip ) { return $tooltip( 'popoverHtmlUnsafe', 'popover', 'click' ); }]) .directive( 'popoverTemplatePopup', [ '$http', '$templateCache', '$compile', '$parse', function ( $http, $templateCache, $compile, $parse) { return { restrict: 'EA', replace: true, scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' }, templateUrl: 'template/popover/popover-template.html', link: function(scope, element, attrs) { scope.getPopoverTemplate = function() { var templateName = scope.content + '.html'; return templateName; }; } }; }]) .directive( 'popoverTemplate', [ '$compile', '$timeout', '$parse', '$window', '$tooltip', function ( $compile, $timeout, $parse, $window, $tooltip ) { return $tooltip( 'popoverTemplate', 'popover', 'click' ); }]); angular.module("template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("template/popover/popover-html.html", "<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" + " <div class=\"arrow\"></div>\n" + "\n" + " <div class=\"popover-inner\">\n" + " <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" + " <div class=\"popover-content\" ng-bind-html=\"content\"></div>\n" + " </div>\n" + "</div>\n" + ""); }]); angular.module("template/popover/popover-html-unsafe.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("template/popover/popover-html-unsafe.html", "<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" + " <div class=\"arrow\"></div>\n" + "\n" + " <div class=\"popover-inner\">\n" + " <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" + " <div class=\"popover-content\" ng-bind-html-unsafe=\"{{getPopoverHtml()}}\"></div>\n" + " </div>\n" + "</div>\n" + ""); }]); angular.module("template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("template/popover/popover-template.html", "<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" + " <div class=\"arrow\"></div>\n" + "\n" + " <div class=\"popover-inner\">\n" + " <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" + " <div class=\"popover-content\" ng-include=\"getPopoverTemplate()\"></div>\n" + " </div>\n" + "</div>\n" + ""); }]); angular.module('bootstrapx.popover.dismisser', []) .directive( 'dismissPopovers', [ '$http', '$templateCache', '$compile', '$parse', function ( $http, $templateCache, $compile, $parse) { return { restrict: 'A', link: function(scope, element, attrs) { element.bind('mouseup', function(e) { var clickedOutside = true; $('.popover-link').each(function() { if ($(this).is(e.target) || $(this).has(e.target).length) { clickedOutside = false; return false; } }); if ($('.popover').has(e.target).length) { clickedOutside = false; } if (clickedOutside) { $('.popover').prev().click(); } }); } }; }]); 

    I have the dismissPopovers directive on the body tag (this is likely applicable for tooltips too, you will just have to modify it to suit your needs):

    <body data-ng-controller="AppController" data-dismiss-popovers> 
    12
    • According to angular-ui's wiki, this is the correct syntax. http://angular-ui.github.io/bootstrap/#/tooltip I tried yours, it didn't worked.
      – Mencls
      CommentedSep 26, 2013 at 15:46
    • Oh crap! You are right, I forgot, I modified bootstrap to support that. Normally, it wants the actual content.
      – aet
      CommentedSep 26, 2013 at 16:01
    • Thanks for your answer. Where do I have to put the link function ?
      – Mencls
      CommentedSep 26, 2013 at 16:39
    • 2
      Ok I found a quicker way to fix it here. But yours was good too ;)
      – Mencls
      CommentedSep 27, 2013 at 8:17
    • 1
      @simmi simmi I have edited the answer to include the popover dismisser that I use, hopefully that will help you!
      – aet
      CommentedMay 21, 2014 at 15:12
    6

    I have created custom directive which allows html tooltips for bootsrap in very simple way. No need to override any templates:

    angular.module('vermouthApp.htmlTooltip', [ ]) .directive('vaTooltip', ['$http', '$templateCache', '$compile', '$parse', '$timeout', function ($http, $templateCache, $compile, $parse, $timeout) { //va-tooltip = path to template or pure tooltip string //tooltip-updater = scope item to watch for changes when template has to be reloaded [optional (only if template is dynamic)] //All other attributes can be added for standart boostrap tooltip behavior (ex. tooltip-placement) return { restrict: 'A', scope: true, compile: function (tElem, tAttrs) { //Add bootstrap directive if (!tElem.attr('tooltip-html-unsafe')) { tElem.attr('tooltip-html-unsafe', '{{tooltip}}'); } return function (scope, element, attrs) { scope.tooltip = attrs.vaTooltip; var tplUrl = $parse(scope.tooltip)(scope); function loadTemplate() { $http.get(tplUrl, { cache: $templateCache }).success(function (tplContent) { var container = $('<div/>'); container.html($compile(tplContent.trim())(scope)); $timeout(function () { scope.tooltip = container.html(); }); }); } //remove our direcive to avoid infinite loop element.removeAttr('va-tooltip'); //compile element to attach tooltip binding $compile(element)(scope); if (angular.isDefined(attrs.tooltipUpdater)) { scope.$watch(attrs.tooltipUpdater, function () { loadTemplate(); }); } else { loadTemplate(); } }; } }; }]); 

    Thats how you call it

     <a va-tooltip="'tooltipContent.html'" tooltip-updater="item" tooltip-placement="bottom"> <b><i>{{item.properties.length - propertyShowLimit + ' properties more...'}}</i></b> </a> 

    And template can be like this:

    <script id="tooltipContent.html" type="text/ng-template"> <span ng-repeat="prop in item.properties> <b>{{prop.name}}</b>: <span ng-repeat="val in prop.values">{{val.value}}&nbsp;</span> <br /> </span> </script> 
    0
      4

      There is now buit-in template functionality: https://angular-ui.github.io/bootstrap/#tooltip

      <a href="#" uib-tooltip-template="'myTooltipTemplate.html'">Custom template</a> <script type="text/ng-template" id="myTooltipTemplate.html"> <span>Special Tooltip with <strong>markup</strong> and {{ dynamicTooltipText }}</span> </script> 

        Start asking to get answers

        Find the answer to your question by asking.

        Ask question

        Explore related questions

        See similar questions with these tags.