Return request response in angularJs - javascript

I've setup a controller and service to grab some JSON from my own NodeJS API/MongoDB endpoint.
In my browser console I see a returned object and 5 items and when I query my api I see the same json in the browser, so I know it's working on the server side.
I'm confused though about why when attempting to NG-Repeat through this I get nothing on page and when I console log out the returned data, it comes back undefined.
I'm new to the HTTP module and I'm trying to refactor out DB calls to a service instead of using it in controller.
--- Controller code ---
vm.getDepartments = function() {
vm.departments = DbService.getAllDepartments();
console.log(vm.departments);
}();
--- Service Code (using $http) ---
function getAllDepartments() {
$http.get('/api/departments').then(function(err, response) {
if (err) {
console.log(err);
}
return response;
});
};
--- html page ---
<tbody>
<tr ng-repeat='dept in vm.departments'>
<td>{{ dept.departmentLong }}</td>
<td>{{ dept.departmentShort }}</td>
<td>
<button class='btn btn-warning'><span class='glyphicon glyphicon-edit'></span></button>
<button class='btn btn-danger' ng-click='vm.deleteDepartment(dept);'><span class='glyphicon glyphicon-ban-circle'></span></button>
</td>
</tr>
</tbody>

You used wrong then method.
The then() method takes two arguments: a success and an error callback which will be called with a response object.
Using the then() method, attach a callback function to the returned promise.
Have a look to an old answer posted by mine.
$http.get('/api/departments') returns a promise, so you can create a function which returns only the promise, as #sachila ranawaka mentioned.
function getAllDepartments() {
return $http.get('/api/departments')
};
In the controller use then method which I mentioned above.
DbService.getAllDepartments().then(function(response) {
vm.departments = response.data;
console.log(vm.departments);
},function(error){
console.log(err);
});
Another method is to create a callback function, and pass it to getAllDepartments method.
function getAllDepartments(callback) {
$http.get('/api/departments').then(function(response) {
callback(response);
});
};
Controller code:
vm.getDepartments = function() {
vm.departments = DbService.getAllDepartments(function(result){
console.log(result);
});
}();

in the service just return the http request
function getAllDepartments() {
return $http.get('/api/departments')
};
And catch the promise inside the controller. use response.data to get the data
vm.getDepartments = function() {
DbService.getAllDepartments().then(function(response) {
vm.departments = response.data;
console.log(vm.departments);
});
}

Related

working with deferred results in angularjs

