AngularJS Resource not Resolved - javascript

I am new to AngularJS, so please be gentle.
I want to retrieve data from my backend using the $resource property of AngularJS, however it seems that the property is getting loaded before the actual call is completed.
Currently I have the following code:
Controller:
MyApp.controller('IndexCtrl', function IndexCtrl($scope, socket, carsRes) {
console.log(carsRes.cars.get());
$scope.cars = carsRes.cars.get();
});
Factory
.factory('carsRes', function($resource) {
var result = {};
result.cars = $resource('/cars/:id', {}, {
'get': {method: 'GET'},
'save': {method: 'POST'},
'query': {method: 'GET', isArray: true},
'remove': {method: 'DELETE'},
'delete': {method: 'DELETE'}
});
return result;
});
But at the point where I want to store carsRes.cars.get() in $scope.cars the call isn't completed yet, and a console.log tells me that the $resolved is still false.
How can I wait for the call to be resolved? I've read something about $q but it isn't really clear to me.

This is happening because resource.get() is asynchronous. It returns an empty reference immediately, and is then populated with the actual data once the ajax call is complete.
The best way to handle the result is with a callback function:
carsRes.cars.get(function(response){
// We now have a completed ajax call, with the response
$scope.cars = response;
});
See more examples in the $resource documentation.

Related

returned object is undefined in a custom ajax service

I created an AngularJS service which does ajax request to the server and returns the response object to the controller. Below is my service
app.factory('ajaxService', function() {
return {
ajaxcall: function(url, type, data, handler) {
$.ajax({
url: url,
type: type,
data: data,
beforeSend: function(xhr) {
xhr.setRequestHeader("X-OCTOBER-REQUEST-HANDLER", handler);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
}
})
.done(function(response) {
console.log("ajaxService done: ");
console.log(response);
return response;
})
.fail(function(response) {
console.log("in onCheckUser-error: ajaxService ");
});
}
}
});
The controller is defined as below
var app = angular.module('starter', [])
app.controller('myCtrl', function(ajaxService) {
var res = {};
res = ajaxService.ajaxcall("https://www.travelmg.in/check-login",'POST','',"onCheckLogin");
console.log(res);
});
Here, i get the expected response in console in the ajaxService service. When i return the response, i see an "undefined" value in res variable in console.
I don't understand why the res variable is undefined. Please suggest
Thats because your making an asynchronous call, which means it will not return the result immediately.
only way to resolve this is to receive the promise object returned from $.ajax & use the .done() function on it to receive the successful data.
What you need to do:
Move the done() & fail() outside service factory.
return the ajax promise object all the way to the consumer, i.e controller.
JS CODE:
//service factory code
return {
ajaxcall: function(url, type, data, handler) {
return $.ajax({
url: url,
type: type,
data: data,
beforeSend: function(xhr) {
xhr.setRequestHeader("X-OCTOBER-REQUEST-HANDLER", handler);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
}
});
}
}
//controller code
app.controller('myCtrl', function(ajaxService) {
var res = {};
ajaxService.ajaxcall("https://www.travelmg.in/check- login",'POST','',"onCheckLogin")
.done(function(response) {
console.log("ajaxService done: ");
console.log(response);
//return response; // dont return instead process the result here
})
.fail(function(response) {
console.log("in onCheckUser-error: ajaxService ");
});
});
Note:
I would personally dont prefer mixing jquery and angularjs library unless jquery is really needed for some third party library. both are two different frameworks with different ideology,so dont mix them.
also if your referring to jquery only for $.ajax api ? then i would suggest you to use $http, whose API is same as $.ajax. ref: https://docs.angularjs.org/api/ng/service/$http
You have built the application in angular so it would be convenient to use $http directive to make ajax calls. Inject $http is your service, then you can handle the response as such:
ajaxService.ajaxcall("https://www.travelmg.in/check-login",'POST','',"onCheckLogin").then(function(response) {
console.log(response.data);
});

ngResource.myfunc().$promise.then returns empty array

