Angular async data checking issue - http

My service looks like
//this mthod under myService
this.checkCookie = this.getAuthorization = function() {
return $http({
method: 'GET',
url: '/api/auth'
});
}
And in my route configuration I am doing like
MyAPP.config(function($routeProvider) {
$routeProvider.
when('/', {
controller: check
}).
when('/login', {
templateUrl: '/partials/login.html',
controller: check
}).
when('/products', {
templateUrl: '/partials/products.html'
})
});
var check = function($location, myService, $q) {
if (myService.checkCookie()) {
$location.path("/products");
} else {
$location.path("/login");
}
};
with get request I want to check session data generated by the server is valid or not. And browser will send the cookie information while sending 'GET' in '/api/auth'.
The problem is when I am calling this.checkCookie I am not getting the response syncronoulsy as angular returns response in asnyc fashion. Depending on the checkCookie response I am suppose to redirect to '/products' but I cant do that now.
How can I do that? What I need to change to get this.checkCookie and check whether the response status is 200 or 500?

You can't do synchronous requests with $http. To handle the promise return by the request, you can do this:
var check = function($location, myService, $q) {
myService.checkCookie()
.success(function() {
$location.path("/products");
})
.error(function() {
$location.path("/login");
})
};

You have to call then on the promise that's returned from $http:
myService.checkCookie().then(function () {
$location.path("/products");
}, function () {
$location.path("/login");
});
The first function is the success handler, and the second one is the error (reject) handler.

Related

Meteor methods returning undefined on client even after using callback

I am trying to get data from AirVisual API using meteor methods on the server and passing it to the client. The data is successfully received on the server. However, the template helper gets undefined when the method is called in it.
Client Helper:
Template.index.helpers({
getCityDataOnClient: function(city, state) {
Meteor.call('getCityData', city.toLowerCase(), state.toLowerCase(), function(error, result) {
if(!error) {
console.log(result); //returns undefined
}
else {
console.log(error);
}
});
}
});
Meteor methods.js in lib folder:
Meteor.methods({
getCityData : function(city, state) {
var data = [];
const result = HTTP.call('GET', 'http://api.airvisual.com/v2/city', {
params: {
state: state,
city : city,
country: 'pakistan',
key: 'xxxxxxxxxx'
}
}, function(err, res) {
if (!err) {
data = res.data.data;
//console.log(data); //prints correct data on the server and client
return data;
}
else {
console.log(err);
return err;
}
});
}
});
I have already looked up answers on similar questions. Nothing seems to work, including Tracker, reactive-var, and reactive-methods.
The problem here is that you are trying to return data from inside a callback to a function that 1. isn't waiting for you, and 2. has already returned.
Thankfully, Meteor does some magic on the server to make asynchronous calls like HTTP.call appear synchronous.
Your method can be done like so:
Meteor.methods({
getCityData : function(city, state) {
const result = HTTP.call('GET', 'http://api.airvisual.com/v2/city', {
params: {
state: state,
city : city,
country: 'pakistan',
key: 'xxxxxxxxxx'
}
});
return result.data.data;
}
});
By excluding the callback on Meteor's HTTP module, Meteor will run it in a Fiber and wait for the result before continuing execution (like with async/await)
If you were using a third party library for HTTP requests, you would need to wrap the function using Meteor.wrapAsync to get the benefit of running in a fiber. Or you could wrap it in a promise and return the promise from the method

Meteor HTTP.POST call on same machine (for testing)

