Commit | Line | Data |
---|---|---|
2386ec88 TO |
1 | /// crmAutosave |
2 | (function(angular, $, _) { | |
3 | ||
4 | angular.module('crmAutosave', ['crmUtil']); | |
5 | ||
06e1fe0a TO |
6 | // usage: |
7 | // var autosave = new CrmAutosaveCtrl({ | |
8 | // save: function -- A function to handle saving. Should return a promise. | |
9 | // If it's not a promise, then we'll assume that it completes successfully. | |
10 | // saveIf: function -- Only allow autosave when conditional returns true. Default: !form.$invalid | |
11 | // model: object|function -- (Re)schedule saves based on observed changes to object. We perform deep | |
12 | // inspection on the model object. This could be a performance issue you | |
13 | // had many concurrent autosave forms or a particularly large model, but | |
14 | // it should be fine with typical usage. | |
15 | // interval: object -- Interval spec. Default: {poll: 250, save: 5000} | |
16 | // form: object|function -- FormController or its getter | |
17 | // }); | |
18 | // autosave.start(); | |
19 | // $scope.$on('$destroy', autosave.stop); | |
20 | // Note: if the save operation itself | |
6877567a | 21 | angular.module('crmAutosave').service('CrmAutosaveCtrl', function($interval, $timeout, $q) { |
06e1fe0a TO |
22 | function CrmAutosaveCtrl(options) { |
23 | var intervals = angular.extend({poll: 250, save: 5000}, options.interval); | |
24 | var jobs = {poll: null, save: null}; // job handles used ot cancel/reschedule timeouts/intervals | |
25 | var lastSeenModel = null; | |
26 | var saving = false; | |
2386ec88 | 27 | |
06e1fe0a TO |
28 | // Determine if model has changed; (re)schedule the save. |
29 | // This is a bit expensive and doesn't need to be continuous, so we use polling instead of watches. | |
30 | function checkChanges() { | |
31 | if (saving) { | |
32 | return; | |
33 | } | |
34 | var currentModel = _.isFunction(options.model) ? options.model() : options.model; | |
35 | if (!angular.equals(currentModel, lastSeenModel)) { | |
36 | lastSeenModel = angular.copy(currentModel); | |
37 | if (jobs.save) { | |
38 | $timeout.cancel(jobs.save); | |
2386ec88 | 39 | } |
06e1fe0a | 40 | jobs.save = $timeout(doAutosave, intervals.save); |
2386ec88 | 41 | } |
06e1fe0a | 42 | } |
2386ec88 | 43 | |
06e1fe0a TO |
44 | function doAutosave() { |
45 | jobs.save = null; | |
46 | if (saving) { | |
47 | return; | |
48 | } | |
2386ec88 | 49 | |
06e1fe0a TO |
50 | var form = _.isFunction(options.form) ? options.form() : options.form; |
51 | ||
52 | if (options.saveIf) { | |
53 | if (!options.saveIf()) { | |
2386ec88 TO |
54 | return; |
55 | } | |
06e1fe0a TO |
56 | } |
57 | else if (form && form.$invalid) { | |
58 | return; | |
59 | } | |
2386ec88 | 60 | |
06e1fe0a TO |
61 | saving = true; |
62 | lastSeenModel = angular.copy(_.isFunction(options.model) ? options.model() : options.model); | |
2386ec88 | 63 | |
06e1fe0a TO |
64 | // Set to pristine before saving -- not after saving. |
65 | // If an eager user continues editing concurrent with the | |
66 | // save process, then the form should become dirty again. | |
67 | if (form) { | |
2386ec88 | 68 | form.$setPristine(); |
06e1fe0a TO |
69 | } |
70 | var res = options.save(); | |
71 | if (res && res.then) { | |
72 | res.then( | |
73 | function() { | |
74 | saving = false; | |
75 | }, | |
76 | function() { | |
77 | saving = false; | |
78 | if (form) { | |
2386ec88 TO |
79 | form.$setDirty(); |
80 | } | |
06e1fe0a TO |
81 | } |
82 | ); | |
2386ec88 | 83 | } |
06e1fe0a TO |
84 | else { |
85 | saving = false; | |
86 | } | |
87 | } | |
2386ec88 | 88 | |
6877567a TO |
89 | var self = this; |
90 | ||
06e1fe0a TO |
91 | this.start = function() { |
92 | if (!jobs.poll) { | |
93 | lastSeenModel = angular.copy(_.isFunction(options.model) ? options.model() : options.model); | |
94 | jobs.poll = $interval(checkChanges, intervals.poll); | |
95 | } | |
96 | }; | |
2386ec88 | 97 | |
06e1fe0a TO |
98 | this.stop = function() { |
99 | if (jobs.poll) { | |
100 | $interval.cancel(jobs.poll); | |
101 | jobs.poll = null; | |
102 | } | |
103 | if (jobs.save) { | |
104 | $timeout.cancel(jobs.save); | |
105 | jobs.save = null; | |
106 | } | |
107 | }; | |
6877567a TO |
108 | |
109 | this.suspend = function(p) { | |
110 | self.stop(); | |
111 | return p.finally(self.start); | |
112 | }; | |
06e1fe0a TO |
113 | } |
114 | ||
115 | return CrmAutosaveCtrl; | |
2386ec88 TO |
116 | }); |
117 | ||
118 | })(angular, CRM.$, CRM._); |