7

I want to show angular UI bootsrap tooltip only when the text is truncated. I tried the below code with custom directive

<div tooltip="{{value}}" tooltip-append-to-body="true" enable-truncate-tooltip>{{value}}</div>

.directive("enableTruncateTooltip", function () {
  return {
    restrict: 'A',
    link: function (scope, elem, attr) {
      elem.bind('mouseenter', function () {
        var $this = angular.element(this);

        if (this.offsetWidth >= this.scrollWidth) {
          angular.element('.tooltip').attr('hide-tooltip', true);
        }
      });
    }
  }
})

It works fine in angular-ui-bootstrap version 0.12.1. But later versions are not supporting this.

How can i achieve this same functionality in latest version of angular-ui-bootstrap?

Thanks in advance for your help.

Sathya
  • 233
  • 1
  • 4
  • 13

2 Answers2

8

TL;DR: Plunker Demo (using $watch) Old demo (using $timeout)

(The answer was updated to reflect a suggestion to use $watch instead of $timeout, thanks for the comment Michael Mish Kisilenko!)

First of all, change your angular-ui directives to the updated ones (prefix with 'uib-'):

<div uib-tooltip="{{value}}" show-tooltip-on-text-over-flow tooltip-enable="false">{{value}}</div>

And then use the following directive, which dynamically changes the angular-ui provided feature tooltip-enable (note that you should initialize the element with directive tooltip-enable="false" so the tooltip will be disabled if the text is not truncated:

myApp.directive("showTooltipOnTextOverflow", ["$timeout", function($timeout) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var el = element[0];
      scope.$watch(function(){
        return el.scrollWidth;
      }, function() {
        var el = element[0];
        if (el.offsetWidth < el.scrollWidth) {
          //console.log('ellipsis is active for element', element);
          attrs.tooltipEnable = "true";
        } else {
          //console.log('ellipsis is NOT active for element', element);
        }
      });
      /*$timeout(function() {
        var el = element[0];
        if (el.offsetWidth < el.scrollWidth) {
          //console.log('ellipsis is active for element', element);
          attrs.tooltipEnable = "true";
        } else {
          //console.log('ellipsis is NOT active for element', element);
        }
      });*/
    }
  };
}]);

To truncate the text i'll use plain CSS:

span.truncated {
    width: 400px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
Community
  • 1
  • 1
Lulu
  • 438
  • 5
  • 15
  • 1
    A better and more flexible solution would probably to loose the "timeout" and use scope.$watch over the element's scrollWidth – Michael Kisilenko Oct 30 '16 at 10:17
  • @MichaelMishKisilenko solution is perfect, just don't forget to use it the right way with the [property](http://stackoverflow.com/questions/20403167/how-to-watch-property-in-attrs-of-directive) – aviram83 Jan 31 '17 at 12:26
  • @Sathya: We need to implement the same thing. We are using cell template and unable to display "uib-tooltip" in the first place. Is "uib-tooltip" not compatible with celltemplate or do you have any workaround for the same? – Nirmalya Jun 05 '17 at 10:52
  • In "cell" you actually mean cellphone (i.e. mobile)? – Lulu Jun 05 '17 at 10:57
  • Hi @Lulu: No, by celltemplate I meant html template (external html file) which is used to display cell contents in Angular ui-grid datatable - [link](http://ui-grid.info/docs/#/api/ui.grid.class:GridOptions.columnDef). Celltemplate is a custom template for each cell in this column. And we are trying to use the "uib-tooltip" inside the celltemplate, – Nirmalya Jun 05 '17 at 11:13
  • @NirmalyaSinha can you post a code example (jsfiddle)? – Lulu Jul 17 '17 at 06:58
  • We are using the multi-line ellipsis using the -webkit-line-clamp strategy. Will this offset width comparison work in that case? It does not work for me in the sense that the offset width and scroll width is the same even when the element shows ellipsis, so I thought I would check. Thank you. – AshD Aug 30 '18 at 22:24
3

Using watch as mentioned in answer posted by Lulu will bring performance down. It will add so many watchers as many cells grid has and these get evaluated in each digest cycle.

I modified his code to use mouseover approach - so the need of tooltip is evaluated in mouseover event only on particular cell:

myApp.directive("showTooltipOnTextOverflow", ["$timeout", function($timeout) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var el = element[0];

      if (angular.isObject(el)) {
        var evaluateTooltip = (event: JQueryEventObject, isOurEvent: boolean) => {
        // evaluate whether to enable tooltip
        attrs.tooltipEnable = el.offsetWidth < el.scrollWidth ? "true" : "false";

        if (isOurEvent !== true && attrs.tooltipEnable === "true") {
          // tooltip should be enabled, trigger mouseover again to trigger tooltip (current mouseover is already handled by tooltip with false value)
          // and mark it as our event to avoid its handling here
          element.trigger("mouseover", [true]);

          // revert tooltip enabling back to false to cover case when mouseover happens and tooltip should not be enabled
          scope.$applyAsync(() => {
          attrs.tooltipEnable = "false";
        });
      }
    };

    element.on("mouseover", evaluateTooltip);

    element.on("$destroy", () => {
      element.off("mouseover", evaluateTooltip);
    });
  }
});
michal.jakubeczy
  • 8,221
  • 1
  • 59
  • 63