I have created a server side route (using iron-router). Code is as follows :
Router.route( "/apiCall/:username", function(){
var id = this.params.username;
},{ where: "server" } )
.post( function(req, res) {
// If a POST request is made, create the user's profile.
//check for legit request
console.log('post detected')
var userId = Meteor.users.findOne({username : id})._id;
})
.delete( function() {
// If a DELETE request is made, delete the user's profile.
});
This app is running on port 3000 on my local. Now I have created another dummy app running on port 5000. Frrom the dummy app, I am firing a http.post request and then listening it on the app on 3000 port. I fire the http.post request via dummy app using the below code :
apiTest : function(){
console.log('apiTest called')
HTTP.post("http://192.168.1.5:3000/apiCall/testUser", {
data: [
{
"name" : "test"
}
]
}, function (err, res) {
if(!err)
console.log("succesfully posted"); // 4
else
console.log('err',err)
});
return true;
}
But I get the following error on the callback :
err { [Error: socket hang up] code: 'ECONNRESET' }
Not able to figure out whats the problem here.
The server side route is successfully called, but the .post() method is not being entered.
Using meteor version 1.6
192.168.1.5 is my ip addr
Okay so if I use Router.map function, the issue is resolved.
Router.map(function () {
this.route("apiRoute", {path: "/apiCall/:username",
where: "server",
action: function(){
// console.log('------------------------------');
// console.log('apiRoute');
// console.log((this.params));
// console.log(this.request.body);
var id = this.params.username;
this.response.writeHead(200, {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
});
if (this.request.method == 'POST') {
// console.log('POST');
var user = Meteor.users.findOne({username : id});
// console.log(user)
if(!user){
return 'no user found'
}
else{
var userId = user._id;
}
}
});
});
It looks like the content type is not set the application/json. So you should do that...
Setting the "Content-Type" header in HTTP.call on client side in Meteor

Angular2 - How to chain async service calls (http requests) in a component?

I have a component which first need to call a service that POST something. Then in the same component I want to wait until the POST is done, to call another service which GETs data.
How can I make the GET call wait for the POST call to finish?
In new-version.component.ts:
private createNewVersion(value) {
...
// create new version, then call on all available versions
// POST call
this._newVersionService.createNewVersion(vnr);
// GET call
this._versionService.getAvailableVersions();
...
}
In new-version.service.ts:
export class NewVersionService {
response$: Subject<any>;
constructor(private _http: Http) {
this.response$ = new BehaviorSubject<any>(null);
}
public createNewVersion(versionNr) {
this._http.post('http://localhost:8080/services/' + versionNr, null, {
method: 'POST',
})
.subscribe(response => {
this.response$.next(response.status);
},
error => console.error(error));
}
Thanks!
When a call returns a Promise chain the calls with
someFunction() {
return returnsPromise()
.then(result => doSomethingNext())
.then(result => doSomethingAfterThat());
}
Ensure you have a return that returns the Promise of that chain so the caller of someFunc() also has a chance to time additional work to execute after doSomethingAfterThat() is completed.
When a call returns an Observable then use the complete callback
someFunction() {
return returnsObservable()
.subscribe(
event => doForEachEvent(),
error => handleError(),
() => doSomethingNext()
.then(result => doSomethingAfterThat());
}
doSomethingNext() is executed after the last event and doSomethingAfterThat() is again chained with then() to show how to mix observable and promise. doSomething().
You should be able to concat to achieve sequence, and reduce to collect the emitted values:
var a = this._newVersionService.createNewVersion(vnr);
var b = this._versionService.getAvailableVersions();
Rx.Observable.concat(a, b).reduce((acc:Array<any>, x:any) => {
acc.push(x); return acc;
}, []).subscribe(t=> {
var firstEmitted = t[0];
var secondEmitted = t[1];
});
You can do like this:
Change createNewVersion to:
public createNewVersion(versionNr) {
return this._http.post('http://localhost:8080/nod_inspection_plugin/services/' + versionNr, null, {
method: 'POST',
});
}
Then in your call:
this._newVersionService.createNewVersion(vnr).subscribe(response=> {
this._versionService.getAvailableVersions();
}, error => console.error(error));
Another way to do the same is to subscribe in the new-version.component.ts and call you GET request from within the POST request i.e check whether your POST request is done Correctly or not
if yes POST is done Properly then call you GET request. As below:
In new-version.component.ts:
private createNewVersion(value) {
...
// create new version, then call on all available versions
// POST call
this._newVersionService.createNewVersion(vnr)
.subscribe((res) => {
if(res){
console.log(res);
if (---Post request done properly check via status or something else here----{
CALL YOUR GET REQUEST HERE.....
// GET call
this._versionService.getAvailableVersions();
}
else {
DO something else whatever you want....
}
}
});
...
}
In new-version.service.ts:
export class NewVersionService {
response$: Subject<any>;
constructor(private _http: Http) {
this.response$ = new BehaviorSubject<any>(null);
}
public createNewVersion(versionNr) {
this._http.post('http://localhost:8080/nod_inspection_plugin/services/' + versionNr, null, {
method: 'POST',
})
.map(response => {
return [{status: response.status, json: response.json()}];
},
error => console.error(error));
}
for more info related to http request you can read here.
Better use switchMap() here.
const versions$ = this._newVersionService.createNewVersion(vnr)
.switchMap(response => this._versionService.getAvailableVersions());
versions$.subscribe(response2 => this.versions = response2)
But the problem will be if you make another POST request before first has been resolved, the previous request will get cancelled.

Extracting data out of http call [duplicate]

I'm using Meteor for first time and i'm trying to have a simple http call within a method so i can call this method from the client.
The problem is that this async call it's keep running even if i put it within a wrapper.
Client side:
Meteor.call('getToken', function(error, results) {
console.log('entered');
if(error) {
console.log(error);
} else {
console.log(results);
}
});
Server Side
Meteor.methods({
getToken: function(){
// App url
var appUrl = 'myAppUrl';
// Key credentials
var apiKey = 'mykey';
var apiSecret = 'mySecret';
function asyncCall(){
Meteor.http.call(
'POST',
appUrl,
{
data: {
key: apiKey,
secret: apiSecret
}
}, function (err, res) {
if(err){
return err;
} else {
return res;
}
}
);
}
var syncCall = Meteor.wrapAsync(asyncCall);
// now you can return the result to client.
return syncCall;
}
});
I'm always getting an undefined return.
If i log the response within the http.post call i'm geting the correct response.
If i try to log the syncCall i get nothing.
I would very appreciate any help on this.
You should use the synchronous version of HTTP.post in this case. Give something like this a try:
Meteor.methods({
getToken: function() {
var appUrl = 'myAppUrl';
var data = {apiKey: 'mykey', apiSecret: 'mySecret'};
try {
var result = HTTP.post(appUrl, {data: data});
return result;
} catch (err) {
return err;
}
}
});
Instead of returning the err I'd recommend determining what kind of error was thrown and then just throw new Meteor.Error(...) so the client can see the error as its first callback argument.

HTTP post call from Template.event to a route in iron:router + meteor

I want to make HTTP.call with post parameters from Template.event in meteor. I have defined the route in iron:router route of my current application.
The route is getting the call but I am not able to get the post parameters. The route is a server side route and returns the pdf content using :
Template.eStatement.events({
'click .pdf': function (event, template){
event.preventDefault();
param = Some json object that I need to pass as post parameter.
HTTP.call("POST", '/statement', JSON.stringify(param),
function(error, result){ if(result){ // } if(error){ // } //done(); }); }});
This is my route in (I am using iron:route package for meteor)
Router.route('/statement', function () {
var param = JSON.parse(this.params.query.param);
/** Get the pdf content by calling the api
/** Write the content back :
this.response.writeHeader('200', {
'Content-Type': 'text/html',
'Content-Disposition': "inline",
});
this.response.write('pdfcontent');
this.response.end(); },{where: 'server'}
Try something like this instead:
On the client: (within the client/ folder)
Template.eStatement.events({
'click .pdf': function (event, template) {
var params = {
something: 'abcdef',
someOption: true
};
HTTP.call('POST', '/statement', {
data: params
}, function (error, result) {
console.log('http callback');
console.log(error);
console.log(result);
});
}
});
On the server: (within the server/ folder)
Router.route('/statement', {
where: 'server',
action: function () {
var params = this.request.body;
// do something with params
this.response.writeHeader('200', {
'Content-Type': 'text/html',
'Content-Disposition': "inline"
});
this.response.write('pdfcontent');
this.response.end();
}
});
And keep in mind that, in the route, this.request.body is an object in this case, not a string. So you don't need to use JSON.stringify and JSON.parse to handle that.

Resources