Angular Resource: Error: $resource:badcfg - javascript

I have looked at other posts here but still can't resolve my issue.
I get a Error:
$resource:badcfg Response does not match configured parameter
I believe this error is caused by it returning an array rather than an object or vice versa.
Here's my code:
Inside my factory(I added the isArray: false but still not luck)
var task = $resource('http://localhost:5000/task/:id', {id:'#id'}, {
'get': {method:'GET', isArray: false},
});
Then in the return section of my factory
find: function(id){
return task.get({ id: id });
}
In my Flask server it does the correct response when I load the page:
127.0.0.1 - - [28/Feb/2017 14:35:13] "GET /task/1 HTTP/1.1" 200 -
However I still get the error?
Also this is what my server provides if i put it in the browser
http://localhost:5000/task/1
[{"completed": true, "id": 1, "ownerName": "Ryan", "task": "Test Activity", "title": "A test task"}]
Iv also tried this and get the same error:
console.log(task.get({ id: 1 }));

This error occurs when the $resource service expects a response that can be deserialized as an array but receives an object, or vice versa. By default, all resource actions expect objects, except query which expects arrays.
To resolve this error, make sure your $resource configuration matches the actual format of the data returned from the server.
--AngularJS Error Reference - $resource:badcfg
The default actions for $resource are:
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
Avoid changing the default method:
var task = $resource('http://localhost:5000/task/:id', {id:'#id'}, {
//AVOID changing default
//'get': {method:'GET', isArray: true},
});
Instead use the query action method:
find: function(id){
//return task.get({ id: id });
//INSTEAD use query action method
return task.query({ id: id });
}
This will avoid confusing programmers that are familiar with the conventions of the $resource service.
For more information, see AngularJS $resource API Reference.
Using transformResponse to Extract Object from Array
If the server is erroneously returning the resource object enclosed in an array, it can be extracted with a response transform function:
var task = $resource('http://localhost:5000/task/:id', {id:'#id'}, {
'get': {method:'GET',
transformResponse: function(json) {
var data = angular.fromJson(json);
if ((typeof data == 'array' && (data.length == 1)) {
return data[0];
} else {
return data;
};
}
}
});

Have you tried with something like:
var task = $resource('http://localhost:5000/task/:id', {id:'#id'}, {
'get': {method:'GET', isArray: false},
});
You've missed {id:'#id'} to pass paramter id

Turns out I needed to set isArray: false to true
so:
var task = $resource('http://localhost:5000/task/:id', {id:'#id'}, {
'get': {method:'GET', isArray: true},
});

Related

Could not load value into angular-bootstrap typeahead values with resources

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.

Using Web API JSON Response from AngularJS - Error: expected and array but got an object

I have a Web API which is returning a response in JSON, in this format:
{
"email": "john#google.com",
"password": null,
"accessLevel": 2
}
I am trying to access the accessLevel field within that response, but I am getting this Angular error:
Error in resource configuration for action `query`. Expected response to contain an array but got an object (Request: GET http://localhost:51608/api/UserRole?email=john#google.com...)
This is my Angular resource code (below), I just added the isArray false to attempt to solve the issue:
function userRoleResource($resource, appSettings) {
return $resource(appSettings.serverPath + "/api/UserRole?email=:email", { email: '#email' },
{
get: {
method: 'GET',
isArray: false
}
});
}
And this is how I am attempting to use the data:
userRoleResource.query({ email: vm.userData.email },
function (data) {
vm.userData.accessLevel = data.AccessLevel;
});
you're specifying that the 'get' function is not an array, but you're using the 'query' function.
try this:
userRoleResource.get({ email: vm.userData.email },
function (data) {
vm.userData.accessLevel = data.AccessLevel;
});

AngularJS code is giving back error?

$scope.logout = function () {
//var auth_token = $cookieStore.get('auth_token');
Auth.delete({
'auth_token': $cookieStore.get('auth_token')
}, function(data){
$scope.isLoggedIn = false;
$cookieStore.remove('auth_token');
});
When this called it given me an error:
Error: [$resource:badcfg] http://errors.angularjs.org/1.2.27/$resource/badcfg?p0=object&p1=array
z/<#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js:6:450
t/</f[d]/q<#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular-resource.min.js:8:1
De/e/l.promise.then/J#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js:101:87
De/e/l.promise.then/J#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js:101:87
De/f/<.then/<#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js:102:259
Yd/this.$get</h.prototype.$eval#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js:113:28
Yd/this.$get</h.prototype.$digest#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js:110:109
Yd/this.$get</h.prototype.$apply#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js:113:360
m#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js:72:452
w#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js:77:463
ye/</B.onreadystatechange#http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js:79:24
http://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js
Line 92
Its a common problem. The delete method of resource model is expecting a json response which must be an object but your server is returning the json data in array format. So you have two options either change your server code to respond the json object data or change your resource model something like:
var Auth = $resource('/your-server-url', {}, {
delete: {
isArray: false
}
});
Hope this helps!
Thanks,
SA

Dynamic $resource URL based on params

I'm trying to hit an endpoint that looks like /shop/<item_id>
However, because it also requires an auth token, I am sending an array to my $resource along with the token.
APIshop.create([CreateCampaignService, {auth_token: User.token}]
How can I make it so APIshop is able to take something like this:
APIshop.create([CreateCampaignService, {auth_token: User.token}, {item_id: Cart.item_id}]) and hit /shop/<item_id>?
Here is APIshop:
app.provider('APIshop', function(API_URL) {
this.$get = ['$resource', function($resource) {
var shop = $resource(API_URL.url + API_URL.loc + ':service/', {service: '#service'}, {
'create': {method:'POST', isArray: false, params: {service: 'shop'}},
'update': {method:'PUT', isArray: false, params: {service: 'shop'}}
});
return shop;
}];
});
You can't send it in the array since it is only accepting one param unless you concatenate the token and item id. That is bad practice. You should include the token in the header of the request.
take a look here for more info
Basic Authentication with a Guid token for REST api instead of username/password
In https://docs.angularjs.org/api/ngResource/service/$resource under paramDefaults
Default values for url parameters. These can be overridden in actions
methods. If any of the parameter value is a function, it will be
executed every time when a param value needs to be obtained for a
request (unless the param was overridden).
Each key value in the parameter object is first bound to url template
if present and then any excess keys are appended to the url search
query after the ?.
Given a template /path/:verb and parameter {verb:'greet',
salutation:'Hello'} results in URL /path/greet?salutation=Hello.
If the parameter value is prefixed with # then the value of that
parameter will be taken from the corresponding key on the data object
(useful for non-GET operations).
So this should work with TOKEN stored somewhere ($rootScope maybe):
app.provider('APIshop', function(API_URL) {
this.$get = ['$resource', function($resource) {
var shop = $resource(API_URL.url + API_URL.loc + ':service/:id', {service: '#service', id:'#id'}, {
'create': {method:'POST', isArray: false, params: {service: 'shop', access_token: TOKEN}},
'update': {method:'PUT', isArray: false, params: {service: 'shop', access_token: TOKEN}}
});
return shop;
}];
});

Using AngularJS $resource to get data

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);
}
});
});

Categories