54

I'm wondering what's the way to do work this snippet:

//html
<div ng-app="app">
    <div ng-controller="AppCtrl">
        <a my-dir ng-repeat="user in users">{{user.name}}</a>
    </div>
</div>

//js
var app = angular.module('app', []);
app.controller("AppCtrl", function ($scope) {
    $scope.users = [{name:'John',id:1},{name:'anonymous'}];
    $scope.fxn = function() {
        alert('It works');
    };

})  
app.directive("myDir", function ($compile) {
    return {
        link:function(scope,el){
            el.attr('ng-click','fxn()');
            //$compile(el)(scope); with this the script go mad 
        }
     };
});

I know it's about the compile phase but I don't get the point so a short explanation would be very appreciate.

bdkopen
  • 494
  • 1
  • 6
  • 16
Whisher
  • 31,320
  • 32
  • 120
  • 201
  • Possible duplicate of [Decorating the ng-click directive in AngularJs](http://stackoverflow.com/questions/18335574/decorating-the-ng-click-directive-in-angularjs) – Paul Sweatte Jun 22 '16 at 02:56

2 Answers2

89

A directive which adds another directive to the same element:

Similar answers:

Here is a plunker: http://plnkr.co/edit/ziU8d826WF6SwQllHHQq?p=preview

app.directive("myDir", function($compile) {
  return {
    priority:1001, // compiles first
    terminal:true, // prevent lower priority directives to compile after it
    compile: function(el) {
      el.removeAttr('my-dir'); // necessary to avoid infinite compile loop
      el.attr('ng-click', 'fxn()');
      var fn = $compile(el);
      return function(scope){
        fn(scope);
      };
    }
  };
});

Much cleaner solution - not to use ngClick at all:

A plunker: http://plnkr.co/edit/jY10enUVm31BwvLkDIAO?p=preview

app.directive("myDir", function($parse) {
  return {
    compile: function(tElm,tAttrs){
      var exp = $parse('fxn()');
      return function (scope,elm){
        elm.bind('click',function(){
          exp(scope);
        });  
      };
    }
  };
});
Community
  • 1
  • 1
Ilan Frumer
  • 32,059
  • 8
  • 70
  • 84
  • 1
    Any idea why this doesn't work when using ng-repeat and calling a function on the directive's controller? I've modified your plunker to show what I mean. Note that the method being called on click is now on the directives controller. Also note that the "This works" link which is not nested in an ng-repeat works while the others do not: http://plnkr.co/edit/Y4ADmznnDCZvuxJrcZQ0?p=preview – Jim Cooper Dec 27 '14 at 20:26
  • Removing "my-dir" messes up my CSS specification. Is there a workaround to that? Thanks – Dinesh Apr 22 '15 at 21:04
  • you could have my-dir-unloaded attr and replace it to my-dir (or to my-dir-loaded) so CSS will work while directive wont get into an infinite loop (just guessing, didn't try) – Ruben.Canton Apr 24 '15 at 16:34
  • 1
    @Dinesh `var fn = $compile(el, null, 1001);` and keep `my-dir` attribute intact. – André Werlang Apr 26 '15 at 01:14
  • It's not correct to simply call $compile() on the same element currently being compiled, it would recompile other, unrelated directives that happen to have greater priorities. – André Werlang Apr 26 '15 at 01:16
  • maybe this is a newbie question but how come you are passing "scope" as the argument for "exp"? – Ian Delairre Aug 18 '15 at 22:04
  • @IanDelairre $parse returns a interpolation function that must be called with a context. In this case, it would then call the function named `fxn` on the `scope`. https://docs.angularjs.org/api/ng/service/$parse – The DIMM Reaper Apr 25 '16 at 21:18
  • Directly using onClick skips the digest handling thats built into ngClick – Patrick Apr 26 '17 at 02:19
2

You can try this:

<div ng-app="app">
    <div ng-controller="AppCtrl">
        <a my-dir ng-repeat="user in users" ng-click="fxn()">{{user.name}}</a>
    </div>
</div>

<script>
var app = angular.module('app', []);

function AppCtrl($scope) {
        $scope.users = [{ name: 'John', id: 1 }, { name: 'anonymous' }];
        $scope.fxn = function () {
            alert('It works');
        };
    }

app.directive("myDir", function ($compile) {
    return {
        scope: {ngClick: '='}
    };
});
</script>
Kaz-LA
  • 226
  • 1
  • 5