Auto save with Angular

A service + directive that enables autosave on lost focus

HTML

<script src="http://code.angularjs.org/1.1.0/angular.min.js"></script>
<script src="http://underscorejs.org/underscore-min.js"></script>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<div ng-app="app" class="container">
    <div ng-controller="Ctrl" class="row">
        <div class="span5 well">
          <input id="first" type="text" ng-model="texts.first" class="span2"/>
          <p class="span2 pull-right">{{texts.first}}</p>

          <input id="second" type="text" ng-model="texts.second" class="span2"/>
          <p class="span2 pull-right">{{texts.second}}</p>
    </div>    
</div>

JavaScript

var $scope;
var app = angular.module('app', []);

function Ctrl($scope, pendingServerInteractions) {
    $scope.texts = {
        first: 'First',
        second: 'Second'
    };
    $scope.$watch('texts', function(newValue, oldValue) {
        if (newValue != oldValue) {
            console.log('texts changed: ' + JSON.stringify($scope.texts));
            pendingServerInteractions.add('texts', function() {
                console.log('saving: ' + JSON.stringify(newValue));
                //do your save thing
            });
        }
    }, true);
}

app.directive('input', function(uiState) {
    return {
        restrict: 'E',
        scope: false,
        link: function(scope, element, attrs) {

            $(element).bind('blur', function(e) {
                scope.$apply(uiState.blur(element));
            });
            $(element).bind('focus', function(e) {
                scope.$apply(uiState.focus(element));
            });
        }
    };
});

app.factory('pendingServerInteractions', function(uiState, $rootScope) {
    var registry = {};

    pendingServerInteractions = {
        /**
         * Adds an interaction that should be performed later to the registry under the
         * given key.
         *
         * If an interaction already existed under this key the old interaction will be 
         * overwritten and not executed.
         *
         * @param {string} key The key under which to store the interaction
         * @param {function()} interaction The interaction function to be executed later
         */
        add: function(key, interaction) {
            registry[key] = interaction;
        },

        /**
         * @private
         *
         * Will be called by a watch on focussed state. Executes all pending interactions.
         */
        executeCallbacks: function() {
            _.forEach(registry, function(value, key, myMap) {
                if (value) {
                    value(key);
                }
            });
       ...