Reputation: 519
I have the following pattern of code, where an update call is made each time the user updates the data on the view.
// FACTORY SERVICE CODE
.factory('updateService', ['$http', '$q', function($http, $q){
var df = $q.defer();
var updateData = function(uriName, dataObj){
$http({
method: 'PUT',
data: dataObj,
url: uriName
}).then(function successCallback(response) {
console.log('from put');
console.log(response.data);
df.resolve(response.data);
}, function errorCallback(response) {
console.log('Error', response.data);
df.reject(response.data);
});
return df.promise;
}
return {updateData:updateData};
}])
// CONTROLLER CODE
.controller('MainCtrl', ['$scope','updateService', function($scope, updateService) {
$scope.saveToServer = function(){
var tmpObj = {data: $scope.dataOne, anotherData: $scope.dataTwo};
myService.updateData(uriName, tmpObj).then(function(resolved) {
console.log('CONTROLLER');
$scope.myData = resolved;
console.log($scope.myData);
console.log('end');
}, function(rejected){
console.log('put rejected');
});
});
$scope.btnUpdateClick = function(){
$scope.saveToServer();
};
}]);
PROBLEM: When the user makes an update request for the first time, the code works as expected. But when the user updates therein after, the "resolved promise" code (in the controller) executes FIRST and then the $http code (in the service) executes.
SAMPLE OUTPUT of btnUpdateClick made TWICE with enough interval in-between:
from put
Array [ Object, Object]
CONTROLLER
Array [ Object, Object]
end
CONTROLLER
Array [ Object, Object]
end
from put
Array [ Object, Object]
QUESTION: Kindly point me out where I'm making a mistake?
Is the problem because of PUT request?
or is it possible to correct the execution queue as expected, through $apply or anything like that?
Upvotes: 2
Views: 1602
Reputation: 101672
You are using the "explicit promise creation antipattern" here, and on top of that, trying to share a single deferred object among all your service calls.
Just get rid of the deferred and take advantage of promises' chaining properties and everything will be good:
.factory('updateService', ['$http', '$q', function($http, $q){
var updateData = function(uriName, dataObj){
return $http({
method: 'PUT',
data: dataObj,
url: uriName
})
.then(function successCallback(response) {
console.log('from put');
console.log(response.data);
return response.data;
})
.catch(function errorCallback(response) {
console.log('Error', response.data);
throw response.data;
});
};
return {updateData:updateData};
}]);
Upvotes: 4
Reputation: 136144
There is no need to create a custom promise over here, though it is considered as bad practice.
Is the problem because of PUT request?
It's not a problem related to PUT request. Your factory is using common deferred
object and same has been returned from method. Once that promise
object will get resolved, it will be always be in resolve state.
Rather I'd like to suggest you to utilize $http.put
promise, and make your code smaller than what you had before.
var updateData = function(uriName, dataObj){
return $http.put(uriName,dataObj)
}
Upvotes: 3
Reputation: 1474
Change your code to :
// FACTORY SERVICE CODE
.factory('updateService', ['$http', '$q', function($http, $q){
var updateData = function(uriName, dataObj){
var df = $q.defer();
$http({
method: 'PUT',
data: dataObj,
url: uriName
}).then(function successCallback(response) {
console.log('from put');
console.log(response.data);
df.resolve(response.data);
}, function errorCallback(response) {
console.log('Error', response.data);
df.reject(response.data);
});
return df.promise;
}
return {updateData:updateData};
}])
// CONTROLLER CODE
.controller('MainCtrl', ['$scope','updateService', function($scope, updateService) {
$scope.saveToServer = function(){
var tmpObj = {data: $scope.dataOne, anotherData: $scope.dataTwo};
myService.updateData(uriName, tmpObj).then(function(resolved) {
console.log('CONTROLLER');
$scope.myData = resolved;
console.log($scope.myData);
console.log('end');
}, function(rejected){
console.log('put rejected');
});
});
$scope.btnUpdateClick = function(){
$scope.saveToServer();
};
}]);
In your old code you was creating defer object only once so it was resolved /rejected only once. But what you need is to return promise each time and resolve/ reject it each time.
Upvotes: 0
Reputation: 2110
Start the promise every time you call a function like below:
.factory('updateService', ['$http', '$q', function($http, $q){
var updateData = function(uriName, dataObj){
var df = $q.defer(); //Changes here
$http({
method: 'PUT',
data: dataObj,
url: uriName
}).then(function successCallback(response) {
console.log('from put');
console.log(response.data);
df.resolve(response.data);
}, function errorCallback(response) {
console.log('Error', response.data);
df.reject(response.data);
});
return df.promise;
}
return {updateData:updateData};
}])
In this way you are creating a new promise on every function call.
Upvotes: 1