Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Resource - how to check if a resource instance has any unsaved changes?

I want to be able to tell whether an $resource instance has been modified by the user - that is, whether its current state is different than what has been initially loaded from the server && has not yet been $saved. How can I achieve that?

like image 923
Kuba Orlik Avatar asked Oct 27 '13 06:10

Kuba Orlik


2 Answers

Assuming you get a resource, and then put it on the current $scope so that it can be edited by a user:

$scope.question = Questions.get({id:"19615328"});

You can then watch it for changes like this:

// some flag, name it anything
$scope.userChange = false;
$scope.$watch('question', function(newValue, oldValue) {
    if(newValue && newValue != oldValue){
        $scope.userChange = true;
        // if you want to you can even do this, this will trigger on every change though
        $scope.question.$save();
    }
}, true);

( Pretty much everything from down here is the result of the extra questions from the chat below )

Then whenever you want to check if it has been changed $scope.userChange can tell you if a change occurred. And when you save the object, reset the $scope.userChange.

You can even do this

$scope.$watch('question', function() {
    $scope.question.$save();
}, true);

Obviously you'd want to add some sort of throttle or "debounce" system so it waits a second or so, once you have this in place any change to the object will cause a save via $scope.$watch.

And in case you want to check for null, for when you have not yet received the actual object.

$scope.$watch('question', function(newValue, oldValue) {
    // dont save if question was removed, or just loaded
    if(newValue != null && oldValue != null){
        $scope.question.$save();
    }
}, true);

You could even wrap the Questions.get call, see this questions for answers on how you can do this on the service & factory level, to do something like this.

Questions.getAndAutosave = function(options){
    var instance = Questions.get(options);
    $scope.$watch(function(){
            return instance;
        },
        function(newValue, oldValue){
            if (newValue === oldValue) return;
            if(newValue != null && oldValue != null){
                instance.$save();
            }
        }, true);
    return instance;
};

Then whenever you call Questions.getAndAutosave, whatever it returns is already being watched, and will be auto-$save'd. The reason we do if (newValue === oldValue) return; is because $watch fires as soon as you call it, and then watches for changes. We don't need to save on the first call.

like image 162
Philipp Gayret Avatar answered Nov 18 '22 16:11

Philipp Gayret


I've found a solution that both does not treat downloading data from server as user change and is implemented directly in the service itself. It might not be the most efficient solution possible, but provides exactly the functionality I want,

app.factory('testService', ['$resource', '$rootScope', function($resource, $rootScope){
var test = $resource('/api/words/:id', {id: '@id'});

test.orig_get = test.get;

test.get = function(options){
    var instance = test.orig_get(options, function(){
        instance.unsaved = false;
        $rootScope.$watch(function(){
            return instance;
        }, function(newValue, oldValue) {
           if(angular.equals(newValue, oldValue)){
                return;
           }
           var changed_indexes = [];
           for(var i in newValue){
                if(!angular.equals(newValue[i], oldValue[i])){
                    changed_indexes.push(i);
                }
           }
           if(newValue != null && oldValue != null && !(changed_indexes.length==1 && changed_indexes[0]=='unsaved')){
                console.log('detected change. setting unsaved to true');
                instance.unsaved = true;
            }

        }, true);
    });
    return instance;
}

test.prototype.orig_save = test.prototype.$save;

test.prototype.$save = function(options){
    return this.orig_save(options, function(){
        this.unsaved = false;           
    })
}

return test;
}]);
like image 45
Kuba Orlik Avatar answered Nov 18 '22 15:11

Kuba Orlik