4

I have a web app that combines Angularjs and d3js. One of my directives called dailyView sets up a tooltip using a function drawtooltip() defined in a service called cfg. The directive code is similar to the following:

app.directive('dailyView', ['cfg', function (cfg) {

  return {
    restrict: 'E',
    link: function(scope, element, attrs) {
      scope.$watch('daily', function(newVal, oldVal) {
        if (!newVal) return;
        cfg.drawDaily(scope.daily[attrs.bus], element, attrs.bus);
        $('#sortable2').sortable({
            start: scope.dragStart,
            update: scope.dragEnd
        });
        cfg.drawTooltip();
      });
    }
  };
}]);

On the other hand, the drawTooltip() function is defined like:

app.factory('cfg', ['$window', '$rootScope', '$cookieStore', function($window, $rootScope, $cookieStore){
 function drawTooltip(){

  var tooltip = d3.select(".tooltip");

  d3.selectAll(".myImage")
    .on("mousemove", function(d){
      tooltip.transition().duration(100)
        .style("opacity", .9);
      tooltip.html('<p>{{"' + d.measure + '"|myLocationFilter}}</p>')
        .style("left", (d3.event.pageX + 20)  + "px")
        .style("top", (d3.event.pageY - 110) + "px");    
    })
    .on("mouseout", function(d) {
      tooltip.transition().duration(200)
        .style("opacity", 0);
    });
}

My angular filter should transform the d.measure string into a descriptive text that changes based on the browser language. The problem is that my the filter is not recognized and my tooltip simply shows the following text (e.g. when the measure data bound to the element is the string "plug": {{"plug"|myLocationFilter}}.

How can I inject my angular filter into the d3js html element?

Note: This is a fairly similar question that has not been answered yet.

Edit 1: I've tried using $compile()(scope) in the directive, right after calling cfg.drawtooltip() but the angular filter did not work.

Edit 2: After testing the multiple suggestions offered in the comments, it is clear that the problem lies in the use of the html() method of a d3 selection. Would it be possible to somehow wait until $compile() is processed and then use the outerHTML value of the resulting object?

Community
  • 1
  • 1
JAC
  • 591
  • 2
  • 6
  • 19
  • Why not, instead of trying to make the text interpolated, evaluate it inside the d3 code? – Jeff Hubbard Dec 09 '13 at 23:20
  • http://docs.angularjs.org/api/ng.$compile – Adam Pearce Dec 09 '13 at 23:28
  • Hi Jeff, thanks for the suggestion. That would work, but I just want to know if it is possible to do what I have in mind. – JAC Dec 09 '13 at 23:31
  • you need to compile the tooltip html using `$compile` – charlietfl Dec 10 '13 at 01:33
  • If you are using `angular` to add the HTML to the DOM (via a directive), then this answer should help: http://stackoverflow.com/questions/19656365/angular-ui-d3-how-to-implement-contextual-menu-popover-vs-modal/19663183#19663183 Post some more code about how this DOM element is getting added. – musically_ut Dec 10 '13 at 06:27
  • The html is added via a directive. This directive calls a service that sets up the tooltip. I edited the question to reflect this and added some code. I tried using $compile()(scope) on the directive but it failed. My guess is that I need to use $compile upon the mouseover event, but I can't figure out how to do it. – JAC Dec 12 '13 at 21:40
  • You need to call the compile service from within your directive code so you need to pass the $scope to `drawTooltip` – Pieter Herroelen Dec 16 '13 at 15:13
  • Pieter, if I pass the scope to `drawTooltip` I would be calling `$compile` from within my service instead of my directive. I've tried doing so and it did not work. I've also tried calling `$compile(element.contents())(scope)` similarly as in [this reference](http://stackoverflow.com/questions/19656365/angular-ui-d3-how-to-implement-contextual-menu-popover-vs-modal/19663183#19663183) but it does not seem to work either. – JAC Dec 18 '13 at 23:04

1 Answers1

0

You need to $compile() the value before you output it.

This line:

 tooltip.html('<p>{{"' + d.measure + '"|myLocationFilter}}</p>')

Should look like this:

 var compiledElement = $compile('<p>{{"' + d.measure + '"|myLocationFilter}}</p>')($rootScope);
 tooltip.html(compiledElement);

This should just work. I've made a small fiddle here. You need to inject and use $rootScope because you call it inside of a factory, which has no own scope. And of course $compile, which does the magic for you.

Generally it seems to be a good idea to use directives also for the tooltip.

Armin
  • 15,582
  • 10
  • 47
  • 64
  • Thanks for the answer. I had tried the same approach and it did not work in my project, but I couldn't understand why. Your answer shows where the issue lies. You use the `html()` method of a jquery selection. I use the `html()` method of a d3 selection. In that case, the `html()` expects a string and not an object. I tried using the outerHTML field of the object, but it looks like that field is not compiled on time and I just get a text saying: {{'this becomes uppercase' | uppercase}}. Any suggestions on how to overcome this? – JAC Dec 23 '13 at 22:19
  • [This link](http://jsfiddle.net/juanargote/yTFVa/) contains your modified fiddle with my attempts, for reference. – JAC Dec 23 '13 at 22:37
  • I ended up using `$q` to make sure that I used the "compiled" html, and in addition I had to access the outerHTML field of the returned object, like this: `compiledElement[0].outerHTML`, but Armin's answer pointed to the root of the issue, so answer accepted. – JAC Jan 07 '14 at 19:00
  • @JAC Did you ever get this working? The [modified fiddle](http://jsfiddle.net/juanargote/yTFVa/) you have is pretty much what I'm trying to do where the bind value in {{value}} is not getting evaluated within the D3 selection. – Chris Casad Jul 17 '15 at 17:55