I want to write a function in AngularJS that returns a value (actually it is a string). That value is returned by a http request, but async is driving me crazy.
My first attempt was:
this.readParameter = function(key) {
$http({
method: "GET",
url: "XXXXXXX",
headers: { 'Content-Type': 'application/json' }
}).then(function successCallback(response) {
return response.data;
}, function errorCallback(response) {
throw new Error("Error");
})
};
But of course it does not work because of Angular async features (response.data is undefined)
What is the way to do it? I just want to return the value (string), so I can use this function like
var a = readParameter("key1")
What you can do is define some variable with initial value outside function and on response set value inside success function instead of returning it.
Delegator pattern works great here to assign $http task to some service and use callback method for response.
Controller (Call Service for specific request) -> Service (Manage request params and other things and return factory response to Controller) -> Factory (Send request and return it to Service)
Basic example of Callback
var myVariable = '';
function myFunction (key, callback) {
$http({
method: "GET",
url: "XXXXXXX",
headers: { 'Content-Type': 'application/json' }
}).then(function successCallback(response) {
callback(response);
}, function errorCallback(response) {
throw new Error("Error");
})
};
function myCallbackFunction(response) {
myVariable = response.data; // assign value to variable
// Do some work after getting response
}
myFunction('MY_KEY', myCallbackFunction);
This is basic example to set value but instead use callback pattern from above example.
var myvariable = '';
function myFunction (key) {
$http({
method: "GET",
url: "XXXXXXX",
headers: { 'Content-Type': 'application/json' }
}).then(function successCallback(response) {
myvariable = response.data; // set data to myvariable
// Do something else on success response
}, function errorCallback(response) {
throw new Error("Error");
})
};
myFunction('MY_KEY');
Don't try to mix async and sync programming. Instead use a callback to use like
readParameter("key1", callback)
for example:
this.readParameter = function(key, callback) {
$http({
method: "GET",
url: "XXXXXXX",
headers: { 'Content-Type': 'application/json' }
}).then(function successCallback(response) {
callback(response)
}, function errorCallback(response) {
throw new Error("Error");
})
};
I resolve this by using promise:
Example :
in Service (invoicesAPIservice => invoicesapiservice.js) you use:
angular.module('app')
.service('invoicesAPIservice', function ($http) {
this.connectToAPI= function () {
return new Promise(function(resolve,reject){
var options = {
method:'GET',
url :'',
headers:{
'X-User-Agent': '....',
'Authorization': '....',
}
};
$http(options).then(function successCallback(response) {
resolve(response);
//console.log(response);
},function errorCallback(response) {
reject(response);
})
});
});
});
and in your Controller (mainCtrl=> mainCtrl.js):
angular.module('app').controller('mainCtrl', function($scope,invoicesAPIservice) {
$scope.connectToAPI=function () {
invoicesAPIservice.connectToAPI().then(function (content) {
console.log(content.statusText);
}.catch(function (err) {
//console.log(err);
alert("Server is Out");
});
}
});
And in your page : index.html:
<button ng-click="connectToAPI()"></button>
:)
Related
I have two functions,
function getRequest(url, api_key, callback) {
$.ajax({
url: url,
contentType: 'application/json',
type: 'get',
beforeSend: function(xhr){
xhr.setRequestHeader('Authorization', 'Bearer ' + api_key);
},
success: function(response) {
callback(response);
},
error: function(error) {
console.log(error);
document.getElementById("droplets").textContent = error;
}
});
}
function getDroplets(url, api_key) {
var request = getRequest(url, api_key, function(response) {
var droplets = response.droplets;
var numDroplets = droplets.length;
return {
droplets: droplets,
numDroplets: numDroplets
};
});
alert(request);
}
I want to have another function, let's call it listDroplets, that will call getDroplets() and manipulate the data returned from it. I'm not sure how to do this because getDroplets has an asynchronous call within it.
EDIT: I have tried the following, but it still doesn't work.
async function listDroplets() {
await getDroplets(api_url, api_key);
alert(request.numDroplets);
}
Here are how your functions could return promise like objects that you can use in an async await function:
function getRequest(url, api_key, callback) {
//to escape terrible jQuery Deferred and comfortably continue in Promise land
// you can do
// const deferred = $.ajax(...); return Promise.resolve(deferred)
return $.ajax({//return promise like object
url: url,
contentType: 'application/json',
type: 'get',
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', 'Bearer ' + api_key);
}
});
}
function getDroplets(url, api_key) {
return getRequest(url, api_key)//return promise like object
.then(function (response) {//
var droplets = response.droplets;
var numDroplets = droplets.length;
return {
droplets: droplets,
numDroplets: numDroplets
};
})
.catch(function (error) {//implement error if something goes wrong
console.log(error);
document.getElementById("droplets").textContent = error;
});
}
async function listDroplets() {
//depending on how old your jQuery is you may want to do this:
// await Promise.resolve(getDroplets(api_url, api_key));
const request = await getDroplets(api_url, api_key);
//note that if something goes wrong then request is undefined (depending on jQuery version)
alert(request.numDroplets);
}
I have a simple login, once user is logged in I have added a call back to run another post so that I have access to the post json to use in my system.
I think the way I have done it is correct however I am getting error
GetData is not defined
Is this the correct way to do this
JavaScript
$scope.LogIn = function () {
$http({
url: "http://www.somesite.co.uk/ccuploader/users/login",
method: "POST",
data: $.param({'username': $scope.UserName, 'password': $scope.PassWord}),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function (response) {
// success
console.log('success');
console.log("then : " + JSON.stringify(response));
GetData();
// location.href = '/cms/index.html';
}, function (response) { // optional
// failed
console.log('failed');
console.log(JSON.stringify(response));
});
};
$scope.UserData = function ($scope) {
$scope.UserName = "";
$scope.PassWord = "";
};
$scope.GetData = function () {
$http({
url: " http://www.somesite.co.uk/ccuploader/campaigns/getCampaign",
method: "POST",
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function (response) {
// success
console.log('you have received the data ');
console.log("then : " + JSON.stringify(response));
location.href = '/cms/index.html';
}, function (response) { // optional
// failed
console.log('failed');
console.log(JSON.stringify(response));
});
};
You need to update your code to be $scope.GetData();.
Currently you are using GetData() which doesn't reference the same method. In fact it is undefined as per the error message.
I know similar questions have been asked before but none of the examples make sense to me. (Any I have found have failed to explain the basics for me with the clarity I need.)
I have four AngularJS functions. Each calls a REST service and does stuff that is unrelated to any of the other functions. E.g.
$scope.step1 = function() { $http({
method: 'GET',
url: "http://www/end/point",
cache: true,
headers: { "Accept": "application/jsonp;odata=verbose" }
}).success(function (data, status, headers, config) {
$scope.step1data = data;
}).error(function (data, status, headers, config) {
$scope.logError(data);
});};
I would like to call the four functions in sequence
$scope.step1
$scope.step2
$scope.step3
$scope.step4
And catch any errors encountered.
I have narrowed the code to the below but it does not work for me. Any help would be greatly appreciated.
$scope.step1().then(function() {
$scope.step2();
}).then(function() {
$scope.step3();
}).then(function() {
$scope.step4();
}).catch(function(e) {
console.log(e);
});
You need to return the promise from each step function to your then() callback so that its resulting promise waits for that promise:
$scope.step1().then(function() {
return $scope.step2();
})...
First, change your step1, step2, step3 and step4 to return promises like below:
$scope.step1 = function() {
return $http({
method: 'GET',
url: "http://www/end/point",
cache: true,
headers: { "Accept": "application/jsonp;odata=verbose" }
})};
$scope.step2 = function() {
return $http({
method: 'GET',
url: "http://www/end/point",
cache: true,
headers: { "Accept": "application/jsonp;odata=verbose" }
})};
$scope.step3 = function() {
return $http({
method: 'GET',
url: "http://www/end/point",
cache: true,
headers: { "Accept": "application/jsonp;odata=verbose" }
})};
$scope.step4 = function() {
return $http({
method: 'GET',
url: "http://www/end/point",
cache: true,
headers: { "Accept": "application/jsonp;odata=verbose" }
})};
Then, chain them together like this:
$scope.step1().then(function (step1data) {
$scope.step1data = step1data;
$scope.step2().then(function (step2Data) {
$scope.step2Data = step2Data;
$scope.step3().then(function (step3Data) {
$scope.step3Data = step3Data;
$scope.step4().then(function (step4Data) {
$scope.step4Data = step4Data;
});
});
});
})
You can mix asynchronous functions with any kind of logic that JavaScript offers if you execute your code synchronously via nsynjs. Here is how your code may need to be transformed:
Step 1: Wrap asynchronous function into generic nsynjs-aware wrapper:
// generic function to retrieve url
// ctx is a reference to caller pseudo-thread context
var httpUrl = function(ctx,url,$http) {
var res={};
$http({
method: 'GET',
url: url,
cache: true,
headers: { "Accept": "application/jsonp;odata=verbose" }
})
.success(function (data, status, headers, config) {
res.data = data;
ctx.resume() // tells nsynjs to resume caller function
}).error(function (data, status, headers, config) {
ctx.resume(data) // resume caller function with exception
// or replace with these 2 lines if you don't want exception
// res.error = data;
// ctx.resume()
});
return res;
};
getUrl.nsynjsHasCallback = true; // this tells nsynjs engine that it
// should pause execution and wait until ctx.resume() is called from callback
Step 2. Write your logic as if it was synchronous, and put it into function:
function synchronousCode(param1, param2) {
var step1 = function() {
var data = httpUrl(nsynjsCtx,"nsynjs/examples/data/file1.json").data;
console.log("data is ready at this point:",data);
// do staff this data
return data;
};
var step2 = function() {
var data = httpUrl(nsynjsCtx,"nsynjs/examples/data/file2.json").data;
console.log("data is ready at this point:",data);
// do staff this data
return data;
};
console.log( step1(param1) + step2(param2) ); // do more staff with data
}
Step 3. Run your synchronous code via nsynjs:
nsynjs.run(synchronousCode,{},"value for param1","value for param1",function(){
console.log("Synchronous Code done");
})
More examples here: https://github.com/amaksr/nsynjs/tree/master/examples
You need to use promise, in your controller, you should inject $q which will handle promise.
angularApp.controller('YourController', ['$q', '$scope',
function ($q, $scope) {
$scope.firstFunction = function () {
var deferred = $q.defer();
//Do things you want here...
console.log(1);
deferred.resolve();
return deferred.promise;
};
$scope.secondFunction = function () {
var deferred = $q.defer();
//Do things you want here...
console.log(2);
deferred.resolve();
return deferred.promise;
};
$scope.firstFunction().then(function () {
$scope.secondFunction.then(function () {
console.log(3)
})
})
}]);
console logs in functions above will print out:
1
2
3
I was trying a lot of aproaches, some of them:
$http({
method: 'GET',
url: '/Url'}).then(function successCallback(response) {
}, function errorCallback(response) {
});
var req = {
method: 'GET',
interceptor: {
response: function (response) {
console.log(response)
var result = response.resource;
result.$status = response.status;
return result;
}
},
url: url,
headers: {
'Authorization': token
}
}
$http(req).success(function (data, status, headers, config) {
console.log(data, status, headers, config);
}).error(function (data, status, headers, config) {
console.log(data, status, headers, config);
});
And a lot of others with no result, so I really need help !!!
I would grateful for any help
The first approach looks close to what you need.
function successCallback(response) {
console.log('status code: ', response.status);
}
Currently the callback is empty so we are accessing response object and logging status code from it.
The first way is the more direct way of doing but I'm pretty sure that the response functions should be anonymous. Putting a console.log() in both will help.
$http({
method: 'GET',
url: '/Url'
}).then(function(response) {
console.log(response.status);
}, function(response) {
console.log(response.status);
});
If that doesn't work just put the console.log(response) in both to see what it gets you.
You can use httpinterceptor if you want to track all your requests :
myModule.factory('myHttpInterceptor', function ($q) {
return {
response: function (response) {
// do something with success response
// status in response.status
return response;
},
responseError: function (response) {
// do something with error response
// status in response.status
return $q.reject(response);
}
};
});
myModule.config(function ($httpProvider) {
$httpProvider.interceptors.push('myHttpInterceptor');
});
I've built an API using Slim Framework.
One of my routes in Slim accept optional parameters.
Is there any way I can do this using angularjs $http.get. How can I set optional parameters in my request.
Below is my code;
$scope.getRestaurant = function () {
return $http({
method: 'get',
url: "http://api.example.co.uk/web/restaurant/details/" + $scope.id + "/" + $scope.u,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
return response.data;
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
return [];
});
}
As you can see I have $scope.id and $scope.u. I will like $scope.u to be optional. At the moment it is always passed even when it is null.
Just add it to the url if it's truthy.
var baseUrl = 'http://api.example.co.uk/web/restaurant/details/' + $scope.id;
if($scope.u) baseUrl += '/' + $scope.u;
//edited
You can use a ternary test like bellow:
$scope.getRestaurant = function () {
return $http({
method: 'get',
url: "http://api.example.co.uk/web/restaurant/details/" + $scope.id +
( $scope.u != null ) ? "/"+$scope.u : "" ,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
return response.data;
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
return [];
});
}
or create your url before you make your return
$scope.getRestaurant = function () {
var url = "http://api.example.co.uk/web/restaurant/details/" + $scope.id +
( $scope.u != null ) ? "/" +$scope.u : "" ;
return $http({
method: 'get',
url: url,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
return response.data;
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
return [];
});
}