Angular.js: Double compiling

Created on 17 Oct 2016  路  11Comments  路  Source: angular/angular.js

I want that any element with title attribute behaves like an uibTooltip atribute (from UI Bootstrap module). So, I created a simple directive that automatically puts this atribute and removes that other one, like this:

angular.module( 'ui.bootstrap.tooltip' )
  .directive( 'title', function( $compile ) {
    return {
      restrict: 'A',
      link: function( scope, elem, attrs ) {
        attrs.$set( 'uibTooltip', attrs.title );
        $compile( elem.removeAttr( 'title' ) )( scope );
      }
    };
  } );

It converts the attributes and then it compiles again.

The problem appears, for example, in this element (in Bootstrap design):

<div ng-controller="ButtonController as vm">
  <button type="button" class="btn btn-default" title="Add" ng-click="vm.add()"><span class="glyphicon glyphicon-plus"></span></button>
</div>

With appropriate controller:

angular.module( 'app' ) // Whatever name
  .controller( 'ButtonController', function() {
    var vm = this;
    vm.add = function() {
      console.log( 'Added!' );
    }
  } );

Since it recompiles, ngClick function executes twice on click.

Which is the way for change element's attributes before compilation?

won't fix

All 11 comments

Re-compiling an already compiled element is not supported (and can lead to all sorts of problems, including memory leaks). What you can do instead, is specify a high-priority, terminal directive, switch the attributes and then continue the compilation starting from your directive's priority.

See https://github.com/angular/angular.js/issues/15270#issuecomment-253900234 for more details on how you can do this.

It is not a solution. The troubles is only in ngClick, ngDblClick, ngMouseenter... event attributes.

It is not a solution. The troubles is only in ngClick, ngDblClick, ngMouseenter... event attributes.

Not sure what you mean 馃槙

Here you have an JSFiddle example. Update with your suggestion and share what you are exactly recommending, please.

The problem is 2 console logs on one click.

The priority on your directive override for ui.bootstrap.tooltip needs to be very high (e.g. 2000), and it needs to be a terminal directive. The reason you're getting a double compile issues is because you're running $compile again on the ENTIRE element after the framework has already compiled it for you. However, if you set priority to something very high, and set the directive to terminal, then it will ONLY compile the "decorator" directive which changes title to uib-tooltip. Then it will stop, due to the terminal flag. You can then manually compile the rest of the directives on the element via the $compile service. Just ensure you set the maxPriority argument to whatever high number you used on the directive override. That way the decorator directive will not be recompiled. This will compile all remaining elements on the directive, but NOT the decorator directive which has already been compiled by the angular framework.

For more info, see the usage section here: https://docs.angularjs.org/api/ng/service/$compile

Fixed your jsFiddle

Nice! It is running well! Take updated JSFiddle example.

Thanks!

The solution is fantastic, but there is a problem with ngRepeat directive when the affected element is a child of repeated parent. I forked the JSFiddle test: here.

@Miqueliu, I'd be glad to take a look at it, but this isn't really the place for debugging. Unless you think this is actually a bug in the framework (which I don't think it is, I think it's something about the transclusion) you should do a stack overflow.

Ok. I will. Thank you.

In Stack Overflow, it is said that the ngRepeat directives conflict against terminal property (of directives definition).

Was this page helpful?
0 / 5 - 0 ratings