In my app i'm using restangular, and i have such method (which i converted from the plain $http request).
And i don't know how to use correctly restangular with promises. How is it possible?
Here is my code:
var test = function(){
var data = '{"Office":"' + office + '"}';
var deferred = $q.defer();
var person = Restangular.one('persons', id)
$scope.person.patch(data).then(function (response) {
deferred.resolve(response);
},function (err, status) {
deferred.reject(status);
});
return deferred.promise;
}
var runIt = function(){
test.then(...)...
}
You could simply use promise returned by patch method of Restangular.one instead of creating a new custom promise.
Code
var test = function() {
var data = '{"Office":"' + office + '"}';
var person = Restangular.one('persons', id);
//returned promise
return person.patch(data).then(function(response) {
return response.data;
}, function(err, status) {
return response.status;
});
}
test().then(...)
Related
So I have a series of functions I've defined in the service that upload an image to my Amazon s3 bucket, and I can console.log and alert in the service itself and everything is coming back correct.
However, now I want to return that promise to the controller so I can let the user know the upload is finished. I'm just not sure how I would do that. I've tried putting returns at the filereader.onload but then I get errors saying that what I've given back isn't a promise and such. Here's my code:
angular.module("testApp", [])
.controller("testCtrl", function($scope, amazonService) {
$scope.test = "leeroy jenkins";
$scope.upload = function() {
amazonService.uploadImage($('#file'));
}
})
.service("amazonService", function($http) {
var url = "/api/"
var uploadImageFilestream = function(obj) {
return $http({
method: "PUT",
url: url + "media/images",
data: obj
}).then(function(res) {
if (res.status === 200) {
alert("upload successful!");
}
return res;
});
}
var formatFileName = function(filename, newBase) {
//first, get the file extension
var extension = filename.substring(filename.lastIndexOf("."));
return newBase + extension;
}
this.uploadImage = function(obj) {
var file = obj[0].files[0];
var fileReader = new FileReader();
fileReader.onload = function(loaded) {
uploadImageFilestream({fileName: formatFileName(file.name, "test1"), fileBody: loaded.target.result});
}
fileReader.readAsDataURL(file);
}
})
I know that if I combined the uploadImageFilestream function with the uploadImage function it would work, but I'm not sure how to structure it with the promise in a separate function.
Use $q:
.service("amazonService", function($http, $q) {
var url = "/api/"
var uploadImageFilestream = function(obj) {
return $http({
method: "PUT",
url: url + "media/images",
data: obj
});
}
var formatFileName = function(filename, newBase) {
//first, get the file extension
var extension = filename.substring(filename.lastIndexOf("."));
return newBase + extension;
}
this.uploadImage = function(obj) {
var file = obj[0].files[0];
var fileReader = new FileReader();
var deferer = $q.defer();
fileReader.onload = function(loaded) {
uploadImageFilestream({fileName: formatFileName(file.name, "test1"), fileBody: loaded.target.result})
.then(function(res) {
if (res.status === 200) {
deferer.resolve();
alert("upload successful!");
}
return res;
});
}
fileReader.readAsDataURL(file);
return deferer.promise;
}
})
You should be using $q service of AngularJs to create deferred object and return promise.
I have modified the following code as to demonstrate way to use promises.
angular.module("testApp", [])
.controller("testCtrl", function($scope, amazonService) {
$scope.test = "leeroy jenkins";
$scope.upload = function() {
var promise = amazonService.uploadImage($('#file')); // call to function returns promise
promise.then(function(){ // when promise is resolved, desired data is passed
alert("success");
}).catch(function(error){ // when promise is rejected, related error object is passed
alert("failure");
});
}
})
.service("amazonService", function($http, $q) { // added $q service to handle promises
var url = "/api/"
var uploadImageFilestream = function(obj) {
return $http({
method: "PUT",
url: url + "media/images",
data: obj
}).then(function(res) {
if (res.status === 200) {
alert("upload successful!");
}
return res;
});
}
var formatFileName = function(filename, newBase) {
//first, get the file extension
var extension = filename.substring(filename.lastIndexOf("."));
return newBase + extension;
}
this.uploadImage = function(obj) {
var file = obj[0].files[0];
var fileReader = new FileReader();
var deferredObject = $q.defer(); // added deferred object which will be used to return promise and resolve or reject is as shown below
fileReader.onload = function(loaded) {
uploadImageFilestream({fileName: formatFileName(file.name, "test1"), fileBody: loaded.target.result}).then(response){
deferredObject.resolve(response); // when resolve function of deferred object is called success callback in controller will be called with the data you pass here
}).catch(function(errorObj){
deferredObject.reject(errorObj); // when reject function of deferred object is called error callback is controller will be called with the error object you pass here
});
}
fileReader.readAsDataURL(file);
return deferredObject.promise; // return promise object which will be resolve or reject and accordingly success callback and error callback will be called with then and catch respectively
}
});
Link to AngularJs Reference.
There are other different ways too to create and return promise you can see it in reference.
One other way to create and return promise as given in reference is using $q object as function and passing callback directly as shown below:
// for the purpose of this example let's assume that variables `$q` and `okToGreet`
// are available in the current lexical scope (they could have been injected or passed in).
// for the purpose of this example let's assume that variables `$q` and `okToGreet`
// are available in the current lexical scope (they could have been injected or passed in).
function asyncGreet(name) {
// perform some asynchronous operation, resolve or reject the promise when appropriate.
return $q(function(resolve, reject) {
setTimeout(function() {
if (okToGreet(name)) {
resolve('Hello, ' + name + '!');
} else {
reject('Greeting ' + name + ' is not allowed.');
}
}, 1000);
});
}
var promise = asyncGreet('Robin Hood');
promise.then(function(greeting) {
alert('Success: ' + greeting);
}, function(reason) {
alert('Failed: ' + reason);
});
Background
I have a hierarchy of entities that looks like (Manufacturer -> Vehicle Type -> Vehicle Model -> Vehicle Submodel). Each manufacturer has multiple vehicle types, each vehicle type has multiple models, and each model has multiple submodels.
I need to retrieve a list of vehicle models given a manufacturer and type, and then for each of those models, retrieve all of its submodels (getSubmodels). Once they're all fetched I place them into a JSON object. I'm trying to use Angular's $q module to ensure that all submodels have been retrieved before I continue execution - aka I need all of the promises to be resolved before I can move on and render the page components.
Issue:
The 'happy' path works perfectly, but if one of the requests for submodels has an error, the rejected promise from the submodel function does not get caught by the $q.all().then block, and thus the overall getVehicleHierarchy promise does not get rejected.
angular.module('uiApp.services.hierarchy', ['restangular'])
.service('VehicleHierarchy', function VehicleHierarchy($http, $q, Restangular) {
this.getVehicleHierarchy = function (manufacturerId, vehicleTypeId, vehicleModelId) {
var that = this;
var deferred = $q.defer();
var promise = deferred.promise;
var promises = [];
Restangular.configuration.baseUrl = urlBuilder.buildHierarchyServiceUrl() + '/vehicle-hierarchy';
Restangular.one('manufacturer', manufacturerId).one('type', vehicleTypeId).one('class', vehicleClassId).customGET('models')
.then(function (models) {
var result = {};
_.forEach(models.result, function (model) {
result[parseInt(model.id)] = model;
});
_.forEach(result, function (model) {
promises.push(that.getSubmodels(manufacturerId, vehicleTypeId, vehicleClassId, model.id));
});
$q.all(promises).then(function (results) {
var i = 0;
_.forEach(result, function (model) {
result[parseInt(model.id)].subModels = results[i++];
}, function (errors) {
deferred.reject(errorResponse);
});
deferred.resolve(result);
});
}, function(error) {
deferred.reject('error!');
});
return promise;
};
this.getSubmodels = function (manufacturerId, vehicleTypeId, vehicleClassId, modelId) {
var submodels = {};
var deferred = $q.defer();
Restangular.configuration.baseUrl = urlBuilder.buildHierarchyServiceUrl() + '/vehicle-hierarchy';
Restangular.one('manufacturer', brandId).one('type', vehicleTypeId).one('class', vehicleClassId).one('model', modelId)
.customGET('submodels').then(function (submodelResponse) {
_.forEach(subclassResponse.result, function (subModel) {
subclasses[parseInt(subModel.id)] = subModel;
});
deferred.resolve(subclasses);
}, function (error) {
deferred.reject('error'!);
});
return deferred.promise;
};
});
});
You need to return the $q.all call for your error function in the then to catch it:
return $q.all(promises).then(function (results) {
I am using cheerio and node to do web scraping. I thought it would be good idea to use promise for making it easier to deal with the asynchronous code. So, tried to chain the promises but could not make it working. I am pasting my code over here such that somebody could help me figure out what exactly I have been doing wrong.
http.createServer(function(req, res){
res.writeHead(200, {"Content-Type": "application/json"})
loadPage().then(parseLoadedData);
}).listen(3000, function(error){
console.log(error);
});
function fetchMainPage(){
var deferred = q.defer();
http.get('http://www.google.com?q=node', function(response){
var responseString = '';
response.on('data', function(data){
responseString += data.toString('utf8');
});
response.on('error', function(error){
deferred.reject(error);
});
response.on('end', function(){
deferred.resolve(responseString);
});
});
return deferred.promise;
}
function parseMainContent(responseString){
var deferred = q.defer();
var $ = cheerio.load(responseString);
var rightCol = $('#right-col');
var children = rightCol.children();
var keys = Object.keys(children);
var results = [];
keys.forEach(function(key){
var div = children[key];
div.children.forEach(function(aChild){
if(aChild.name == 'h3' && aChild.children[0].data == "Some title"){
lis = aChild.next.children;
var results = lis.map(function(li){
var anchor = $(li).find('a');
if(anchor != undefined && anchor.attr('href') != undefined)
return [anchor.text(), anchor.attr('href')]
});
results = results.filter(function(result){
return result != undefined;
});
deferred.resolve(results);
}
});
});
return deferred.promise;
}
var loadPage = function(){
return fetchMainPage().then(function(data){
return data;
})
},
parseLoadedData = function(data){
return parseMainContent(data).then(function(results){
console.log(results);
});
}
The problem here is I can't get my parseLoadedData being called. The response is fetched from the server but the second chaining does not seem to be working. I would like to thank you all in advance for helping me out.
Note: The url I am using is different and so the parsing function deal with that specific url only.
You don't really need the loadPage function since fetchMainPage already returns a promise so this should work:
var loadPage = function(){
return fetchMainPage();
}
To chain promises every then callback should return another promise and you were returning the data.
Eg.:
var loadPage = function(){
var deferred = q.defer();
fetchMainPage().then(function(data){
return someOtherPromise(data);
}).then(function(someOtherData) {
return myThirdPromise(someOtherData);
}).then(function(myThirdData) {
return deferred.resolve(myThirdData);
});
}
// IS THE SAME AS
var loadPage2 = function(){
return fetchMainPage().then(function(data){
return someOtherPromise(data);
}).then(function(someOtherData) {
return myThirdPromise(someOtherData);
});
}
I have an $http request that is returning a bunch of rows. I need to process each of those results synchronously. Having trouble wrapping my brain around Angular.
Each of the records needs to be processed against a local SQLite database on an iOS device, and that is an asynchronous call.
If any of the loop records fail, I need to abort the entire operation (and loop).
Here's the code to see if it helps...
var username = $rootScope.currentUser;
window.logger.logIt("Executing incremental sync with username " + username);
var url = $rootScope.serviceBaseUrl + 'SyncData/GetSyncItems?userid=' + username + '&lastSyncDate=' + lastSyncDate.toString();
var encoded = encoder.encode($CONFIG.serviceAccount);
$http.defaults.headers.common.Authorization = 'Basic ' + encoded;
$http({ method: 'Get', url: url })
.success(function(data, status, headers, config) {
var processes = [];
for (var i in data) {
var params = data[i].Params;
var paramsMassaged = params.replaceAll("[", "").replaceAll("]", "").replaceAll(", ", ",").replaceAll("'", "");
var paramsArray = paramsMassaged.split(",");
var process;
if (data[i].TableName === "Tabl1") {
window.logger.logIt("setting the process for a Table1 sync item");
process = $Table1_DBContext.ExecuteSyncItem(data[i].Query, paramsArray);
} else if (data[i].TableName === "Table2") {
window.logger.logIt("setting the process for an Table2 sync item");
process = $Table2_DBContext.ExecuteSyncItem(data[i].Query, paramsArray);
} else {
window.logger.logIt("This table is not included in the sync process. You have an outdated version of the application. Table: " + data[i].TableName);
}
window.logger.logIt("got to here...");
processes.push(process);
}
window.logger.logIt("Finished syncing all " + data.length + " records in the list...");
$q.all(processes)
.then(function (result) {
// Update the LastSyncDate here
$DBConfigurations_DBContext.UpdateLastSyncDate(data[i].CreatedDate);
alert("finished syncing all records");
}, function (result) {
alert("an error occurred.");
});
})
.error(function(data, status, headers, config) {
alert("An error occurred retrieving the items that need to be synced.");
});
Table2 ExecuteSyncItem function:
ExecuteSyncItem: function (script, params) {
//window.logger.logIt("In the Table2 ExecuteSyncItem function...");
//$DBService.ExecuteQuery(script, params, null);
var deferred = $q.defer();
var data = $DBService.ExecuteQuery(script, params, null);
if (data) {
deferred.resolve(data);
} else {
deferred.reject(data);
}
return deferred.promise;
}
DB Service code:
ExecuteQuery: function (query, params, success) {
$rootScope.db.transaction(function (tx) {
tx.executeSql(query, params, success, onError);
});
},
Update: In response to Maxim's question "did you log process method". Here's what I'm doing...
ExecuteSyncItem: function (script, params) {
window.logger.logIt("In the Experiment ExecuteSyncItem function...");
//$DBService.ExecuteQuery(script, params, null);
var deferred = $q.defer();
var data = $DBService.ExecuteQuery(script, params, function () { window.logger.logIt("successCallback"); });
if (data) {
window.logger.logIt("success");
deferred.resolve(data);
} else {
window.logger.logIt("fail");
deferred.reject(data);
}
return deferred.promise;
}
"data" is undefined everytime. "fail" is logged everytime, as well as "successCallback". Also, the executeQuery IS working, and updating the data the way I expect.
So now, it's just a matter of the promise syntax I guess. If the ExecuteQuery isn't actually populating the "data" variable since it's asynchronous, how do I set the deferred.resolve() and deferred.reject stuff?
You are on right way
I would use $q.all
$q.all([async1(), async2() .....])
Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
Returns a single promise that will be resolved with an array/hash of values, each value corresponding to the promise at the same index/key in the promises array/hash. If any of the promises is resolved with a rejection, this resulting promise will be rejected with the same rejection value.
For example:
var processes = [];
processes.push(Process1);
processes.push(Process2);
/* ... */
$q.all(processes)
.then(function(result)
{
/* here all above mentioned async calls finished */
$scope.response_1 = result[0];
$scope.response_2 = result[1];
}, function (result) {
alert("Error: No data returned");
});
From your example you run in loop and call async methods (Process1, Process2) 10 times (8 and 2 respectively). In order to use $q.all the Process1, Process2 must return promise.
So I would write it something like that:
var Process1 = function(stuff) {
var deferred = $q.defer();
var data = $DBService.ExecuteQuery(stuff.query); // This is asynchronous
if (data ) {
deferred.resolve(data);
} else {
deferred.reject(data);
}
return deferred.promise;
}
I am following this Angular tutorial on promises. I have a service that checks if an array is empty and if so, hits a REST service, returns a promise and updates the array. Here is the relative code:
requestingProviders: [],
getRequestingProviders: function() {
var that = this;
var deferred = $q.defer();
if(this.requestingProviders.length === 0) {
this.getResource().search({
role: 'REQUESTING_PROVIDER'
}, function(data) {
that.requestingProviders = data.providers;
deferred.resolve(data.providers);
});
return deferred.promise;
} else {
return that.requestingProviders;
}
}
The service is being called from a controller. Here is the code where it is being called:
$scope.providers = providerService.getRequestingProviders();
The REST call is made and returns fine, but the view is never updated. This is not working like the tutorial explained. Here is a plunker that shows what I am expecting. What am I doing wrong?
You need to resolve your promise:
var prom = providerService.getRequestingProviders();
prom.then(function (data) {
$scope.providers = data;
});
Also, change your code to always return the promise:
getRequestingProviders: function() {
var that = this;
var deferred = $q.defer();
if(this.requestingProviders.length === 0) {
this.getResource().search({
role: 'REQUESTING_PROVIDER'
}, function(data) {
that.requestingProviders = data.providers;
deferred.resolve(data.providers);
});
} else {
deferred.resolve(that.requestingProviders);
}
return deferred.promise;
}