Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$watchCollection is not triggered

i have the following directive:

    app.directive('rickshawChart', function()
{
    return{
        $scope: {
            data: '=',
            renderer: '='
        },
        template: '<div></div>',
        restrict: 'E',
        link: function postLink(scope, element, attrs)
        {
           scope.$watchCollection('[data,renderer]', function(newVal, oldVal)
           {
                if(!newVal[0])
                {
                    return;
                }
               var graphEl = element.find("div:first");
               graphEl.html('');
               var graph = new Rickshaw.Graph({
                   element: graphEl[0],
                   width: attrs.width,
                   height: attrs.height,
                   series: [{data: scope.data, color: attrs.color, name: attrs.series}],
                   renderer: scope.renderer
               });
               graph.render();
           });
        }

    }
});

With the following html:

<rickshaw-chart
        data="sightingByDate"
        color="green"
        renderer="renderer"
        width="100%"
        height="100%">
</rickshaw-chart>

Here i fill the data in with the following controller:

    app.controller('CompetenceController', ['$http', '$scope','$sessionStorage','$log','Session','api',  function ($http, $scope, $sessionStorage,$log, Session, api) {

    $scope.result =[ { x: 0, y: 40 }, { x: 1, y: 49 }, { x: 2, y: 38 }, { x: 3, y: 30 }, { x: 4, y: 32 } ];
    $scope.renderer = 'scatterplot';
    $scope.sightingsByDate = _($scope.result)
        .chain()
        .countBy(function(sighting){
            return sighting.y;
        })
        .pairs()
        .map(function(pair){
            return pair;
        })
        .value();
}]);

however from the debugger i am able to see that the $watchCollection is never executed.

What am i doing wrong?

like image 838
Marc Rasmussen Avatar asked Mar 17 '23 13:03

Marc Rasmussen


1 Answers

You should use the $watch expression with true as a third parameter:

scope.$watch('[data,renderer]', function (newVal, oldVal) {
         //code
 }, true);

$watchCollection doesn't work for you because you have both an array and a property. In this case is like you're checking contemporarily $scope.$watch('data' and $scope.$watch('renderer'. And if a property in the array changes, the event will not trigger.

There are four types of $watch:

  1. the simpliest is $watch, it simply check if the property or the whole object changed
  2. the second is $watchCollection which watches if any property within an array changed (in your case this doesn't work, because you have both an array and a property)
  3. the third is $watchGroup (since 1.3) which is the most recent version of $watchCollection, which allows you to check an array of expressions (but the same could be achieved with $watchCollection as you were trying to do
  4. $watch('object', function(){...}, true). This check if every single property changed within an object

This is a simplified version of your code, if you press the change value button, you can see how an element in the array is changed and how the event is triggered in the console: http://jsfiddle.net/27gc71bL/

EDIT: I forgot to add something in my answer. I think there's a typo in your code, you are creating a directive using:

        $scope: {
            data: '=',
            renderer: '='
        },

This is not creating a new scope within the directive. So when you are using scope.data in the link function, you are referring to the $scope.data of the parent (the controller).

What I think you wanted, is to create a new scope in this way:

        scope: {
            data: '=',
            renderer: '='
        },

and, in your controller, bind the array you want to check to sightingByDate.

like image 184
Michael Avatar answered Mar 19 '23 01:03

Michael