Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angularjs: restoring model value in $watch after failed update on server side

Here's the scenario:

fiddle:

$scope.$watch('obj.value',function(val, oldVal) {
    if (val === oldVal) return;
    MyService.update($scope.obj, $scope.result).then(function(response){
        $scope.results.push(response);
    }, function(response) {
        $scope.obj.value = oldVal;
         $scope.results.push(response);
    });
});

I set a watch on a value and update it to db whenever it changes. But if the update fails for some reason (connection problem, server error, invalid session, insufficient permissions, .etc), I'd like to restore that value to the previous version. In the fiddle you can see what happens if you select "reject deferred" and try to change the value - it starts infinte loop of failed requests, restored values and $watch triggers.

For the time being I'm setting a flag on the scope to indicate that request has failed and next $watch should not call the service. But I'm looking for ways to reduce this boilerplate code.

Of course, I could always use some other ways to inform the scope, for example ng-change, but then I lose reference to the old value. I could keep the reference in my scope, but that is even worse than current situation.

Have you any ideas how these situations should be handled? Basically what I'm looking for is a way to update the model in the $watch without triggering further $watches, if that's even possible.

like image 577
package Avatar asked Dec 01 '25 19:12

package


2 Answers

Use ng-change directive instead of the watcher, and use an internal state variable to store the value oft the last successful save.

See it in action: http://jsfiddle.net/Zmetser/vscGP/6/

function MyCtrl($scope, MyService) {
    var lastSaved;
    $scope.obj = {value: "foo"};
    $scope.results = [];
    $scope.result = "1";

    lastSaved = $scope.obj.value;

    $scope.sentinel = function ( value ) {
        MyService.update($scope.obj, $scope.result).then(function(response){
            lastSaved = angular.copy($scope.obj.value);
            $scope.results.push(response);
        }, function(response) {
            if ( lastSaved )
                $scope.obj.value = lastSaved;
            $scope.results.push(response);
        });
    };
}

<input type="text" ng-model="obj.value" ng-change="sentinel(obj.value)"/>
like image 178
Oliver Avatar answered Dec 03 '25 19:12

Oliver


Rather than using a timeout or $timeout, you could also use a closure to encapsulate a boolean flag that you reset.

(function WatchWithRevert () {
    var justReverted = false;
    $scope.$watch('obj.value',function(val, oldVal) {
        //if (val === oldVal) return;
        if (justReverted) {
            justReverted = false;
            return;
        }
        MyService.update($scope.obj, $scope.result).then(function(response){
            $scope.results.push(response);
        }, function(response) {
            $scope.obj.value = oldVal;
            justReverted = true;
            $scope.results.push(response);
        });
    });
})();
like image 26
Brandon M. Avatar answered Dec 03 '25 19:12

Brandon M.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!