I'm trying to pass a params object to the $http.get() service. My params look like this:
var params = {
one: value,
two: value
}
And I'm trying to pass them into my function like so:
$http.get('/someUrl', params)
.success(function(data) {
// stuff
})
.error(function(data) {
// error stuff
});
Is this the correct way to go about doing this?
The second argument of $http is a config object (see documentation). Amongst other properties, the config object accepts a params property:
params – {Object.<string|Object>} – Map of strings or objects which will be serialized with the paramSerializer and appended as GET parameters.
Therefore you have to pass the parameters as such
var config = {
params: {
one: value,
two: value
}
}
$http.get('/someUrl', config).then(...)
Suppose the values for the parameters are respectively '1' and '2', $http will send a GET request to the following url:
/someUrl?one=1&two=2
As a side note, try to avoid using success and error functions on $http. They have been deprecated as of angular 1.4.4. Use the methods then with a success and an error callback instead, or then with only a success callback and catch.
Service/Factory
For the actual call use a factory or service that you can inject to the controllers you need it in. This is an example factory passing parameters
.factory('Chats', function ($http, $rootScope, $stateParams) {
return {
all: function () {
return $http.get('http://ip_address_or_url:3000/chats', { params: { user_id: $rootScope.session } })
}
};
});
Controller
In your controller you use the service like this
.controller('ChatsCtrl', function ($scope, Chats) {
Chats.all().success(function (response) {
$scope.chats = response;
})
})
I have faced similar issue in recent time and I had to add few additional details to request (I used accepted answer with some headers):
$http.get(url, {
params: {
paramOne: valueOne,
paramTwo: valueTwo,
...
},
headers: {
'key': 'value'
},
// responseType was required in my case as I was basically
// retrieving PDf document using this REST endpoint
// This is not required in your case,
// keeping it for somebody else's reference
responseType: 'arraybuffer'
}).success(
function(data, status, headers, config) {
// do some stuff here with data
}).error(function(data) {
// do some stuff here with data
});
The $http documentation suggest that the second argument to the $http.get method is an object which you can pass with it "param" object.
Try something like this:
$http.get('/someUrl', {params: params})
.success(function(data) {
// stuff
})
.error(function(data) {
// error stuff
});
Related
I have a problem with angular-ui typeahead component. It does not show values populated by angular resources, however using $http works well. I suppose I missing some trick here with asycn call and correct population of returned values.
Working code
$scope.searchForContact = function(val) {
return $http.get('/api/contacts/search', {
params: {
q: val
}
}).then(function(response){
return response.data.map(function(item){
return item.name;
});
});
};
Not working code
$scope.searchForContact = function(val) {
return Contact.search({q: val}, function(response){
return response.map(function(item){
return item.name;
});
});
});
...
'use strict';
app.factory("Contact", function($resource, $http) {
var resource = $resource("/api/contacts/:id", { id: "#_id" },
{
'create': { method: 'POST' },
'index': { method: 'GET', isArray: true },
'search': { method: 'GET', isArray: true, url: '/api/contacts/search', params: true },
'show': { method: 'GET', isArray: false },
'update': { method: 'PUT' },
'destroy': { method: 'DELETE' }
}
);
return resource;
});
Pug template code
input.form-control(
type='text'
ng-model='asyncSelected'
uib-typeahead='contact for contact in searchForContact($viewValue)'
typeahead-loading='loadingLocations'
typeahead-no-results='noResults'
)
i.glyphicon.glyphicon-refresh(ng-show='loadingLocations')
div(ng-show='noResults')
i.glyphicon.glyphicon-remove
|
|No Results Found
Angular resources are working fine, including search endpoint - I just output on page result returned by the search endpoint. In both results should be just an array with string values. What am I doing wrong?
The difference between $http.get and your Contact.search is that the first one returns a promise and the latter doesn't. Any $resource method will usually be resolved to the actual response. I'll show that with an example.
Getting data with $http
var httpResult = $http.get('http://some.url/someResource').then(function(response) {
return response.map(function(item) { return item.name });
});
The httpResult object contains a promise, so we need to use then method to get the actual data. Moreover, the promise will be resolved to the mapped array, which is the expected result.
Getting data with $resource
var someResource = $resource('http://some.url/someResource');
var resourceResult = someResource.query(function(response) {
return response.map(function(item) { return item.name });
});
The resourceResult isn't a promise here. It's a $resource object which will contain the actual data after the response comes from the server (in short, resourceResult will be the array of contacts - the original, not mapped, even though there is a map function). However, the $resource object contains a $promise property which is a promise similar to one returned by $http.get. It might be useful in this case.
Solution
I read in documentation that in order to make uib-typehead work properly, the $scope.searchForContact needs to return a promise. Instead of passing the callback function to search, I would simply chain it with the $promise from $resource object to make it work.
$scope.searchForContact = function(val) {
return Contact.search({q: val}).$promise.then(function(response){
return response.map(function(item){
return item.name;
});
});
});
Let me know if it works for you.
I have this in my Angular service:
return $resource(BASE + '/cases/:id',
{id: '#id'}, {
status: {method: 'GET', params: {status: '#status'}}
});
When using the method added to the $resource definition along with the promise's .then() function, I'm getting an error:
Cases.status({status: 'pending'})
.then(function(res) {
console.log(res);
$scope.cases.pending = res.data.cases;
})
.then(function() {
$scope.tabbed.pending = true;
});
After the above snippet is run, the error I get is:
TypeError: undefined is not a function on this line: .then(function(res) {
Can I not use these functions as I usually do when I'm using an extra method defined on the $resource?
I think you need to use $promise of $resource object which will call success function when actual promise gets resolved & then you could proceed with the promise chain.
CODE
Cases.status({status: 'pending'})
.$promise
.then(function(res) {
console.log(res);
$scope.cases.pending = res.data.cases;
})
.then(function(cases) {
$scope.tabbed.pending = true;
});
You have to use $promise in order to access the promise is created on that call, like this:
Cases.get({id: 123}).$promise
.then(function (success) {
//do something
}).then(function (error) {
//do something else
})
Or you can send both functions as callbacks:
Cases.get({id: 123}, function (success) {
//do something
}, function (error) {
//do something else
});
Tip
You don't have to add a new method to that $resource to send a GET to the same url, you can just have your $resource be plain:
//... code
return $resource(BASE + '/cases'});
and when you pass the parameters to it (if you are passing them as in the example) it will match the keys according to the object so you can just say:
Cases.get({status: 'something'}).$promise
.then(function(success){
//... code
})
.then(function(err){
//... code
});
Cases.get({id: 123}).$promise
.then(function(success){
//... code
})
.then(function(err){
//... code
});
$http({
method: 'POST',
url: '/getAllUsers'
}).success(function(data, status, headers, config) {
if (data.length === 0) {
$location.path('/admin/login');
}
// process data
if (data.ok == 1) {
// correct data
} else {
// error
}
})
I use $http to fetch server data, and the server will respond an empty string if the auth fails. What I want to do is, if data is empty, then the application should redirect to login page. But since there are many controllers, and each controller has several $http call, so I don't want to copy and paste the directing code everywhere.
Is there any way I can "inject" this function on every $http response without writing the same code everywhere?
And am I able to apply it only on $http calls in some specific controllers?
You can use a http intercetor. The intercetor's response method is called right after $http receives the response from the backend. You can modify the response or make other actions. The method get called with the http response object and needs to return the response object directly, or as a promise containing the response or a new response object.
$provide.factory('myHttpInterceptor', function() {
return {
'response': function(response) {
if (/* response failed */ ) {
// $rootScope broadcast
// or $location login
// or whatever you want
}
return response;
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
Edit:
I'm not sure if this is correct usage of $http config (I'never done this..) but you could also conditionally add a transformResponse function to a single $http call like this:
function transformErrorResponse(data, headersGetter, status) {
if (/* failed */)
// handle failure
}
$http({ transformResponse : transformErrorResponse})
.get(/* */)
.then(/* */);
You can create a factory definition and push it in config phase as Interceptor (using $httpprovider)
Factory Definition
angular.module('myApp').factory('httpInterceptor', ['$location', function($location) {
var httpInterceptor = {
response: function(config){
if (config.data.trim() == "") {
$location.path('\login');
}
return config;
}
};
return httpInterceptor;
}]);
Config Phase
angular.module('myApp').config(['$httpProvider' ,function($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
}]);
You can write a simple config for $http in your module config and write your error handling in the transformResponse property of the $http config.
Something like this:
angular.module("yourModule", []).config ($http) ->
$http({
transformResponse:(data, headersGetter, status)->
/**Handle your data empty stuff here. ***/
})
I have functions like the getData function below.
I understand that $http returns a promise. In my current set up I am using $q so that I can do some processing of the results and then return another promise:
var getData = function (controller) {
var defer = $q.defer();
$http.get('/api/' + controller + '/GetData')
.success(function (data) {
var dataPlus = [{ id: 0, name: '*' }].concat(data);
defer.resolve({
data: data,
dataPlus: dataPlus
});
})
.error(function (error) {
defer.reject({
data: error
});
});
return defer.promise;
}
Is there any way that I can do this without needing to use the AngularJS $q (or any other $q implementation) or is the code above the only way to do this? Note that I am not looking for a solution where I pass in an onSuccess and an onError to the getData as parameters.
Thanks
As you say $http.get already returns a promise. One of the best things about promises is that they compose nicely. Adding more success, then, or done simply runs them sequentially.
var getData = function (controller) {
return $http.get('/api/' + controller + '/GetData')
.success(function (data) {
var dataPlus = [{ id: 0, name: '*' }].concat(data);
return {
data: data,
dataPlus: dataPlus
};
})
.error(function (error) {
return {
data: error
};
});
}
This means that using getData(controller).then(function (obj) { console.log(obj) });, will print the object returned by your success handler.
If you want you can keep composing it, adding more functionality. Lets say you want to always log results and errors.
var loggingGetData = getData(controller).then(function (obj) {
console.log(obj);
return obj;
}, function (err) {
console.log(err);
return err;
});
You can then use your logging getData like so:
loggingGetData(controller).then(function (obj) {
var data = obj.data;
var dataPlus = obj.dataPlus;
// do stuff with the results from the http request
});
If the $http request resolves, the result will first go through your initial success handler, and then through the logging one, finally ending up in the final function here.
If it does not resolve, it will go through the initial error handler to the error handler defined by loggingGetData and print to console. You could keep adding promises this way and build really advanced stuff.
You can try:
Using an interceptor which provides the response method. However I don't like it, as it moves the code handling the response to another place, making it harder to understand and debug the code.
Using $q would be the best in that case IMO.
Another (better ?) option is locally augmented transformResponse transformer for the $http.get() call, and just return the $http promise.
I am trying to use $resource to get data from a static json file and here is the code snippet :
angular.module('app.services', ['ngResource']).
factory('profilelist',function($resource){
return $resource('services/profiles/profilelist.json',{},{
query:{method:'GET'}
});
});
In the controller,
function ProfileCtrl($scope,profilelist) {
$scope.items = [];
$scope.profileslist = profilelist.query();
for (var i=0;i<=$scope.profileslist.length;i++){
if($scope.profileslist[i] && $scope.profileslist[i].profileid){
var temp_profile = $scope.profileslist[i].profileid;
}
$scope.items.push(temp_profile);
}
But now, I am facing an error :
TypeError: Object #<Resource> has no method 'push'
Could you please help me where I am going wrong ?
You don't need to specify actions parameter for default $resource methods (these are 'get', 'save', 'query', 'remove', 'delete'). In this case you can use .query() method as is (this requires only service definition to be chaged):
angular.module('app.services', ['ngResource']).
factory('profilelist',function($resource){
return $resource('services/profiles/profilelist.json');
});
P.S. And one more hint is that your example unwrapped json into hash rather then array (that's why you received no push method error), if you need it to be an array set isArray: true to action config:
'query': {method:'GET', isArray:true}
And as #finishingmove spotted you really can't assign $resource result to obtain immediately, provide callback:
$scope.profileslist = profilelist.query(function (response) {
angular.forEach(response, function (item) {
if (item.profileid) {
$scope.items.push(item.profileid);
}
});
});