srikarg
srikarg

Reputation: 331

Shopping Directive Scope Issue

In my app, I have a shop item directive which has a price attribute. When the user clicks the directive, the total cost (a variable stored in the parent controller) is updated by the price of the clicked item.

However, as of right now, I am not able to transfer the price attribute in the shop item directive to the parent controller. Any help please?

HTML:

<div ng-app="Shop" ng-controller="MainCtrl">
  <p>The total is {{ total }}.</p>
  <shop-item price="100"></shop-item>
</div>

JavaScript:

var app = angular.module('Shop', []);

app.controller('MainCtrl', ['$scope',
  function($scope) {
    $scope.total = 0;
  }
]);

app.directive('shopItem', function() {
  return {
    restrict: 'E',
    template: '<div class="shop-item">Shop Item</div>',
    scope: {
      price: '@'
    },
    link: function(scope, element, attrs) {
      element.on('click', function() {
        // How can I update the parent scope's total by the attribute 'price' of this directive?
      });
    }
  };
});

Upvotes: 0

Views: 32

Answers (1)

PSL
PSL

Reputation: 123739

There could be many ways to approach this. But a simple directive as yours you could as well use a function binding to register the click event or even use 2-way binding.

2-way binding

<div ng-app="Shop" ng-controller="MainCtrl">
  <shop-item price="100" total="total"></shop-item>
</div>

In the directive do not bind the event handler manually instead use angular events, example ng-click

app.directive('shopItem', function() {
  return {
    restrict: 'E',
    /*register an ng-click*/
    template: '<div class="shop-item" ng-click="addItem()">Shop Item</div>',
    scope: {
      price: '@',
      total: '=' //<-- 2 way binding
    },
    link: function(scope, element, attrs) {
       scope.addItem = function(){
          //increment the price
          scope.total = (scope.total || 0) + +scope.price;
       }
    }
  };
});

plnkr

Function binding

You can also use function binding approach so that you do not handle the price, instead you will just notify about the addCart event with the price, which in your case seems more desirable considering the separation of concerns. So that total calculation is just done by the controller which defines the price and not the cart. But this probably this example you have is too simple to find the difference, but if you were to do it that way then:

  <shop-item price="100" total="total" on-add-cart="addToCart(price)">  </shop-item>

In the controller:

$scope.addToCart = function(price){
   $scope.total += price;
}

In the directive (as an example i am adding a discount of 5%):

app.directive('shopItem', function() {
  return {
    restrict: 'E',
    /*register an ng-click*/
    template: '<div class="shop-item" ng-click="onAddCart({price: (+price)*.95})">Shop Item</div>',
    scope: {
      price: '@',
      onAddCart: '&' //<-- 2 way binding
    },
    link: function(scope, element, attrs) {
       scope.addItem = function(){
          //increment the price
          scope.total = (scope.total || 0) + +scope.price;
       }
    }
  };
});

Plnkr

Event Bus

Another way is to use the angular event bus. Take a look at various options like $emit/ $broadcast with $on. You may have to choose between $emit/$broadcast based on the hierarchy of the DOM elements.

It will look like:

  <shop-item price="100" total="total">  </shop-item>

In the directive:

 return {
    restrict: 'E',
    /*register an ng-click*/
    template: '<div class="shop-item" ng-click="onAddCart()">Shop Item</div>',
    scope: {
      price: '@'
    },
    link: function(scope, element, attrs) {
      scope.onAddCart = function(){
        scope.$emit("ITEM_ADDED", +scope.price); //emit an event with price
      }
    }
  };

In the controller:

$scope.$on('ITEM_ADDED', function(e, price){
   $scope.total += price;
});

Plnkr

Upvotes: 1

Related Questions