core
core

Reputation: 33079

Passing function to directive that directive should execute

I'm confused about using '&' with the isolate scope's rowClick. Take a look at the code below. In a code review I was told that this, below, is an anti-pattern and that '&' should be used instead, but I didn't get an example of how to do that.

<div ng-controller="tableController as table">
    <row ng-repeat="row in table.rows" row-data="row" row-click="table.updateChart">
    </row>
</div>

.controller('tableController', [function() {
    this.rows = [
        { id: 'foobar', values: ['Chris', 'Kayti'] }
    ];

    this.updateChart = function(row) {
        alert('TODO: update row ' + row.id);  
    };
}])
.directive('row', [function() {
    return {
        restrict: 'E',
        scope: {
            rowData: '=',
            rowClick: '='
        },
        template: '<div class="row" ng-click="rowClick(rowData)">' +
                    '<span ng-repeat="cell in rowData.values">{{ cell }}</span>' +
                  '</div>'
    };
}])

(See http://plnkr.co/edit/ZofcUQcPKQp4zeSNvoOe?p=preview)

I'm reading about it but still confused. Can someone give me an example of how this code could be refactored to use '&', please?

Upvotes: 0

Views: 254

Answers (2)

PSL
PSL

Reputation: 123739

It is a weird syntax and probably not well documented on how to pass the arguments. You would need to make some changes:-

In the template set & binding and pass the argument as value of the key with a name, say row i.e ng-click="rowClick({row: rowData})"> :

return {
        restrict: 'E',
        scope: {
            rowData: '=',
            rowClick: '&'
        },
        template: '<div class="row" ng-click="rowClick({row: rowData})">' +
                    '<span ng-repeat="cell in rowData.values">{{ cell }}</span>' +
                  '</div>'
    };

When consuming the directive set up the name of the argument which should be inline with what your directive defines internally, i.e say row :

row-click="table.updateChart(row)"

Demo

angular.module('app', []).controller('tableController', [
    function() {
      this.rows = [{
        id: 'foobar',
        values: ['Chris', 'Kayti']
      }];

      this.updateChart = function(row) {
        alert('TODO: update row ' + row.id);
      };
    }
  ])
  .directive('row', [
    function() {
      return {
        restrict: 'E',
        scope: {
          rowData: '=',
          rowClick: '&'
        },
        template: '<div class="row" ng-click="rowClick({row:rowData})">' +
          '<span ng-repeat="cell in rowData.values">{{ cell }}</span>' +
          '</div>'
      };
    }
  ])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="tableController as table">
  <row ng-repeat="row in table.rows" row-data="row" row-click="table.updateChart(row)">
  </row>
</div>

Upvotes: 1

Nikos Paraskevopoulos
Nikos Paraskevopoulos

Reputation: 40298

From the caller of the directive, pass the row-click attribute as function call:

<row ng-repeat="row in table.rows" row-data="row" row-click="table.updateChart(row)"></row>

Of course change the scope definition:

    scope: {
        rowData: '=',
        rowClick: '&'
    },

And the tough part: when calling the & function, give it an object where each "formal" argument from the calling site is a property. I cannot express it better, sorry :) For this case:

template: '<div class="row" ng-click="rowClick({row:rowData})">' +
        '<span ng-repeat="cell in rowData.values">{{ cell }}</span>' +
    '</div>'

If, for instance, the call was: row-click="table.updateChart(x,y,z)" and you actually wanted to do: row-click="table.updateChart(1,2,3)", you should call the scope & function as: scope.rowClick({x:1, y:2, z:3}).

And a forked plunk: http://plnkr.co/edit/9xVWXJmMLP8A2OgGTv7R?p=preview

Upvotes: 1

Related Questions