jmls jmls - 29 days ago 12
AngularJS Question

Angular '&' Binding and parameters

I am trying to write a component called "foo" - this component takes a label, and says "hello {{$ctrl.label}}". It also invokes a callback function on click

<foo label="'bar'" callback="$ctrl.myCallback()"</foo>


All well and good so far. My controller does indeed get the callback

So now I put this element into a ng-repeat:

<foo ng-repeat="item in $ctrl.items" label="item.label" callback="$ctrl.myCallback(item)></foo>


How do I define in the component what to pass back into the controller on a click ? The component does not have
item
passed to it, only the "label".

From what I've read, I need to say something like

this.callback({item: SomeObject});


I have 2 questions here: 1) how does the component know that it needs to supply the "item" key and 2) how does the component know what SomeObject is ?

I could easily reuse the component in another ng-repeat:

<foo ng-repeat="order in $ctrl.orders" label="order.orderNum" callback="$ctrl.myCallback(order)></foo>


and in this case, how does the component know to send the order object as a parameter to the click callback function ?

Answer

The component doesn't know nothing about it, you know, your unit test know and your e2e tests knows (may be), but the component only know that it has a callback and a property and their way-binding, that's what needed to do the bindings. Also, you don't have to pass anything from the component side, unless you are sending the item object from the component, otherwise, just use a simple $ctr.callback() that the bind will call the $parent.myControllerFunction(item) from the ng-repeat scope.

When using & you create a delegate method to be called from the component according to the mask that you'va created on the template. For example:

<ANY ng-repeat="item in items" callback="myCallBack(item)">
...
bindings: {
    callback: '&'
}
...

If from the component you call it like $ctrl.callback() it will use the item from ng-repeat scope. But if you want to send it from the component, then you have to pass an object with the name of the argument your were using on the template like $ctrl.callback({ item: { name: 'foo'}}) to fill the parameter declared on callback="myCallBack(item)".

angular
  .module('mBoard', [])
  .component('widget', {
    bindings: {
      item: '=',
      callback: '&'
    },
    controller: function() {

      var $ctrl = this;

      $ctrl.save = function() {
        //$ctrl.callback();
        $ctrl.callback({ item: { name : $ctrl.item.name + '%%%%' }});

      }
    },
    templateUrl: 'template.html'
  })
  .controller('mBoardCtrl', function mBoardCtrl($interval, $scope) {
    $scope.items = [{name: 'uno'}, {name: 'dos'}, {name: 'tres'}];
    $scope.setItem = function(item) {

      console.log(item)
    }
  });

/* https://docs.angularjs.org/guide/bootstrap */
angular.bootstrap(document, ['mBoard']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.js"></script>
<div ng-controller="mBoardCtrl ">
  <widget ng-repeat="item in items" item="item" callback="setItem(item)"></widget>
  <hr>
  <widget ng-repeat="item in items" item="item" callback="setItem('quatorze')"></widget>


  <!-- https://docs.angularjs.org/api/ng/service/$templateCache -->
  <script type="text/ng-template" id="template.html">
    <button type="button" ng-click="$ctrl.save()">{{ $ctrl.item.name }}</button>
  </script>
</div>