2

I think this might be quite common use-case with any angular app. I am simply watching some objects on my scope that are changed as part of several digest cycles. After digesting them (changing their values via databinding) has finished, I want to save them to databse.

A. Now, with the current solutions I see following problems:

  1. running save in $timeout() - how to assure that save is called only once

  2. running a custom function in $scope.$evalAsync - how to find out what has been chaged

There are of course solutions to both of these prolblems, but non of those I know seem ehough elegant to me.

The question is: What is the most elegant solution to the problem?

B. In particular, what are the best practices to

  1. make sure that save gets called only once in a digest cycle

  2. find out that object is dirty after last digest

Misha Moroshko
  • 166,356
  • 226
  • 505
  • 746
honzajde
  • 2,270
  • 3
  • 31
  • 36

1 Answers1

2

Here is a solution I've found working best for me - as an AMD modul. Inspired by Underscore.

   /**
     * Service function that helps to avoid multiple calls 
     * of a function (typically save()) during angular digest process.
     * $apply will be called after original function returns;
     */
        define(['app'], function (app) {
            app.factory('debounce', ['$timeout', function ($timeout) {
                return function(fn){ // debounce fn
                    var nthCall = 0;
                    return function(){ // intercepting fn
                        var that = this;
                        var argz = arguments;
                        nthCall++;
                        var later = (function(version){
                            return function(){
                                if (version === nthCall){
                                    return fn.apply(that, argz);
                                }
                            };
                        })(nthCall);
                        return $timeout(later,0, true);
                    };
                };
            }]);
        });


    /*************************/

    //Use it like this: 

    $scope.$watch('order', function(newOrder){
      $scope.orderRules.apply(newOrder); // changing properties on order
    }, true);

    $scope.$watch('order.valid', function(newOrder){
      $scope.save(newOrder); //will be called multiple times while digested by angular
    });

    $scope.save = debounce(function(order){
      // POST your order here ...$http....
      // debounce() will make sure save() will be called only once
    });
honzajde
  • 2,270
  • 3
  • 31
  • 36
  • the final version of this function allows one to pass in the timeout value and whether to call apply in the timeout. oddly, if you use apply = true, this appears to call a digest on every call to debounce, rather than just the last time in which the debounced fn is called. passing in false fixes this, but then you need to manually call apply() in the debounced fn – chrismarx Oct 15 '13 at 20:48