Victor Sand
Victor Sand

Reputation: 2340

Getting HTML into a directive

I am an Angular noob, working on an app containing tooltips. Initially, these just contained text. Now we want these to include links, images etc (HTML markup, in other words). The idea is that an element can have two tooltips, and that the HTML one will show up after a while.

I've thrown together a quick and dirty example (FIDDLE) to illustrate the current structure. Here is the directive:

app.directive('myTooltip', ['$timeout', function($timeout) {    
    return {
        restrict: 'A',
        link: function($scope, $element, $attr) {
            var tooltip;
            var richTooltip;
            var DELAY = 1200;
            var showTooltip = function() { 
                tooltip.style.visibility = 'visible'; 
                $timeout(showRichTooltip, DELAY);
            }
            var showRichTooltip = function() { 
                richTooltip.style.visibility = 'visible'; 
            }
            var hideTooltip = function() { 
                tooltip.style.visibility = 'hidden'; 
                $timeout(hideRichTooltip, DELAY);
            }     
            var hideRichTooltip = function() { 
                richTooltip.style.visibility = 'hidden'; 
            }
            var initTooltips = function() {
                tooltip = document.createElement('div');
                tooltip.innerHTML = $attr.myTooltip;
                tooltip.className = 'tooltip';
                tooltip.style.visibility = 'hidden';
                document.body.appendChild(tooltip);       
                $element.on('mouseenter', showTooltip);
                $element.on('mouseleave', hideTooltip);

                richTooltip = document.createElement('div');
                richTooltip.innerHTML = $attr.myRichTooltip;
                richTooltip.className = 'rich-tooltip';
                richTooltip.style.visibility = 'hidden';
                document.body.appendChild(richTooltip);       

            } 
            initTooltips();
        }
    };
}]);

The markup is used kind of like this:

<button my-tooltip="Text only tooltip" my-rich-tooltip="This is a tooltip with <a href='#'>HTML</a>!">Button</button>

Now this works as an example, but it does not look very nice. My question is therefore how one should do this in a nicer way, not having to pass the tooltip HTML as an attribute! It would be nice to, for example, pass an URL containing the HTML instead.

Upvotes: 0

Views: 108

Answers (1)

Konstantin Krass
Konstantin Krass

Reputation: 8646

First of all, you can use all jqLite features like angular.element($element).css(...) and chaining (https://docs.angularjs.org/api/ng/function/angular.element)

eg:

angular.element(tooltip).css("display", "none").addClass("tooltip");

To use URL instead of directly passing this stuff, you can make use of $templateCache (https://docs.angularjs.org/api/ng/service/$templateCache), $http to load the template if necessary and use $compile to apply your scope to the loaded html.

Small example:

app.directive('myTooltip', function($templateCache, $compile, $http) {    
    return {
        restrict: 'A',
        scope: {
          myRichTooltip: "@"   //template URL as string
        }
        link: function($scope, $element, $attr) {
           //Because the template cache will load the html via xhr, this will be async -> promise
           $http.get($scope.myRichTooltip, {cache: $templateCache}).then(function(toolTipString){                  
              var template = angular.element(toolTipString);
              //By doing this, you can even use scope expressions in your external toolTipString
              $compile(template.contents())(scope);

              // [...] Now you got the compiled html element
              // use it as you like.
              //angular.element("body").append(template);
              //angular.element(template).css("top", "100px").css("left", "100px);
           });
        }
    }
});

Always isolate your directive scope, if possible due to performance. (otherwise it will inherit the complete parent scope, this may hurt)


If you will not use the $compile this for example won't work in the external html file:

<span>{{1+1}}</span>

But with $compile it will.

Upvotes: 1

Related Questions