I'm using angularjs-1.5.0 to write my application.
I created a service that uses a $resource to fetch data from the api server. the service is called MyApi.
(function () {
angular.module('myalcoholist').factory('MyApi', ['$resource', '$auth', function ($resource, $auth) {
return $resource('https://myalcoholist.com:8888/drink/:cmd',
null, {
pouringSpeedTypes: {
method: 'GET',
params: {api_token: $auth.getToken(), cmd: 'get_pouring_speed_types'},
isArray: true
},
pouringLocationTypes: {
method: 'GET',
params: {api_token: $auth.getToken(), cmd: 'get_pouring_location_types'},
isArray: true
}
});
});
}]);
})();
and in my controller i use this service using the following code:
MyApi.pouringLocationTypes().$promise.then(function(data) {
console.info(data);
});
MyApi.pouringSpeedTypes().$promise.then(function (data) {
console.info(data);
});
the problem is that the data is.. a promise i guess.
this is what data contains:
$promise: d
$resolved: true
length: 0
__proto__: Array[0]
what do I do with that? how do I get the actual data from the get request ?
thank you for your comments, you allowed me to pinpoint the issue.
first of all I didn't update my server's code that's why when you tried these addresses you got Method not Allowed.
the problem that i've been having is that these requests really returned empty arrays.
so first of all my API returns a json Object not an array so i changed isArray to false.
so after that and actually fixing my API Server, this is what I received:
$promise: d
$resolved: true
data: Array[4]
success: true
my API server returns a json with data and success. so things are working now! thanks a lot.

Best way to initialize a controller with several ajax requests

