I have an application having a lot of things to save in cascade, imaging a normal master – detail view.
In this view I have a “Save All” Button that save each row in an iteration, triggering jQuery custom events, to serialize the saving operations and prevent the generation an uncontrolled queue of requests.
Each time a row is saved, the program decrement the counter and launch the save of the new row.
Everything ends when there where no rows to save (counter = 0).
This is a code snippet doing this:
var save_counter = -1; // Creates a counter and save content header when finished to save rows. var updCounter = function(evt){ // Update Counter save_counter--; // Register updates When there are not rows to skip if ((save_counter===0) || (save_counter===0 && edit_status == "modified") ){ console.log('Persist Master'); $(document).trigger('save_ok'); } }; saveRows = $(form_sel); // Reset Save Counter save_counter = saveRows.length; // Iterate through lines saveRows.each(function(idx){ var form = $(this); // Execute Uptade Counter once form.one(update_counter, updCounter); // Per each performed save, decrese save counter form.trigger('submit'); });
Now I’m migrating some critical application modules, using angular but I have no idea to do that.
There is a best practice to perform a batch request call?
Is it a good idea to use $scope
variables and $watch
, using something like this?
var RowController = angular.controller('RowController', function($scope, $http){ $scope.rows = [ {id : 1, title : 'lorem ipsum'} , {id : 2, title : 'dolor sit amet'} , {id : 3, title : 'consectetuer adipiscing elit'} ]; // Counter Index $scope.save_counter = -1; // "Trigger" the row saving, changing the counter value $scope.saveAll = function () { $scope.save_counter = 0; }; // Watch the counter and perform the saving $scope.$watch('save_counter', function( // Save the current index row if ($scope.save_counter >= 0 && $scope.save_counter < $scope.rows.length) { $http({ url : '/row/' + $scope.rows[$scope.save_counter].id, data: $scope.rows[$scope.save_counter] }).success(function(data){ // Update the counter ... $scope.save_counter ++; }).error(function(err){ // ... even on error $scope.save_counter ++; }); }; )); });
Advertisement
Answer
The best approach is to use a service with promises ($q).
Here’s an example:
app.factory('RowService', function($http, $q) { return { saveRow: function(row) { return $http({ url: '/row/' + row.id, data: row }); }, saveAll: function(rows) { var deferred = $q.defer(); var firstRow = rows.shift(); var self = this; // prepare all the saveRow() calls var calls = []; angular.forEach(rows, function(row) { calls.push(function() { return self.saveRow(row); }); }); // setup the saveRow() calls sequence var result = this.saveRow(firstRow); angular.forEach(calls, function(call) { result = result.then(call); }); // when everything has finished result.then(function() { deferred.resolve(); }, function() { deferred.reject(); }) return deferred.promise; } } });
And on your controller:
app.controller('RowController', function($scope, RowService) { ... $scope.saveAll = function() { // $scope.rows.slice(0) is to make a copy of the array RowService.saveAll($scope.rows.slice(0)).then( function() { // success }, function() { // error }) }; });
Check this plunker for an example.