L. Desjardins
L. Desjardins

Reputation: 1495

digest loop shows no change in my array (and view does not refresh)

I am retrieving an array of names from a service call. This service call takes in a filter parameter, which is initially empty when first executed (i.e. when I first show the table of names).

I have a filter input where the user can type characters to filter the list of names. This filter parameter gets passed to the service call and updates the list of names.

When stepping through the code, I see that my list of names has been updated and applies the new filter. I also step into the angularjs source and see that the apply \ digest loop automatically occurs right after the list of names has been updated. However, it does not refresh the view because inside the angular code, I see that the length of the array appears unchanged (even though up in my controller, the array is smaller due to the filter).

I don't understand why angular does not see the updated array?

View:

// My view contains the following input box where the user types in the string to filter with.
// The custom directive calls the action when focus is lost from the input (on blur).
<div class="input-group" data-ng-controller="myController">
    <input my-custom-directive action="doFilter()" ng-model="name.filter">
</div>

// Display the names using another custom directive: nameList
<div data-ng-controller="myController" name-list></div>

Controller:

myModule
.controller('myController', ['$scope', 'Names', function ($scope, Names) {

    $scope.name = { filter: '' };

    $scope.doFilter = function () {
        Names($scope.name.filter).query().$promise.then(function (data) {
                $scope.names = data;
            });
        }

        // Call doFilter to get the list of names when first enter the controller (filter is empty) 
        $scope.doFilter();
    }
]);

Resource:

myModule
.factory('Names', ['$resource', '$rootScope', function ($resource, $rootScope) {
    return function (name) {
        return $resource(
            'api/getnames/:Name', {
                name: name
            });
        }
}])

Finally, the list of names is displayed using the custom nameList directive:

myModule
.directive("nameList", function () {
return {
    link: function(scope, element, attrs) {

        var update = function() {
            var data = scope["names"];
        }

        var watcherFn = function(watchScope) {
            return watchScope.$eval(data, data[i]);
        }

        scope.$watchCollection('names', function (newValue, oldValue) {
            update();
        });
    },
    template:
    "<table class='table table-hover'>"
        + "<tbody>"
        + "<tr ng-repeat='n in names'>"
        + "        <td>{{n.first}}</td>"
        + "        <td>{{n.last}}</td>"
        + "    </tr>"
        + "</tbody>"
        + "</table>"
    }    
})

When myController is first entered, I see a full list of names as expected, because doFilter is called when the controller is first entered, with an empty filter.

When I enter some characters in the filter input, I see that $scope.names has been upated inside doFilter with a smaller list of names.

However the view does not update. If I step into angular source code, particularly inside the $digest function:

if (watch) {
  if ((value = watch.get(current)) !== (last = watch.last) &&
     !(watch.eq
       ? equals(value, last)
       : (typeof value === 'number' && typeof last === 'number'
       && isNaN(value) && isNaN(last)))) {

I see that the current value is always the same as the last value (when I hit my specific watch collection), and the current array is still the original large sized name array (whereas higher up in the call stack, the name array was updated to a smaller array due to the filter).

Why is angular not seeing my new sized array?

Note that I have also tried updating my $watchCollection to using just $watch (with true as the last parameter) with the same results, and I have also tried wrapping my $scopes.names = data in a $scope.$apply() again with no change.

Upvotes: 1

Views: 143

Answers (1)

Gruff Bunny
Gruff Bunny

Reputation: 27976

You've got two myControllers there. Each will have their own scope and not a common one.

The usual solution is to use a service for the common data which can be injected into the controller.

Upvotes: 2

Related Questions