I'm making a get request to my server, I get the response and I store the value inside a $scope.productId
userService.get(true)
.then(function(res) {
$scope.productId = res.user.productid;
}
});
then I need to use this value in another get request to the api to get the product related to this id.
apiService.get('/product/' + ???)
.then(function(response) {
console.log(response)
})
.catch(function(response) {
});
I'm new to promises, so the objective is to get the value of the first request in to the second one!
use this
userService.get(true)
.then(function(res) {
$scope.productId = res.user.productid;
apiService.get('/product/' + ???)
.then(function(response) {
console.log(response)
})
.catch(function(response) {
});
}
});
Related
I have a state defined like this:
.state('list', {
url: '/list',
controller: 'ctrl',
resolve: {
data: ['DataService', function(DataService) {
return DataService.getList();
}]
}
})
The getList of DataService makes the http request:
var httpRequest = $http(categoryRequest);
httpRequest.then(function (response) {
return response.data;
})
.catch(function (error) {
console.log('could not get categories from server');
});
return httpRequest;
In controller I just assign the list to its list property:
function ctrl(data) {
this.list = data.data;
}
The problem:
No matter what I return in success callback of http request, I always get the whole response in resolve of state provider.
So I have to do data.data in controller to get the data from response.
Questions:
Is my assumption true that I will always get the whole reponse in resolve?
How to get just the data form response that I do not have to get it in controller.
Best regards,
var httpRequest = $http(categoryRequest);
So httpRequest is a Promise<Response>
httpRequest.then(function (response) {
This creates another promise, but this new promise is not assigned to anything.
return httpRequest;
This returns the original Promise<Response>.
You want
httpRequest = httpRequest.then(function (response) {
Or simply
return httpRequest.then(function (response) {
So that what you return is the new promise.
To give you a simpler analog example, your code is similar to
var a = 1;
a + 1;
return a;
That returns 1, not 2.
To return 2, you need
var a = 1;
a = a + 1;
return a;
or
var a = 1;
return a + 1;
There are two problems. First, the service is returning the original httpPromise and not the promise derived from the original promise. Second, the error handler is converting the rejection to a success.
var httpRequest = $http(categoryRequest);
//httpRequest.then(function (response) {
var derivedPromise = httpRequest.then(function onSuccess(response) {
//return to chain data
return response.data;
}).catch(function onReject(error) {
console.log('could not get categories from server');
//IMPORTANT to avoid conversion
throw error;
//OR
//return $q.reject(error);
});
//return httpRequest;
return derivedPromise;
The .then method of a promise returns a new promise derived from the original promise. It does not mutate the original promise.
A common problem is the omission of a throw or return $q.reject statement from a rejection handler. Functions without such statements return a value of undefined which will convert a rejection to a success which resolves as undefined.
I created following controller with 2 service calling with services. Second response comes before then first. i want to do like i need first response first and second response second. but i just stuck with async and sync please help me for solving.
Second call is depends on first call. For example if first call returns 10 record then i have to call second web service 10 time taking id from first response. so i use for loop but it is not proper.
Controller
var mycompaigndata = [];
asyncService.loadDataFromUrls($http.get(WSURL + 'api/first/',
{
headers:
{
"Authorization":'Bearer <my-token>'
}
}))
.then(function(data)
{
console.log(data);
});
asyncService.loadDataFromUrls($http.get(WSURL + 'api/second',
{
headers:
{
"Authorization":'Bearer <my-token>'
}
}))
.then(function(data)
{
console.log(data);
});
Service
app.service('asyncService', function($http, $q)
{
return {
loadDataFromUrls: function(url)
{
var deferred = $q.defer();
var urlCalls = [];
urlCalls.push(url);
$q.all(urlCalls)
.then(
function(results)
{
deferred.resolve(results)
},
function(errors)
{
deferred.reject(errors);
},
function(updates)
{
deferred.update(updates);
});
return deferred.promise;
}
};
});
To make sure the second calls are executed after the first one is finished, put the second call within then of the first call. To make multiple 'second' calls depending on the number of results of the first call, use $q.all.
asyncService.loadDataFromUrls('api/first/')
.then(function(firstData) {
//assuming firstData is an array of 'x' items, do a call for each of these items:
console.log('results of first call holds ' + firstData.length + ' items');
var promises = [];
for(var i = 0; i<firstData.length; i++){
var id = firstData[i].id;//you can use this to pass to the second call
promises.push(asyncService.loadDataFromUrls('api/second'));
}
return $q.all(promises);
})
.then(function(results) {
//'results' is an array of results, the nth item holds the result of the 'nth' call to loadDataFromUrls
for(var i = 0; i<results.length; i++){
console.log('result nr. ' + i + ' :' + results[i])
}
});
By using return $q.all(promises), you're avoiding the promise pyramid of doom, and keep a flat structure.
Your service code doesn't need to loop anymore. As a sidenote, you can shorten the code of the service and avoid using the 'explicit promise construction antipattern' (see here) like this:
app.service('asyncService', function($http, $q)
{
return {
loadDataFromUrls: function(url)
{
return $http.get(WSURL + url, {
headers: {
"Authorization": 'Bearer <my-token>'
}
}).then(function(response){ return response.data; });
}
};
});
Your asyncService seems completely unnecessary and unuseful.
It sounds like you just need to learn how to chain promises and use $q.all correctly:
function queryApi(subUrl) {
return $http.get(WSURL + subUrl, {
headers: {
"Authorization":'Bearer <my-token>'
}
}).then(function (result) { return result.data; });
}
queryApi('api/first/')
.then(function (data) {
return $q.all(data.map(function (entry) {
return queryApi('api/second/' + entry.id);
}));
})
.then(function (results) {
console.log(results);
});
put second request inside first request promise:
var mycompaigndata = [];
asyncService.loadDataFromUrls($http.get(WSURL + 'api/first/',
{
headers:
{
"Authorization":'Bearer <my-token>'
}
}))
.then(function(data)
{
asyncService.loadDataFromUrls($http.get(WSURL + 'api/second',
{
headers:
{
"Authorization":'Bearer <my-token>'
}
}))
.then(function(data)
{
console.log(data);
});
});
I think the best answer is to use loop since you need to iterate the response to get the id.
asyncService.loadDataFromUrls(WSURL + 'api/first/')
.then(function(data) {
//iterate to get the id
//call service again
asyncService.loadDataFromUrls(WSURL + 'api/first/')
.then(function(data) {
//code here
});
});
Service
app.service('asyncService', function($http, $q) {
return {
loadDataFromUrls: function(url) {
return $http.get(url, {
"Authorization":'Bearer <my-token>'
});
}
};
});
I bit new to Angular, using controller, factory that keeps methods for the $http requests.
--------------In Service------------------------------
factory.getContract = function(guid) {
return $http
.get(Configuration.apiurl + '/contracts/' + guid, { headers: {"accesstoken": 'XXXXXXXXXX'}})
.then(function (response) {
return response;
});
};
--------------- In Controller ------------------
$scope.getContract = function (guid) {
ContractService.getContract(guid).then(
function (response) {
var data = response.data;
$scope.contract = {
contract_status: data.contract_status,
car: data.car,
properties: data.data
};
},
function (response) {
console.log('Error while loading the contract,', response);
}
);
};
if ($state.includes('contracts.edit')) {
$scope.getContract($stateParams.guid);
// In this controller, i check if requesting route is the Edit then get contract data. When i do this it works and fill the form with correct information. BUT AS I TRY TO ACCESS $scope.contract in console it says undefined
console.log($scope.contract);
}
What is wrong with my code?
$http.get is returning Promise, so just remove .then part from your factory if you want to execute callback inside controller:
factory.getContract = function(guid) {
return $http.get(Configuration.apiurl + '/contracts/' + guid,
{ headers:
{"accesstoken": 'XXXXXXXXXX'}
});
}
I'm totally new to ionic/angular, this is my code:
.controller('PostCtrl', function($scope, Posts, $cordovaSQLite, $http) {
$scope.getPosts = function() {
$http.get('http://localhost/postIds').then(function(resp) {
_.each(resp.data, function(id) {
var query = "SELECT id FROM posts WHERE id = ?";
$cordovaSQLite.execute(db, query, [id]).then(function(res) {
if(res.rows.length = 0) {
$http.get('http://localhost/post/' + id).then(function(resp) {
var post = resp.data;
var query = "INSERT INTO posts (postId, title, user, content) VALUES (?,?,?,?)";
$cordovaSQLite.execute(db, query, [post.id, post.title, post.user, post.content]).then(function(res) {
// success
}, function(err) {
console.log(err);
});
}, function(err) {
console.log(err);
});
}
}, function (err) {
console.error(err);
});
});
}, function(err) {
console.log(err);
});
}
})
what am I doing is
get all ids from server
if id doesnt exist in db(sqlite)
get post by id from server
insert post into db
It ends up deeply nested, ugly.
what is the ionic, angular way to do this?
As the others suggested the best option is to use promises so you don't have to nest statements like you're doing.
AngularJs uses $q promises:
A service that helps you run functions asynchronously, and use their
return values (or exceptions) when they are done processing.
On the internet there are tons of articles about promises and how to chain them.
Recently I found this article which explains the common mistakes with promises.
It's worth reading cause it goes deep into the topic.
In AngularJs you would create a promise using the $q service:
function doSomething() {
var deferred = $q.defer();
deferred.resolve({value: true});
return deferred.promise;
}
This bit of code returns a promise which is resolved - since there's no async operation - when it's called. It would return an object with a property value = true.
The cool thing about promises is the fact that you can chain them:
doSomething()
.then(function(result){
// result.value should be true.
return doSomething();
})
.then(function(result){
// result.value should be true.
// this is the result of the second call.
});
passing the result of the previous - resolved - promise.
If promises are rejected because of some exceptions:
deferred.reject({value: false});
you can trap the error and stop the execution in the chain:
doSomething()
.then(function(result){
// result.value should be true.
return doSomething();
})
.then(function(result){
// result.value should be true.
// this is the result of the second call.
})
.catch(function(reason){
// reason for failure.
});
Finally you can use the finally to do some cleanup or other things:
doSomething()
.then(function(result){
// result.value should be true.
return doSomething();
})
.then(function(result){
// result.value should be true.
// this is the result of the second call.
})
.catch(function(reason){
// reason for failure.
})
.finally(function(){
// it's going to be executed at the end of the chain, even in case of error trapped by the catch.
});
Things are not so simple, though. At the beginning you might find yourself spending a few hours debugging the code.
How would I fix your code ?
First of all I would create a function which fetch the ids calling the web api:
function fetchIds() {
console.log('Fetching Ids ...');
var deferred = $q.defer();
$http({
method: 'GET',
url: 'http://localhost/postIds',
params: {}
})
.success(function(data) {
deferred.resolve(data);
})
.error(function(data, status) {
deferred.reject(data);
});
return deferred.promise;
}
As you can see I've implemented the system described above.
$http already returns a promise but I wrapped it creating a new promise, anyway.
Then I would have to query the database to find the non existing ids (I didn't put my code in a loop as it is easier to get all the records in one call):
function queryForIds(ids) {
console.log('Querying for Ids ' + ids.toString() + ' ...');
var deferred = $q.defer();
var params = [];
for (var i = 0; i < ids.length; i++) {
params.push('?');
}
window.myDatabase.transaction(function(tx) {
tx.executeSql("SELECT * FROM posts WHERE postId IN (" + params.join(',') + ")", ids,
function(tx, results) {
deferred.resolve(results.rows);
},
function(tx, reason) {
deferred.reject(reason);
});
});
return deferred.promise;
}
My code is going to be slightly different from your as I've used WebSql cause I wanted to test it in the browser.
Now we need to find the ids which do not exist in the db:
function getNonExistingIds(ids, dbData) {
console.log('Checking if Ids ' + ids.toString() + ' exist in the db ...');
if (!ids || ids.length === 0) {
console.log('No ids');
return [];
}
if (!dbData || dbData.length === 0) {
console.log('database is empty');
return ids;
}
var dbIds = [];
angular.forEach(dbData, function(data, key) {
dbIds.push(data.postId);
});
var nonExisting = [];
angular.forEach(ids, function(id, key) {
var found = $filter('filter')(dbIds, id, true);
if (found.length === 0) {
nonExisting.push(id);
}
});
return nonExisting;
}
This function does not return a promise but you still can pipe it like you would do with a real promise (You'll find out how later).
Now we need to call the web api to fetch the posts for the ids which couldn't be found in the database:
function fetchNonExisting(ids) {
if (!ids || ids.length === 0) {
console.log('No posts to fetch!');
return;
}
console.log('Fetching non existing posts by id: ' + ids.toString() + ' ...');
var promises = [];
angular.forEach(ids, function(id, key) {
var promise = $http({
method: 'GET',
url: 'http://localhost/post/' + id,
params: {}
});
promises.push(promise);
});
return $q.all(promises);
}
Things here get interesting.
Since I want this function to return one and only result with an array of posts I've created an array of promises.
The $http service already returns a promise. I push it in an array.
At the end I try to resolve the array of promises with $q.all. Really cool!
Now we need to write the posts fetched in the database.
function writePosts(posts) {
if (!posts || posts.length === 0)
{
console.log('No posts to write to database!');
return false;
}
console.log('Writing posts ...');
var promises = [];
angular.forEach(posts, function(post, key) {
promises.push(writePost(post.data));
});
return $q.all(promises);
}
Again, we are chaining an array of promises so that we can resolve them all in one go.
This function up here calls writePost:
function writePost(post) {
return $q(function(resolve, reject) {
window.myDatabase.transaction(function(tx) {
tx.executeSql("INSERT INTO posts (postId, title, user, content) VALUES (?,?,?,?)", [post.id, post.title, post.user, post.content],
function(tx, result) {
console.log('INSERT result: ' + result);
resolve(result);
},
function(tx, reason) {
console.log('INSERT failure: ' + reason);
reject(reason);
});
});
});
}
this bit here is quite complicated cause WebSql doesn't work with promises and I want them to be resolve in one go and get the result back.
Now what can you do with all these functions? Well, you can chain them as I explained earlier:
var ids = [];
fetchIds()
.then(function(data) {
console.log(data);
ids = data;
return queryForIds(data);
})
.then(function(dbData) {
return getNonExistingIds(ids, dbData);
})
.then(function(nonExistingIds) {
console.log('Non existing ids: ' + nonExistingIds);
return fetchNonExisting(nonExistingIds);
})
.then(function(response) {
return writePosts(response);
})
.then(function(result) {
console.log('final result: ' + result);
})
.catch(function(reason) {
console.log('pipe error: ' + reason);
})
.finally(function() {
// Always Executed.
});
The final result can find found in this gist.
If you prefer to download the whole application and test it on your PC, this is the link (myApp).
var newservices = angular.module('newservices', []);
newservices.service('newservice', function ($http) {
return{
newdata: function(parameter){
return $http.get('/devicedetails/'+parameter).success(function(data) {
console.log(data)
return data
});
},
}
});
The above service is included in one of my controllers
data=newService.newdata($scope.dummy)
console.log(data)
while trying to print data what i get is $http function object as shown below
Object {then: function, catch: function, finally: function, success: function, error: function}
why is this so??
What you see is not an error. It's a Promise.
You did an $http GET request, which is asynchronous. $http.getreturns a promise that will be resolved when the remote request is completed. In that moment, you'll get the final value.
See this example, where getShops would be your method newData
this.getShop = function (id, lang) {
var promise = $http.get(appRoot + 'model/shops_' + lang + '.json');
return promise;
};
In a controller you can use it like this:
Shops.getShop($routeParams.id).then(function (response) {
console.log("data is", response.data);
$scope.shop = response.data[$routeParams.id];
});
When the data is ready, assign it to a scope.
In your case:
var data;
newService.newdata($scope.dummy).then(function (response) {
data = response.data;
});
Your service is returnig a promise
You should use some what like this, not tested though it should work.
data = newService.newdata($scope.dummy).then(function (response) {
return response.data;
},
function (error) {
return error;
});
You are using it wrong.
This work in promises. so in you controller you need to consume the promisses.
newService.newData($scope.dummy)
.then(function (data){
$scope.data = data;
console.log(data);
});
Try this.