Can't seem to get my head over the concept of 'promise' in AngularJS, I am using the restangular library to fetch a resource over REST, however I always get null results. Here's the code
.service('CareersService', [ 'Restangular', '$sce', function(Restangular, $sce){
var vacancies = [];
var result;
this.getVacancies = function() {
Restangular.all('job_posts').getList({active: 'true'}).then(function(job_posts){
job_posts.forEach(function(job_post){
vacancies.push(_.pick(job_post,['id','title','min_experience','max_experience','location']));
})
})
return vacancies;
}
this.getVacancy = function(job_id){
Restangular.one('job_posts',job_id).get().then(function(job_post){
result = _.pick(job_post, 'title','min_experience','max_experience','location','employment_type','description');
var safe_description = $sce.trustAsHtml(result.description);
var emp_type = _.capitalize(result.employment_type);
_.set(result, 'description', safe_description);
_.set(result, 'employment_type', emp_type);
});
return result;
}
}]).controller('DetailsCtrl', ['$scope' ,'$stateParams', 'CareersService' ,function($scope, $stateParams, CareersService) {
$scope.data.vacancy = { title: 'Loading ...', contents: '' };
$scope.data.vacancy = CareersService.getVacancy($stateParams.job_id);
}])
and then in view
<div class="container">
<a ui-sref="careers" class="btn btn-primary">Show All</a>
<div class="row">
<h2>{{ data.vacancy.title }}</h2>
<p>{{ data.vacancy.min_experience }}</p>
<p>{{ data.vacancy.max_experience }}</p>
<p>{{ data.vacancy.location }}</p>
<p>{{ data.vacancy.employment_type }}</p>
<p ng-bind-html="data.vacancy.description"></p>
</div>
</div>
Am I missing something in the way to use promises?
Update
here's the updated code thanks to all the help I got here,
this.getVacancies = function() {
Restangular.all('job_posts').getList({active: 'true'}).then(function(job_posts){
job_posts.forEach(function(job_post){
vacancies.push(_.pick(job_post,['id','title','min_experience','max_experience','location']));
})
return vacancies;
})
}
this.getVacancy = function(job_id){
Restangular.one('job_posts',job_id).get().then(function(job_post){
vacancy = _.pick(job_post, 'title','min_experience','max_experience','location','employment_type','description');
...
return vacancy;
});
}
}])
And in controllers
CareersService.getVacancy($stateParams.job_id).then(function (vacancy){
$scope.data.vacancy = vacancy;
});
and
CareersService.getVacancies().then(function (vacancies){
$scope.data.vacancies = vacancies;
});
I now get the error
Cannot read property 'then' of undefined
At the line
CareersService.getVacancies().then(function(vacancies) {
Restangular makes an API call over a http, and once it make a call it returns underlying promise object. And inside .then function of it you can get the data responded by API.
So here you are making an async call and considering it to happen it in synchronous way like you can see you had returned result/vacancies array from Restangular call, in that way result/vacancies is always going to be empty.
In such you should return a promise from a service method. And return appropriate formatted data from promise so that you can chain that promise in controller as well(by retrieving a data).
Service
this.getVacancies = function() {
//returned Restangular promise
return Restangular.all('job_posts').getList({
active: 'true'
}).then(function(job_posts) {
job_posts.forEach(function(job_post) {
vacancies.push(_.pick(job_post, ['id', 'title', 'min_experience', 'max_experience', 'location']));
});
//return calculated result
return vacancies;
})
}
this.getVacancy = function(job_id) {
//returned Restangular promise
return Restangular.one('job_posts', job_id).get().then(function(job_post) {
result = _.pick(job_post, 'title', 'min_experience', 'max_experience', 'location', 'employment_type', 'description');
var safe_description = $sce.trustAsHtml(result.description);
var emp_type = _.capitalize(result.employment_type);
_.set(result, 'description', safe_description);
_.set(result, 'employment_type', emp_type);
//returned result to chain promise
return result;
});
}
As I said now you can easily chain promise inside controller by having .then function over service method call.
CareersService.getVacancy($stateParams.job_id).then(function(result){
$scope.data.vacancy = result;
});
Update
The syntax without .then would work, but you need to make small change in it by adding .$object after a method call.
$scope.data.vacancy = CareersService.getVacancy($stateParams.job_id).$object;
$object is property which added inside promise object by Restangular. While making an API call, at that time it makes $scope.data.vacancy value as a blank array ([]) and once server respond with response, it fills that object with response received by server. Behind the scene it only updates the value of $object property which automatically update $scope.data.vacancy value.
Same behaviour is there in $resource of ngResource.
I wanted to also put down that when you're chaining promise, that time you have to explicitly handle error case. Whereas in current code you haven't handle such failure condition. So I'd suggest you to go for that as well by adding error function inside Restangular REST API call. and do use $q.reject('My error data, this can be object as well').
You are making a async call to get the result. So you need either a callback or promise to handle this. One option with minimum code change is to make the service to return promise and in the controller get the result via then
.service('CareersService', ['Restangular', '$sce', function(Restangular, $sce) {
var vacancies = [];
var result;
this.getVacancies = function() {
Restangular.all('job_posts').getList({
active: 'true'
}).then(function(job_posts) {
job_posts.forEach(function(job_post) {
vacancies.push(_.pick(job_post, ['id', 'title', 'min_experience', 'max_experience', 'location']));
})
})
return vacancies;
}
this.getVacancy = function(job_id) {
return Restangular.one('job_posts', job_id).get().then(function(job_post) {
result = _.pick(job_post, 'title', 'min_experience', 'max_experience', 'location', 'employment_type', 'description');
var safe_description = $sce.trustAsHtml(result.description);
var emp_type = _.capitalize(result.employment_type);
_.set(result, 'description', safe_description);
_.set(result, 'employment_type', emp_type);
return result;
});
}
}]).controller('DetailsCtrl', ['$scope', '$stateParams', 'CareersService', function($scope, $stateParams, CareersService) {
$scope.data.vacancy = {
title: 'Loading ...',
contents: ''
};
CareersService.getVacancy($stateParams.job_id).then(function(result) {
$scope.data.vacancy = result;
});
}])
You are doing return outside of then try to move it inside in then function
this.getVacancies = function() {
Restangular.all('job_posts').getList({active: 'true'}).then(function(job_posts){
job_posts.forEach(function(job_post){
vacancies.push(_.pick(job_post,['id','title','min_experience','max_experience','location']));
})
return vacancies;
})
}

Meteor js | Display Json in view via helper

Im struggling with an issue using Meteor JS.
I call an api wich return me a Json array wich look like the one returned on this url (I don't put the whole array here cause of the size): https://blockchain.info/address/12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX?format=json&offset=0
I call it server side like :
if (Meteor.isServer) {
Meteor.methods({
getWalletPreviousTx: function() {
var url = "https://blockchain.info/address/12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX?format=json&offset=0";
var result = Meteor.http.get(url);
if(result.statusCode==200) {
var tx = JSON.parse(result.content);
return tx;
} else {
console.log("Response issue: ", result.statusCode);
var errorJson = JSON.parse(result.content);
throwError("Couldn't fetch wallet balance from Blockchain, try again later !");
}
}
});
}
And i retrieve it to my view via an helper in a specific template :
Template.wallet.helpers({
addrTxs: function () {
Meteor.call('getWalletPreviousTx', function(err, tx) {
console.log(tx);
return [tx];
});
}
});
The console.log in the helper actually log my Json array wich mean it have access to it.
Now the part im struggling with is to retrieve this Json to my view, i've tried a lot of way and none of them works, actually i have this in my view :
<template name="wallet">
<table>
{{#each addrTxs}}
<ul>
{{> addrTx}}
</ul>
{{/each }}
</table>
</template>
The part of the Json I want to display is the "addr" and "value" of each transactions :
"inputs":[
{
"sequence":4294967295,
"prev_out":{
"spent":true,
"tx_index":97744124,
"type":0,
"addr":"1AWAsn8rhT555RmbMDXXqzrCscPJ5is5ja",
"value":50000,
"n":0,
"script":"76a914683d704735fd591ba9f9aebef27c6ef00cbd857188ac"
}
}
]
Fact is, i never managed to display anything from this Json array in my view, even puting directly this in my view doesn't show anything :
{{addrTxs}}
What am I doing wrong ? Can anyone help with this ?
Thanks for reading.
----------------------- Edit ---------------------
I think the problem is more that my helper and template are loaded before the api call is finished (because the console.log appear in my console like 3seconds after my page is rendered). How can i make my helper wait until the api call is finished before rendering it in the view ? I use iron router.
I have tried to add a waitOn action on my route in order to wait until my api call is finished :
Router.route('/wallet', {
name: 'wallet',
template: 'wallet',
loadingTemplate: 'loading',
waitOn: function () {
Meteor.call('getWalletPreviousTx', function(error, result) {
if(!error) {
Ready.set(result)
}
});
return [
function () { return Ready.get(); }
];
},
action: function () {
if (this.ready())
this.render();
else
this.render('loading');
}
});
The above code with the waitOn action seems to work (i have no errors) but i don't know the way to display in my view the specific result from :
if(!error) {
Ready.set(result)
}
Transactions are contained in tx.txs, iterates through that.
Template.wallet.helpers({
addrTxs: function () {
Meteor.call('getWalletPreviousTx', function(err, tx) {
console.log(tx);
return tx.txs;
});
}
});
You're right, you need to use the sessions variables with async call.
First, call method on created :
Template.wallet.created = function () {
Meteor.call('getWalletPreviousTx', function(err, tx) {
console.log(tx.txs);
Session.set('tx', tx.txs);
});
};
Helper should look like this :
Template.wallet.helpers({
addrTxs: function () {
return Session.get('tx');
}
});

Model not updating when using a service with $http

I have an idea why it's not working, but not exactly how to fix this, and I've looked at the various similar questions/answers on SO but I can not fix my issue.
I am trying to call a service on page load to list a table of data, or let's just call this a list of urls.
The model would simply be { urls } would should return an array of urls, where in the future I'd use ng-repeat.
My controller:
function UrlListCtrl($scope, UrlService){
$scope.urls = UrlService.getUrls();
}
Service:
function UrlService($http) {
return {
getUrls: getUrls
}
function getUrls() {
console.log('Fetching Urls...');
return $http.get('urls/get')
.then(function(response) {
console.log('response:', response.data);
this.urlsResults = response.data;
});
}
}
The service does run, however it doesn't update the view. How can I fix this?
$http.get() is an async operation that returns a promise. You have to wait for this promise to be fulfilled, and then you can get the data from the response.
In your code $scope.urls = UrlService.getUrls(); was assigning the promise, and not it's results to $scope.urls, and the view was never updated when the operation ended.
Controller:
function UrlListCtrl($scope, UrlService){
UrlService.getUrls().then(function(response) {
$scope.urls = response.data;
});
}
Service:
function UrlService($http) {
return {
getUrls: getUrls
}
function getUrls() {
console.log('Fetching Urls...');
return $http.get('urls/get');
}
}

angular.js ui + bootstrap typeahead + asynchronous call

I'm using typeahead with the angular.js directive but my function to populate the autocomplete makes an asynchronous call and I can't return it to populate the autocomplete. Is there anyway to make it work with this asynchronous call?
Can I assume that you are using the typeahead of Bootstrap 2.x ?
If so, in the documentation, the description of the source field of typeahead()'s options is this:
The data source to query against. May be an array of strings or a
function. The function is passed two arguments, the query value in the
input field and the process callback. The function may be used
synchronously by returning the data source directly or asynchronously
via the process callback's single argument.
You can definitely pass in an async function as the source attr. The source function could be something like:
function someFunction(query, process) {
someAsyncCall(...query or so... , function(res) { // success callback
process(res);
}, function(err) { // error callback
process(null);
});
}
Update:
If you are using Angular Bootstrap's typeahead, it should be even easier. According to Angular Bootstrap's docs(http://angular-ui.github.io/bootstrap/), you can just return a promise for the typeahead function. Some example from the docs:
$scope.getLocation = function(val) {
return $http.get('http://maps.googleapis.com/maps/api/geocode/json', {
params: {
address: val,
sensor: false
}
}).then(function(res){
var addresses = [];
angular.forEach(res.data.results, function(item){
addresses.push(item.formatted_address);
});
return addresses;
});
};
A simpler one could be:
$scope.getSomething= function(query) {
var promise = $http.get('...some url...', {
params: {
queryName: query
}
});
return promise;
};
Or you can build your own promise:
$scope.getSomething= function(query) {
var deferred = $q.defer();
someAsyncCall(...query or so... , function(res) { // success callback
deferred.resolve(res);
}, function(err) { // error callback
deferred.reject(err);
});
return deferred.promise;
};
Actually, many services like $http are just returning promises when you call them.
More about promise in AngularJS: https://docs.angularjs.org/api/ng/service/$q

Angularjs filter error from services data in the http url

i have a django method that calls a service as follows. Here is the method in the controller:
$scope.showDegrees = function(degree){
debugger
$scope.DegreeCategory = degree.DegreeCategory;
$scope.filterSub = degree.DegreeCategoryID;
DegreesService.getDegrees(function(filterSub, data){
$scope.degrees = data;
console.log(data);
});
$scope.display.academicdegrees = true;
};
and in my services, i defined my factory as follows:
.factory('DegreesService',function($http) {
return {
getDegrees: function(degreecategoryid, callback) {
//$http.get('/api/academicprogram/?format=json').success(callback);
$http.get('/api/academicprogramlist2/'+degreecategoryid+'/').success(callback);
}
};
})
When i am running it in chrome, the url is being passed wrong. It gives me the following error:
GET http://127.0.0.1:8000/api/academicprogramlist2/function%20(filterSub,%20data)%7B%20%20%20%20$scope.degrees%20%20=%20data;console.log(data);%20%20%7D/ 404 (NOT FOUND)
Where am i going wrong?
You should call DegreesService.getDegrees() like this:
DegreesService.getDegrees(degree.DegreeCategoryID, function(response){...})
BTW, $http.get() returns a promise. DO NOT use callbacks if you have promises!

Categories

Resources