I'm using angular-http-auth to show a login dialog whenever a 401 "unauthorized" response is returned from the server.
Since I'm cool, I also try to deserialize response objects in my services. For example, if a service requests a car and the response is {make: Honda, model: Civic}, I try to deserialize that into a Car object using transformResponse.
For example:
getCar: function() {
return $http.get('/api/car', {
method: 'GET',
transformResponse: function(data, headers) {
var c = angular.fromJson(data);
return new Car(c);
}
});
}
This doesn't work with angular-http-auth. If the response was a 401 Unauthorized, you'll get a javascript error. It's because angular will try to run that transformResponse code even if the response was a 401.
It turns out that $http interceptors (which is what angular-http-auth uses) are run AFTER the transformResponse code. That's a huge problem, because none of that code in transformResponse will work if the server response was a 401 (there wouldn't be any data)
Is this a problem for anyone else? How did you get around it? Am I not to use transformResponse if I use $http interceptors?
Late to the party, I know, but to anyone coming here from Google like I did (I also posted this as a comment on a related issue filed with the Angular repo):
I also found it to be confusing that response interceptors run after the transformResponse method. I added a method to $http.defaults.transformResponse. Here is an example from the documentation on how to do that.
So, if you need to basically have a response interceptor that runs before the transformResponse method, this should do it:
'use strict';
angular.module('app')
.run(function ($http) {
$http.defaults.transformResponse.push(function (data, headers) {
// do stuff here before the response transformation
// Be sure to return `data` so that the next function in the queue can use it.
// Your services won't load otherwise!
return data;
});
});
If your services or http calls don't have their own response transformer, you're good now.
If your services do have their own transformResponse method, they will actually override all default transformers (I found this out after a long read of the documentation), and the above code will not run.
To circumvent this, you can follow this example in the docs.
To get around this problem I don't use transformResponse anymore. I just can't see the point of transformResponse if it runs before $http interceptors.
In order to use angular-http-auth and also deserialize responses in your services, you can write your services so that they execute the HTTP request first and then deserialize the response in a callback function.
As an example, here is how I would have restructured the example in the OP:
Plunker
services.factory('HttpCarService', function($resource, $q) {
var resource = $resource('/api/car');
return {
getCar: function() {
var deferred = $q.defer();
var car = null;
var successCallback = function(data, status, headers, config) {
var c = angular.fromJson(data);
car = new Car(c);
deferred.resolve(car);
};
var errorCallback = function(data, status, headers, config) {
deferred.reject("something wrong");
};
var result = resource.get(successCallback, errorCallback);
return deferred.promise;
}
};
});
This pattern will also work if data is an array.
$http interceptors will run before either of the callback methods are executed. If your $resource needs url params, you can make the getCar() function accept a config object as a parameter, and pass the necessary information on when you make the $resource.get() call.
Related
I have 4 controllers in a page in AngularJS. Each controller calls Api's via http request(scope $http). I want to ensure that all the Api's has been called and loaded till then I can show the loading gif image. How to check all the Api's has been loaded in the page in AngulaJS.
I am not sharing the exact code some variable and name I have modified.
myApp.controller('testController',function ($scope, $http, $q, $filter) {
var _promises = {};
_promises['abc'] =
$http({
url: API_URL+'abc-type/',
method: 'GET',
params: {'test1': 'test2'}
});
_promises['abc1'] =
$http({
url: API_URL+'abc-type2/',
method: 'GET'
});
}
$q.all(_promises).then(function (res) {
alert("All promises executed.");
});
});
$http uses promises ($q) for it's API. You can use the $q.all method to run a callback when an array of $http requests are resolved (you will need to make sure all service requests return promises to avoid undefined behavior).
Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
It would look something like this
$scope.showLoadingGif = true;
$q.all([MyService.makeGet(), MyService.makeAnotherGet(), ...]).then(function(responses) {
// all the calls have returned a response by this point
$scope.showLoadingGif = false;
})
I am trying multiple ways to access my users in a local json file in able to later compare them to the users input. and if there is a match, access is allowed, but my main problem now is just getting to those users.
My code so far:
entire code
json file
What am i doing wrong? i am such a newbie in programming. I have been trying different things and nothing works.
Thanks so much for help
Can you access the file through the browser (via your url localhost:8080/resources/data/users.json)?
If you can't, you will not be able to get access through the $resource or $http.
If you can, any method should work:
1) Via $resource
$scope.users = [];
var UsersResource = $resource('/resources/data/users.json');
where we can get response by callback
UsersResource.get({}, function(response) {
$scope.users = response;
});
or by $promise
UsersResource.get().$promise.then(function(response) {
$scope.users = response;
});
2) Via $http.get
$scope.users = [];
$http.get('/resources/data/users.json').then(function(response) {
$scope.users = response;
});
In your sample, your are trying to get array of users by returning $resource, but $resource returns a object with methods. Each method has callbacks (success, error) or return $promise object.
There is no need to use $resource if you are just going to fetch a json file. Use $http instead:
this.getUsers = function(){
return $http.get('path/to/file.json');
};
And usage:
dataService.getUsers().then(function(resp){
//Do something with the data
$scope.users = resp;
})
$resource is meant to be used when communicating with RESTful apis. What your getUsers() is doing is actually returning a resource-object, upon which you can then call get(). But I recommend using $http in this case.
If you want to use $resouce then you need to create two functions in your controller/factory where "templatesSuccess" return data of request.
getAllTemplates: function(query) {
return $resource(CONST.CONFIG.BASE_URL + 'receiver/get-templates').get(query, obj.templatesSuccess).$promise;
},
templatesSuccess: function(response) {
obj.allTemplates = response;
},
I am posting some data to an MVC action method using AngularJS. This action method will either show its backing view or redirect to another page. Currently all that is happening is the data is getting posted but the redirect is not happening via MVC. I am getting this done using angular's window.location method. I want to know if there is a better way or if I need to post differently using Angular.
On page A I have angular scripts posting data to page B like below:
serviceDataFactory.POST('http://localhost:1234/home/B', someData, pageConfig).then(function () {
//on success
window.location = 'http://localhost:1234/home/Index';
},
function() {
//on error
window.location = 'http://localhost:1234/home/B';
});
This is my service factory
app.factory('serviceFactory', function($http, $q) {
var service = {};
//POST
service.POST = function (url, postData, conf) {
var d = $q.defer();
$http({
method: 'POST',
url: url,
data: postData,
config: conf
}).success(function(data) {
d.resolve(data);
}).error(function(error) {
d.reject(error);
});
return d.promise;
}
return service;
}
);
On Page B I want to redirect to another page. This is my page B in MVC
[HttpPost]
public ActionResult B(string someData)
{
//recieve string someData and perform some logic based on it
.
.
.
if(boolCondition)
return RedirectToAction("Index", "Home");
else
return View();
}
Here once Angular posts to the action method B, it executes all the code all the way till the if(boolCondition) statement. Since I am unable to have that redirect affected via MVC, I do that in Angular itself using the success or error block that the promise returns to.
I want to know if there is a better way to do this or if I am doing something wrong here or if this is the only acceptable way. How do I get angular to hand-off to the MVC action method and let further redirects continue from there only?
You should not use the .success() / .error() pattern with $http, because this has been deprecated. Instead, use then() with two arguments, the first argument being the success function and the second being the error function.
The $http legacy promise methods success and error have been
deprecated. Use the standard then method instead. If
$httpProvider.useLegacyPromiseExtensions is set to false then these
methods will throw $http/legacy error.
You do not need to promisify the result of $http, because $http returns a promise. Just return $http from your service.
app.factory('serviceFactory', function($http, $q) {
var service = {};
//POST
service.POST = function (url, postData, conf) {
return $http({
method: 'POST',
url: url,
data: postData,
config: conf
});
}
return service;
});
Your Page A controller will work the same as before with this new simplified code. At the server, be sure to emit a 500 http status code in cases where you want to trigger the
function() {
//on error
window.location = 'http://localhost:1234/home/B';
}
to run. The 500 in the headers of the response will cause the AngularJS promise to run the second function in your controller.
I send a request server-side by $resource which is inside my factory.
In the return object there many information, but I'd like to have access to the authorization in the headers.
I tried to print the returning object by console.log() but I dont see any headers and authorization in console. What should I do?
controllers.controller('ProfileSettingCtrl', function ($scope,User) {
User.get({id: 'me'}, function(res) {
console.log(res);
$scope.profile = res;
})
Documentation for $resource
Success callback is called with (value, responseHeaders) arguments.
Seems like you can just get the headers with function (res, headers) { console.log(headers); }
According to the $resource docs, the header is passed as second argument to your success callback.
It's worth noting that the success callback for get, query and other methods gets passed in the response that came from the server as well as $http header getter function, so one could rewrite the above example and get access to http headers as:
var User = $resource('/user/:userId', {userId:'#id'});
User.get({userId:123}, function(u, getResponseHeaders){
u.abc = true;
u.$save(function(u, putResponseHeaders) {
//u => saved user object
//putResponseHeaders => $http header getter
});
});
If you want to use the header information, you have to use the success callback. Otherwise I always advice to use promises, which you can chain and pass around:
User.get().$promise.then(successCallback).catch(errorCallback);
The tutorial and docs show this very convenient syntax for AJAX calls:
$scope.myvar = $resource(/*blah blah*/);
Is there a way to use this syntax (instead of having to create a new successfn every time) if my API doesn't return myvar but something like {"myvar": myvar}?
UPDATE: Ah, now I think I got what you're after: You want to have the empty reference (placeholder) for a field of the returned object, in a call like this:
var User = $resource('/user/:userId', {userId:'#id'});
Am I right?
If so - I think the easiest way is simply to bind to the object's field (aka {{user.myvar}}).
Well, the success function is called only once (and if) the data has been received successfully. The function is called with the fetched data given to it as an argument and you can access it no matter its structure. You can reuse a function instead of defining one inline, of course.
So instead of this (example from Angular docs):
$http({method: 'GET', url: '/someUrl'}).
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
You can use:
function myHandler(data) {
// Do something with data.myvar ...
}
$http({method: 'GET', url: '/someUrl'}).success(myHandler);
Hopefully this helps, but very possibly I did not get what exactly you're after :) Let me know.
Relevant documentation.
You need to call get() or query() on your resource to make an actual AJAX request. If you don't want to assign the full result of the server API directly to some ($scope) variable, then you have to write the success function.
var SomeResource = $resource('/blah/blah');
var theResource = SomeResource.get({optional params here}, function() {
$scope.myVar = theResource.myVar;
}
or
var SomeResource = $resource('/blah/blah');
SomeResource.get({optional params here}, function(theResource) {
$scope.myVar = theResource.myVar;
}
Or do what #Chasseur recommends -- assign the full result then bind to the appropriate field in your view:
var SomeResource = $resource('/blah/blah');
$scope.theResource = SomeResource.get({optional params here});
Then in HTML use {{theResource.myVar}}.
Also, $resource, unlike $http, does not return a promise. Update: in newer versions of Angular, $resource now exposes a promise.