0

I am attempting to write a directive that will sort arbitrary data at an arbitrary place in my application. Let us say I have the following code (based on actual real code, sort function and some of the complexity for simplicity)

angular.module('test', []);
angular.module('test').controller('wrapperController',['$scope', function(scope){
  scope.data = {}
  scope.data.rows = [{name: 'foo'}, {name: 'bar'}, {name: 'bazz'}]
  scope.data.columns = [{name: 'Name', sortBy: 'name'}]
}]);
angular.module('test').directive('grid', [function(){
  return {
    restrict: 'A',
    templateUrl: 'grid.html',
    scope: {
      grid: '='
    }
  }
}]);
angular.module('test').directive('sortable', [function(){
  return {
    restrict: 'A',
    scope: {
      sortableCollection: '=',
      sortableKey: '&'
    },
    compile: function(el, at, transclude){
      if(at['ng-click']){
        el.attr('ng-click', el.attr('ng-click')+';sortFunction()')
      }else{
        el.attr('ng-click', 'sortFunction();')
      }
      return(function(scope, element, attrs){
        scope.sortableKey = scope.sortableKey();
        scope.sortFunction = function(){
          alert(" I AM IN UR FUCNTION SORTING UR D00dZ!!1");
        }
      });
    }
  }
}]);

And the following html:

<body ng-app='test'>
    <div ng-controller='wrapperController'>
      <div grid='data'></grid>
    </div>
  </body>

and (in grid.html):

    <div>
  <table>
    <thead>
      <tr>
        <td ng-repeat='column in grid.columns'>
            <div sortable sortable-collection='grid' sortable-key='column.sortBy'>{{column.name}}</div>
        </td>
      </tr>
    </thead>
    <tbody>
      <tr ng-repeat='row in grid.rows'>
        <td ng-repeat='cell in grid.columns'>
          {{row.name}}
        </td>
      </tr>
    </tbody>
  </table>
</div>

Inspecting the HTML shows that the ng-click is correctly populating, however when the heading is clicked the function never fires. Why is that? Is there a way to get this kind of behaviour?

plunr: http://plnkr.co/edit/aXjMqhZxI7ME8wQJOLpA?p=preview

Abraham P
  • 15,029
  • 13
  • 58
  • 126

2 Answers2

3

Simply adding an attribute doesn't make angular to run that directive. When angular starts the application it scans the DOM for directives. Then it calls their compile functions. So when you add ng-click within that function it's too late.

Adding ng-click within a directive doesn't make sense anyway. You want to add an event listener for the click event.

link: (function(scope, element, attrs){
    scope.sortableKey = scope.sortableKey();
    element.on('click',  function(){
      alert(" I AM IN UR FUCNTION SORTING UR D00dZ!!1");
      // call $scope.apply(); after you have changed the model
    });
a better oliver
  • 26,330
  • 2
  • 58
  • 66
1

Your grid directive can create a control object on its scope,

link: function(scope, element, attrs) {
  scope.sorter = {};
}

and pass it to the sortable directive:

<div sortable sort-control='sorter'...></div>

The sortable directive will then add the sorting function to the control object:

scope: {
  ....
  sortControl: '='
},
link: function(scope, element, attr) {
  if (scope.sortControl) {
    scope.sortControl = {
      sortFunction: function() {
        alert(" I AM IN UR FUCNTION SORTING UR D00dZ!!1");
      }
    };
  } 
}

Finally, back in the grid template, we can access the sorting function as a property of our control object:

<div sortable ng-click='sorter.sortFunction()' sort-control='sorter'...></div>

Here is a working demo: http://plnkr.co/edit/iYkVbh2wGfCWBWA0SX3M?p=preview

Community
  • 1
  • 1
j.wittwer
  • 9,497
  • 3
  • 30
  • 32