Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angularjs autosave form is it the right way?

My goal is to autosave a form after is valid and update it with timeout. I set up like:

(function(window, angular, undefined) {
    'use strict';
    angular.module('nodblog.api.article', ['restangular'])
        .config(function (RestangularProvider) {
            RestangularProvider.setBaseUrl('/api');
            RestangularProvider.setRestangularFields({
                id: "_id"
            });
            RestangularProvider.setRequestInterceptor(function(elem, operation, what) {
                if (operation === 'put') {
                    elem._id = undefined;
                    return elem;
                }
                return elem;
            }); 
        })
        .provider('Article', function() {
            this.$get = function(Restangular) {
                function ngArticle() {};
                ngArticle.prototype.articles = Restangular.all('articles');
                ngArticle.prototype.one = function(id) {
                    return Restangular.one('articles', id).get();
                };
                ngArticle.prototype.all = function() {
                    return this.articles.getList();
                };
                ngArticle.prototype.store = function(data) {
                    return this.articles.post(data);
                };
                ngArticle.prototype.copy = function(original) {
                    return  Restangular.copy(original);
                };
                return new ngArticle;
            }
    })
})(window, angular);

angular.module('nodblog',['nodblog.route'])
.directive("autosaveForm", function($timeout,Article) {
    return {
        restrict: "A",
        link: function (scope, element, attrs) {
            var id = null;
            scope.$watch('form.$valid', function(validity) {
                if(validity){
                    Article.store(scope.article).then(
                        function(data) {
                            scope.article = Article.copy(data);
                            _autosave();
                        }, 
                        function error(reason) {
                            throw new Error(reason);
                        }
                    );
                }  
            })
            function _autosave(){
                    scope.article.put().then(
                    function() {
                        $timeout(_autosave, 5000); 
                    },
                    function error(reason) {
                        throw new Error(reason);
                    }
                );
            }
        }
    }
})

.controller('CreateCtrl', function ($scope,$location,Article) {
        $scope.article = {};
        $scope.save = function(){
            if(typeof $scope.article.put === 'function'){
                $scope.article.put().then(function() {
                    return $location.path('/blog');
                });
            }
            else{
                Article.store($scope.article).then(
                    function(data) {
                        return $location.path('/blog');
                    }, 
                    function error(reason) {
                        throw new Error(reason);
                    }
                );
            }
        };
     })

I'm wondering if there is a best way.

like image 556
Whisher Avatar asked Jan 15 '14 10:01

Whisher


1 Answers

Looking at the code I can see is that the $watch will not be re-fired if current input is valid and the user changes anything that is valid too. This is because watch functions are only executed if the value has changed. You should also check the dirty state of the form and reset it when the form data has been persisted otherwise you'll get an endless persist loop.

And your not clearing any previous timeouts.

And the current code will save invalid data if a current timeout is in progress.

I've plunked a directive which does this all and has better SOC so it can be reused. Just provide it a callback expression and you're good to go.

See it in action in this plunker.

Demo Controller

myApp.controller('MyController', function($scope) {

  $scope.form = {
    state: {},
    data: {}
  };

  $scope.saveForm = function() {
    console.log('Saving form data ...', $scope.form.data);  
  };

});

Demo Html

  <div ng-controller="MyController">

    <form name="form.state" auto-save-form="saveForm()">

      <div>
        <label>Numbers only</label>
        <input name="text" 
               ng-model="form.data.text" 
               ng-pattern="/^\d+$/"/>
      </div>

      <span ng-if="form.state.$dirty && form.state.$valid">Updating ...</span>      

    </form>
  </div>

Directive

myApp.directive('autoSaveForm', function($timeout) {

  return {
    require: ['^form'],
    link: function($scope, $element, $attrs, $ctrls) {

      var $formCtrl = $ctrls[0];
      var savePromise = null;
      var expression = $attrs.autoSaveForm || 'true';

      $scope.$watch(function() {

        if($formCtrl.$valid && $formCtrl.$dirty) {

          if(savePromise) {
            $timeout.cancel(savePromise);
          }

          savePromise = $timeout(function() {

            savePromise = null;

            // Still valid?

            if($formCtrl.$valid) {

              if($scope.$eval(expression) !== false) {
                console.log('Form data persisted -- setting prestine flag');
                $formCtrl.$setPristine();  
              }

            }

          }, 500);
        }

      });
    }
  };

});
like image 101
null Avatar answered Oct 12 '22 19:10

null