0

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.

3
  • I feel like this could be a use for ng-include: docs.angularjs.org/api/ng/directive/ngInclude - you would pass some url as an attribute, and then use that directly in the <ng-include> in your tooltip content Commented Jun 12, 2014 at 13:32
  • Thanks @Ian, I think I see what you mean. I'm trying this (jsfiddle.net/JZb6J/3) but can't get the tooltip include to render even though the innerHTML does seem equal to the test in the HTML. Any thoughts? Commented Jun 12, 2014 at 14:03
  • Hmm I don't think you can use angular components in plain HTML - they have to be parsed/compiled by angular. I just saw the answer that was posted - that's more what I was going for (although I didn't think of compiling a template dynamically). I was thinking of using the templateUrl for the tooltip, which would point to a template that simply had a <ng-include> (and maybe a few more things) Commented Jun 12, 2014 at 14:18

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.