2

I've created a directive in Angular that looks like this:

angular.module('msfApp')
    .directive('listitem', function () {
        return {
            templateUrl: 'assets/templates/directives/listitem.html',
            restrict: 'E',
            scope: {
                'item': '='
            }
        }
    });

And the template looks like so:

<div class="tsProductAttribute" ng-click="toggleInBasket(item)">
    <span class="tsProductAttribute-image">
        <img ng-src="{{item.variants[0].image}}">
    </span>
    <span class="tsProductAttribute-desc">{{item.productName}}</span>
    <span class="tsProductAttribute-price">{{item.variants[0].price[0].amount}} {{item.variants[0].price[0].entity}}</span>
</div>

But now I have two questions:

  1. The ng-click function doesn't fire in my controller, toggleInBasket(item), why is that?
  2. And secondly, how do I add a toggle behaviour to the list item so that it toggles a class called "tsProductAttribute--selected"

Thanks in advance guys!

Joel
  • 3,166
  • 5
  • 24
  • 29

3 Answers3

12

1) Problem is the isolated scope. You cannot see the function in the controller scope. One solution is to pass the function reference to the directive:

http://plnkr.co/edit/GorcZZppa8qcIKbQAg2v?p=preview

<body ng-controller="ItemController">
  <listitem item="item" item-click="toggleInBasket(item)"></listitem>
</body>

in the directive:

scope: {
    'item': '=',
    'itemClick': '&'
}

and in the template:

<div class="tsProductAttribute" ng-click="itemClick(item)">

2) Create another function in the directive to toggle selected state and call the controller function:

angular.module('msfApp').directive('listitem', function () {
  return {
    templateUrl: 'listitem.html',
    restrict: 'E',
    scope: {
      'item': '=',
      'itemClick': '&'
    },
    link: function(scope, iElement, iAttrs) {
      scope.selected = false;
      scope.toggleState = function(item) {
        scope.selected = !scope.selected;
        scope.itemClick(item);
      }
    }
  }
});

and toggle the class in the template:

<div class="tsProductAttribute" 
    ng-class="{'tsProductAttribute--selected': selected}" 
    ng-click="toggleState(item)">
Macros
  • 415
  • 2
  • 8
  • 1
    Normally `&` (one-way expression) is used rather than `=` (two-way variable binding) when a directive wants to call a function defined in the parent scope. Is there a reason you used `=` instead? (I think it is semantically better to use '&'.) – Mark Rajcok Jul 10 '13 at 14:00
  • You are right Mark. I have used '&' in the Plunker, but I forgot to change it in the answer. Thanks! – Macros Jul 11 '13 at 12:46
  • @Macros your answer helped me a lot. How would I got about passing data from my directive to the controller to be used in the click event? Currently the parameter is undefined when it makes it to the controller. – Mcestone Mar 24 '16 at 18:13
  • FYI the answer to my question above is here: http://stackoverflow.com/questions/36209664/passing-data-from-ng-click-within-directive-into-a-function-in-the-controller/36209713#36209713 – Mcestone Mar 25 '16 at 13:04
1

This is happening because you are using isolated scopes in the directive using scope: { 'item': '=' } which creates a new scope so your ng-click is not able to bind to controller function.

Kindly refer to below link to call parent function using ng-click

calling method of parent controller from a directive in AngularJS

Community
  • 1
  • 1
Ajay Beniwal
  • 18,857
  • 9
  • 81
  • 99
1

@Macros answer made it work just fine for me! Here's my finished code:

Directive template file:

<div    class="tsProductAttribute" 
        ng-class="{'tsProductAttribute--selected': selected}" 
        ng-click="toggleState(item)">

    <span class="tsProductAttribute-image">
        <img ng-src="{{variantImage}}">
    </span>
    <span class="tsProductAttribute-desc">{{item.productName}}</span>
    <select ng-model="variantImage">
        <option  ng-repeat="variant in item.variants" value="{{variant.image}}">{{variant.name}} - {{variant.listprice.amount}}</option>
    </select>
    <span class="tsProductAttribute-price">{{item.variants[0].listprice.amount}} {{item.variants[0].listprice.entity}}</span>
</div>

Directive:

angular.module('msfApp')
.directive('listitem', function () {
    return {
        templateUrl: 'assets/templates/directives/listitem.html',
        restrict: 'E',
        scope: {
            'item': '=',
            'itemClick': '='
        },
        link: function(scope, iElement, iAttrs) {
          scope.selected = false;
          scope.toggleState = function(item) {
            scope.selected = !scope.selected;
            scope.itemClick(item);
          }
        }
    }
});

Directive implementation:

<listitem item="item" item-click="toggleInBasket"></listiten>

Function in the Controller:

$scope.toggleInBasket = function(item) {
        $scope.basket.toggle(item);

        console.log(basket.get());

    }
Joel
  • 3,166
  • 5
  • 24
  • 29