I have 2 $http functions that I call using ng-init because the data they return populates the page.
ng-init = "getOpen(); getClosed();"
Is this the best way?
First function;
$scope.getOpen = function () {
$http({
method: 'post',
url: "http://www.example.co.uk/php/open-get.php",
data: $.param({ 'location' : $scope.l,
'time' : $scope.t,
'day' : $scope.d,
'type' : 'get_restopen' }),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).
success (function(data, status, headers, config){
if(data.success && !angular.isUndefined(data.data) ){
$scope.open = data.data;
} else {
$scope.open = [];
}
}).
error(function(data, status, headers, config) {
//$scope.messageFailure(data.message);
});
}
Second function;
$scope.getClosed = function () {
$http({
method: 'post',
url: "http://www.example.co.uk/php/closed-get.php",
data: $.param({ 'location' : $scope.l,
'time' : $scope.t,
'day' : $scope.d,
'type' : 'get_restopen' }),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).
success (function(data, status, headers, config){
if(data.success && !angular.isUndefined(data.data) ){
$scope.closed = data.data;
} else {
$scope.closed = [];
}
}).
error(function(data, status, headers, config) {
//$scope.messageFailure(data.message);
});
}
Everything works great. My question I guess is this an efficient way of doing things in AngularJS? I'm new to angular so just seeking guidance.
1 - Are my $http executed simultaneously? Or is one completed before the other one is started?
2 - Is there any need to introduce $q or promises into my code? The functions are independent of each other
3 - How can I detect when all $http request have completed regardless of if successful or not
Is the below code correct?
$q.all([$scope.getOpen, $scope.getClosed]).then(function() {
//Both requests have been completed and I shall now set a bolean
$scope.compelete = true;
});
Assuming you call both methods yourself somewhere, yes. $http calls are async by default
Already done, $http actually returns a promise!
promise.all() is an elegant way to do so without modifying the return of the promise. It is effectively a completion watcher. More details over on the promise reference
I have 2 $http functions that I call using ng-init because the data
they return populates the page.
ng-init = "getOpen(); getClosed();"
Is this the best way?
As said into angular documentation :
This directive can be abused to add unnecessary amounts of logic into
your templates. There are only a few appropriate uses of ngInit, such
as for aliasing special properties of ngRepeat, as seen in the demo
below; and for injecting data via server side scripting. Besides these
few cases, you should use controllers rather than ngInit to initialize
values on a scope.
You can find it here : https://docs.angularjs.org/api/ng/directive/ngInit
you should avoid using ng-init into your templates. The recommended usage for initiating something into a controller is to call a private function directly inside your controller.
I also recommend to read that style guide about angular :
https://github.com/johnpapa/angular-styleguide
1 - Are my $http executed simultaneously? Or is one completed before
the other one is started?
The 2 calls are started almost simultaneously. Then, javascript holds a stack of callbacks he has to execute when he gets answers from your server. Just press F12 into your browser and find a "network" tab to see all your requests being launched.
Furthermore, ´.success´ is deprecated now. You should rather use ´.then´
https://docs.angularjs.org/api/ng/service/$http#deprecation-notice
2 - Is there any need to introduce $q or promises into my code? The
functions are independent of each other
Not in this case. You could use $q.all([promise1, promise2]) if you wanted to execute something after both calls had been executed
3 - How can I detect when all $http request have completed regardless
of if successful or not
Have a look at $httpInterceptors. Let share you a piece of code I implemented
angular.module("myModule").factory("myHttpInterceptor", [
"LoadingService",
"$q",
"Alert",
function (LoadingService, $q, Alert) {
function requestInterceptor(config) {
LoadingService.increaseCounter();
return config;
}
function responseInterceptor(response) {
LoadingService.decreaseCounter();
return response;
}
// ...
Is the below code correct?
$q.all([$scope.getOpen, $scope.getClosed]).then(function() {
//Both requests have been completed and I shall now set a bolean
$scope.compelete = true;
});
nope because you still the promise to be returned by your function. Also, ´.success´ does not implement the chaining of promises. You now HAVE to use ´.then()´
$scope.getOpen = function () {
return $http({
// config
}).
then(function(data, status, headers, config){
//handling success
},(function(data, status, headers, config) {
//handling error
});
Summary :
angular.module("yourApp").controller("someController", ["$scope", "$q"
function($scope, $q){
// init
init();
// Shared functions
$scope.getOpen = function () {
return $http({
method: 'post',
url: "http://www.example.co.uk/php/open-get.php",
data: $.param({ 'location' : $scope.l,
'time' : $scope.t,
'day' : $scope.d,
'type' : 'get_restopen' }),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
$scope.getClosed = function () {
return $http({
// config
});
// private functions
function init(){
var promises = [];
promises.push($scope.getOpen());
promises.push($scope.getClosed());
$q.all(promises).then(function(datas){
// Here, you know both calls have been successfull
// datas[0] is the result of $scope.open()
$scope.open = datas[0].data;
$scope.complete = true;
}, function(errors){
$scope.complete = true;
// You fall here if one of your calls has failed
});
}
}
]);
$http is asynchronous, so your 2 calls should be executed in parallel. If you really need to boil the 2 functions down to one, and/or you need to check all $http requests have completed, you can use $q.all Angularjs $q.all

multiple $http with promise using factory, angular

I am attempting to call multiple $http request in a factory which I Am using to flush out multiple inputs with data and set default selections. I Want to then call a promise once all 3 of these are done to call in the real form data (if there is any, sometimes there will not be in which case it will do nothing) to overwrite the defaults in place.
So here is my attempt at this -
the factory
I set up a factory to call all 3 inputs and their defaults (I am being sent them individually, i cannot change this for now). It looks like this:
.factory("getDefaults", function() {
return {
instructions: function() {
$http({
method: "GET",
url: "/getStringDropdown/materials"
})
.success(function(data, status, headers, config){
$scope.instructions.materials = data.text;
});
$http({
method: "GET",
url: "/getStringDropdown/reinforce"
})
.success(function(data, status, headers, config){
$scope.reinforce = data.options;
$scope.instructions.reinforce = $scope.reinforce[data.default];
});
$http({
method: "GET",
url: "/getStringDropdown/procedure"
})
.success(function(data, status, headers, config){
$scope.procedures = data.options;
$scope.instructions.procedures = $scope.procedures[data.default];
});
//return data here?
}
};
})
My question here is - do i have to return the data here? And also can I define the scopes here that are being used (as apposed to in the actual controller). I'm pretty sure this is wrong but I cant' find a good example of how to structure something like this properly.
The call in the contoller
So i the controller my thinking is i would then try something like this -
getDefaults.instructions().then(function(){
//possible return data here
//AFTER data is flushed out call farm data
$http({
method: "GET",
url: "/getSavedFormData/formID"
})
.success(function(data, status, headers, config){
$scope.instructions.materials= data.materials;
$scope.instructions.procedures = $scope.procedures[data.procedure];
$scope.instructions.reinforce = $scope.reinfoce[data.reinforcement];
});
});
So big picture - I am trying to get these 3 calls to run and populate and THEN the second one. I'm not sure what might or might not be the best approach, factory seemed to make sense based on the trying to consolidate the 3 calls into 1 place with a promise when they are all done. I'm thinking i need to return the data, but it would be pretty nice if i could define the scopes for the controller in the factory. I am still getting my bearing with angular so any/all guidance would be much appreciated. Thanks for reading!!
Your service is not aware of your $scope out of the box, nor do you probably want it to be as the whole point of services is to assist with the modularity of your code.
What you probably want to do is actually return the $http promise from your service so that your .success() callback can actually set models on the $scope via closure (being inside the controller).
So your factory would be more like this:
.factory("getDefaults", function() {
return {
instructions: $http({ method: "GET", url: "/getStringDropdown/materials" })
}
});
If you really think you'll never need those http calls separately and you only care about when they all resolve. You could return a $q.all() promise that will resolve when they all resolve:
.factory("getDefaults", function($http, $q) {
var promise1 = $http({ method: "GET", url: "/getStringDropdown/materials" });
var promise2 = $http({ method: "GET", url: "/getStringDropdown/materials" });
var promise3 = $http({ method: "GET", url: "/getStringDropdown/materials" });
return {
data: $q.all([promise1,promise2,promise3]),
anotherCall: $http.get('/anothercallUrl')
}
});
So now from your controller you could just do:
function myCtrl($scope,getDefaults){
getDefaults.data.then(function(data){
//access all three of your promises, their data, and set $scope models here
getDefaults.anotherCall.success(function(data){
//or another http call
});
};
}
$q.all documentation here: https://docs.angularjs.org/api/ng/service/$q
Using scopes in factories is not a good idea , my suggestion to not use, about multiple http calls you should use $q.all
example here in fiddle
for each call you should create defered object push it into array then use
$q.all(deferesArray).then(
function(resp){
// all requests are resolver , resp is array with resolved datas
}
)
You may want to have a look at angular's q$ (https://docs.angularjs.org/api/ng/service/$q)
1.) Create the first three promises that must finish "first"
var deferred = $q.defer();
$http({
method: "GET",
url: "/getStringDropdown/procedure"
})
.success(function(data, status, headers, config){
deferred.resolve(data);
});
var promise1 = deferred.promise;
2.) Use all method of q$, then call a "then" to the result to execute the second part of your logic
q$.all([promise1, promise2, promise3])
.then(function(results) {
$http({
method: "GET",
url: "/getSavedFormData/formID"
})
.success(function(data, status, headers, config){
$scope.instructions.materials= data.materials;
$scope.instructions.procedures = $scope.procedures[data.procedure];
$scope.instructions.reinforce = $scope.reinfoce[data.reinforcement];
});
});

AngularJS tokenWrapper - wrong callback arguments

AngularJS 1.2.1
ngResource 1.2.1
I got the weirdest problem.
I'm using tokenWrapper by Andy Joslin (AngularJS: How to send auth token with $resource requests?)
I have a resource defined like that:
.factory('someService', ['$resource', 'api_host', 'TokenHandler',
function($resource, api_host, TokenHandler) {
var Resource = $resource(api_host + 'applicant/:command/:xxx', { xxx: '#xxx', command: '#command' }, {
'get': { method: 'GET', isArray: false },
'save': { method: 'POST', isArray: false },
'create': { method: 'put', isArray: false },
'message': { method: 'post', isArray: false }
});
Resource = TokenHandler.wrapActions( Resource,
["query", "get", "save", "remove", "create", "message"] );
return Resource;
}])
its wrapped by tokenHandler and token is sent with every request and thats great.
the problem is with invoking the error callback.
when using the resource like that
var interview = new someService({ command: 'doSomething', xxx: $scope.xxx});
interview.$create({}, function(response) {
console.log(response);
}, function(e) {
alert('error');
});
The Problem
When the resource returns 200 the success function (1st argument) is invoked as it should.
When the resource returns something else, the error callback is not invoked.
I've tried to log the tokenHandler and it seems like the handler is massing up the arguments.
var tokenWrapper = function( resource, action ) {
// copy original action
resource['_' + action] = resource[action];
// create new action wrapping the original and sending token
resource[action] = function( data, success, error){
console.log(success, 'success');
console.log(error, 'error');
return resource['_' + action](
angular.extend({}, data || {}, {token: tokenHandler.get()}),
success,
error
);
};
};
the result of the console.log prints out the data as success and success as error.
how can I fix that?!
angular 1.2.0 changed the way promises are handled. and this look like a promise issue (you are not getting the expected order of suceess``failure```. see this for an example. my guess that insisting on using resource from 1.5 doesn't work we'll with this behavior, so you are not getting the expected data. yes. there are error messages when upgrading. but solving them shouldn't take long.

Categories

Resources