AlexandruC
AlexandruC

Reputation: 3637

AngularJS: view not updating after deep object modification

I have this html code :

<table class="table table-striped table-condensed table-hover">
    <tbody>
        <tr ng-repeat="doc in container.Documents ">
                {{doc.Status.Message}}
            </td>
            <!-- .... -->
        </tr>
    </tbody>
</table

and this angularjs controller:

(function (ng, app) {

    "use strict";

    app.controller(
        "my.DocumentsController",
        function ( $scope, $stateParams, MYContainer, $timeout ) {

            $scope.container = new MYContainer();            
            $scope.$watch('container', function (newVal, oldVal) {
                $timeout(function () {
                    $scope.$apply();
                }, 0);
            }, true);

            $scope.container.load($stateParams.Id);
    });
})(angular, my_app);

the load function of MYContainer:

function load(id) {
    var scope = this;
    var deffered = $q.defer();
    containerService.getData(id, "true").success(function (data) {
        angular.extend(this, data);
        deffered.resolve();
    }).error(function (e) {
        deffered.reject('Error while loading container');
    });
    return deffered.promise;
}

The problem is that whenever I reload the batch (with modified container.Documents) it does not show the modified batch in the html view.

I've read about equality watches in angular so I added the watch function in the controller, and it does detect the modification in the watch function so I call $scope.$apply() but it does not change in html.

If I put 200 ms in the $timeout call instead of 0 the view gets changed.

Upvotes: 0

Views: 1002

Answers (2)

Omri Aharon
Omri Aharon

Reputation: 17064

You don't need to use any watchers or $apply here, angular is capable of taking care of this and updating the view when the data changes. Moreover, $watchers are expensive performance-wise, and $scope.$apply is something that is recommended to avoid unless absolutely necessary, which is not here.

$angular.extend is not a deep copy. You can read about it here.

I suggest you change your code to this:

load($stateParams.Id).then(function(result) {
    $scope.container = result;
});

And removing angular.extend(this, data); from the success callback.

Also remove the entire $watch section, it's redundant.

Upvotes: 3

doldt
doldt

Reputation: 4506

It's pointless to call $timeout(0) or $apply inside a $watch, as the $watch itself is called by a digest - and the point of calling $apply would be triggering a digest.

Rather, you want to force this digest when the data is changed asynchronously - in this case, when the load promise is resolved.

Upvotes: 1

Related Questions