Having a directive listen to a function

I have the below code:

HTML

<div id="header">
    <h1>The JSON Store</h1>
    <div class="cart-info" ng-controller="CartController" quantity="basketContents">
        My Cart (<span class="cart-items">{{basketCount()}}</span> items)
    </div>
</div>
<div id="main" ng-view></div>

JavaScript

app.controller(
    'CartController',
   function ($scope, basketService) {
       $scope.basketCount = basketService.getCount;
       $scope.basketContents = basketService.items;
   }
);




app.factory('basketService', function () {
    return {
        getCount: function () {
            var basket = JSON.parse((localStorage.getItem('shoppingBasket') || '{ "items": [] }'));
            var count = 0;
            basket.items.forEach(function (element) {
                count += element.quantity;
            });

            return count;
        },

        get items() {
            var basket = JSON.parse((localStorage.getItem('shoppingBasket') || '{ "items": [] }'));
            var quantities = basket.items.map(function (x) { return x.quantity; });
            if (quantities.length > 0) {
                var total = quantities.reduce(function (previousValue, currentValue) { return previousValue + currentValue; });
                return total;
            } else {
                return 0;
            }
        },

        addItem: function (item) {
            var basket = JSON.parse((localStorage.getItem('shoppingBasket') || '{ "items": [] }'));

            var itemStoredAlready = basket.items.filter(function (x) { return x.id === item.id; }).length === 1;

            if (itemStoredAlready) {
                var basketItem = basket.items.filter(function (x) { return x.id === item.id; })[0];
                basketItem.quantity += parseInt(item.quantity, 10);
            } else {

                var basketItem = {};
                basketItem.id = item.id;
                basketItem.title = item.title;
                basketItem.quantity = parseInt(item.quantity, 10);

                basket.items.push(basketItem);
            }

            localStorage.setItem('shoppingBasket', JSON.stringify(basket));
        }
    };
});




app.directive('quantity', function () {
    var linker = function (scope, element, attrs, basketService) {

        scope.$watch(attrs.quantity, function (value, oldValue) {
            if (value > oldValue) {
                alert("added");
            }

        }, true);
    };

    return {
        restrict: 'A',
        link: linker
    };
});

I then have other controllers that handle templates for the "main" div. In one of those controllers it calls basketService.addItem and the view is updated by basketCount() (I don't fully understand how that happens, something is triggering that I assume)

When the addItem is called I would like to do some jQuery animate or fadeIn and I know I have to use a Directive but I cannot understand how I get the directive to watch the addItem function and then to do some jQuery stuff. As you can see I have tried to expose a property in the basketService which is set in the CartController Scope and the custom HTML element but when something is added the HTML element is not getting updated and the directive function is not getting called.

Thanks for your help

Here's a Plunker - http://plnkr.co/edit/gis0114bTRRJLHCdYnMG?p=preview

The animation should be inside a directive (like you said).

And the controller should bind the directive to a property, that changes when the count is changed in the service. You accomplish that when using an isolated scope in the directive.

Here is an example, you can change the css() with your jQuery logic : http://plnkr.co/edit/Mi5QzViUgl5mS21EAKh6?p=preview

Hope it helps,

Shai

I cannot understand how I get the directive to watch the addItem function and then to do some jQuery stuff

Normally, you'll want to $watch a scope property, not a function. Here's one way to do it, that I think simplifies things:

  • Create a counter primitive on basketService. Increment this property when an item is added.
  • Create a basket property on the CartController that references the basketService
  • $watch('basket.counter') in the quantity directive
  • Because the quantity directive is not creating a new isolate scope, it does not need a value -- i.e., just <div class="..." ng-controller="..." quantity> is sufficient

Plnkr

There are other ways, like @ShaiRez suggested -- using an isolated scope. That's more complicated because you need to understand how isolate scopes work and you need to use attributes to pass information into the isolate scope, but it does make your directive more reusable.

Some other observations:

  • Instead of a getCount() function (which gets re-evaluated every digest cycle), how about an itemCount property instead. The property would be updated as part of "add" and "remove" operations.
  • Your plnkr is very large. For future questions it would be better to create a minimal example. (You will get more people to look at it, and you'll get a faster response).