AngularJS: Instance resource with POST errors out on promise - javascript

I am attempting to build a generic filter which needs to be able to handle - based on configuration - parameters. Both a resource instance and a service.
I have the following factory code:
angular.module('sample').factory('Sample', function($resource) {
var methods = {
search: {
method: 'POST',
isArray: true
}
}
var Sample = new $resource('/sample', methods);
Sample.prototype.elasticFilter = function (search) {
var query = {
query: {
filtered: {
query: {
wildcard: {
name: '*' + search.toLowerCase() + '*'
}
}
}
}
}
return this.$search(query);
}
return Sample;
});
Now if I do the following in my directive:
scope.search = function (search) {
Sample.elasticFilter(search).$promise.then(function () {
handle
});
this results in
TypeError: Cannot read property 'then' of undefined
printing out Sample.elasticFilter(search), the result is directly a Promise.
Modifying the directive to read directily off Sample.elasticFilter(search).then results in:
TypeError: value.push is not a function.
For non-instance-resources (e.g. NOT newed up, rather service directly), .$promise works fine.
Why is this happening? How can I work around it? When does a resource have a $promise, and when does it not?

This appears to be a "feature" https://github.com/angular/angular.js/issues/11767
The only way to really work around this is to not use isArray on instance level resource methods.

Related

Can't extract data from $resource (consuming rest webservice)

I'm trying to consume a REST webservice, responding with a JSON String containing a fairly "complex" schema.
I created a model that contains every fields sent by the webservice.
Here are the relevant codes that should be a problem :
public getUser(user_id: number): PlanDeCharge.Modeles.User {
var toto;
this.UserRest.get({ user_id: user_id }, function(){}, function(err){
this.$window.location.href = "http://localhost:8080/myapp_webapp/login.do";
}).$promise.then(function(data){
toto = data;
});
return toto;
}
-
this.userConnecte = this.gestionUserService.getUser(759);
-
export function userRest($resource: ng.resource.IResourceService, $cookies: ng.cookies.ICookiesService): PlanDeCharge.Modeles.IUserResource {
this.key = $cookies.get("encodedKey");
var urlService: string = "http://localhost:8080/teambox_webapp/resource-rest/V1_1/users/:user_id";
return <PlanDeCharge.Modeles.IUserResource> $resource(urlService, {user_id: "#user_id"}, {
get:{
headers:{"key" : this.key}
}
});
}
app.factory("UserRest", ["$resource", "$cookies", userRest]);
I did a lot of modifications, trying to fix the call without success... The request actually get a response containing the JSON string, but I can't put it inside an object to be use (like user['id'] = 2)
Thanks in advance
I deleted the last post and made this new one, the first one wasn't clear enough and people were confused
When working with promises you should let Angular handle the resolvement.
Am I right, if you are actually using AngularJS 1 and not ng2 as the question is tagged? The syntax is ng1 anyways.
Some notes on the getUser method. Return the reference created by $resource instead of creating one your self. Further more, use the fat-arrow syntax on the callbacks to bind this to the proper context. See this article for more on this.
To remove even more code use TypeScripts object initialization and init the user id object with just { user_id }. This creates a JavaScript object with a property user_id with the value of user_id.
public getUser(user_id: number): SomeModel {
return this.UserRest
.get({ user_id }, () => { }, () => {
this.$window.location.href = "http://localhost:8080/myapp_webapp/login.do";
});
}
In your component or controller access
this.userConnecte = this.gestionUserService.getUser(759);
Lastly, the factory/service.
Use the fact that $resource is generic and set your variables as constants when not changed.
export function userRest(
$resource: ng.resource.IResourceService,
$cookies: ng.cookies.ICookiesService
): ng.resource.IResourceClass<PlanDeCharge.Modeles.IUserResource> {
this.key = $cookies.get("encodedKey");
const urlService = "http://localhost:8080/teambox_webapp/resource-rest/V1_1/users/:user_id";
return $resource<PlanDeCharge.Modeles.IUserResource>(urlService, { user_id: "#user_id" }, {
get: {
headers: { "key": this.key }
}
});
}
This should fix your problems and make to code more readable. :)

Update ng-repeat after ng-resource call is finished

I am using ngResource in combination with ng-repeat and noticed that slow REST calls doesn't update the list properly. It keeps empty.
As far as I understood I need a binding between controller and ng-repeat element.
My resource and controller definition:
(function (configure) {
configure('loggingResource', loggingResource);
function loggingResource($resource, REST_CONFIG) {
return {
Technical: $resource(REST_CONFIG.baseUrl + REST_CONFIG.path + '/', {}, {
query: {
method: 'GET',
isArray: true
}
})
};
}
})(angular.module('loggingModule').factory);
(function (configure) {
configure('logController', logController);
function logController(loggingResource) {
var that = this;
loggingResource.Technical.query(function (data) {
that.logs = data;
});
//that.logs = loggingResource.Technical.query();
}
})(angular.module('loggingModule').controller);
ng.repeat usage:
<tr class="table-row" ng-repeat="log in logController.logs">
What I have tried so far:
ng-bind in combination with ng-repeat
$q with deferrer
$promise of ngResource
What did I miss?
My try to get it on plnkr: https://plnkr.co/edit/t1c5Pxi7pzgocDMDNITX
The {{}} provides a default binding between your controller and view and you don't need to add anything explicitly. I have updated your plunkr with some minor changes to injected constants etc. and it is working.
// Code goes here
angular.module("loggingModule", ['ngResource']);
(function(configure) {
configure('loggingResource', loggingResource);
function loggingResource($resource) {
return {
Technical: $resource('https://api.github.com/users/addi90/repos', {}, {
query: {
method: 'GET',
isArray: true
}
})
};
}
})(angular.module('loggingModule').factory);
(function(configure) {
configure('logController', logController);
function logController(loggingResource) {
var that = this;
that.logs = [{
title: 'test2'
}];
loggingResource.Technical.query(function(data) {
that.logs = data;
});
//that.logs = loggingResource.Technical.query();
}
})(angular.module('loggingModule').controller);
Since the api resource was not working, I have used my github repo api link there
The updated working plunkr is available here: https://plnkr.co/edit/cederzcAGCPVzc5xTeac?p=preview
Try to use
that.logs.push(data)
so you prevent to reinitialise the logs list. If you override the logs list and the ng-repeat is initialised before the logs list it seems to not be resolved.
Your logs list is set correctly after the rest call?

undefined is not a function using $resource [duplicate]

When trying to poll a custom method copies on an AngularJS Resource I get the following error at angular.js:10033: (The method copy works just fine.)
TypeError: undefined is not a function
at https://code.angularjs.org/1.3.0-beta.8/angular-resource.min.js:9:347
at Array.forEach (native)
at q (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:7:280)
at q.then.p.$resolved (https://code.angularjs.org/1.3.0-beta.8/angular-resource.min.js:9:329)
at J (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:101:5)
at J (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:101:5)
at https://code.angularjs.org/1.3.0-beta.8/angular.min.js:102:173
at g.$eval (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:113:138)
at g.$digest (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:110:215)
at g.$apply (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:113:468)
Angular.js 10016 - 10035:
function consoleLog(type) {
var console = $window.console || {},
logFn = console[type] || console.log || noop,
hasApply = false;
// Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
// The reason behind this is that console.log has type "object" in IE8...
try {
hasApply = !!logFn.apply;
} catch (e) {}
if (hasApply) {
return function() {
var args = [];
forEach(arguments, function(arg) {
args.push(formatError(arg));
});
return logFn.apply(console, args); // This is line 10033 where the error gets thrown.
};
}
Simplified resource:
angular.module('vgm.content-pages')
.factory('ContentPage', function($resource, $http) {
return $resource('/api/content-page/:id', { id:'#page.id' }, {
copy: {
method: 'POST',
url: '/api/content-page/copy/:id'
},
copies: {
method: 'GET',
isArray: true,
url: '/api/content-page/copies/:id'
}
});
});
Simplified directive where I'm getting the error:
angular.module('vgm.genericForms')
.directive('vgmFormCopyPicker', function() {
return {
restrict: 'E',
replace: true,
templateUrl: '/static/common/generic-forms/widgets/view-copy-picker.html',
scope: {
resource: '=',
},
controller: function($scope, $element) {
$scope.pages = [];
$scope.loadCopies = function() {
$scope.resource.$copies()
.then(function(response) {
$scope.pages = response.data;
});
};
}
};
});
As soon as I run the loadCopies method, executing the line $scope.resource.$copies() throws the error above.
In the Chrome Inspector I see the call to my API is actually being done. But resolving the promise seems to throw some error...
How can I solve this error?
EDIT:
$scope.resource = ContentPage.get({id: $stateParams.id}).$promise
$scope.resource.$save() // Works
$scope.resource.$update() // Works
$scope.resource.$copy() // Works
$scope.resource.$copies() // Does not work!
Angular Resource is trying to overwrite my initial resource with an array of items. But the instance of Resource does not have a method push of course.
I found the answer:
A resource is supposed to represent the matched data object for the rest of its lifespan. If you want to fetch new data you should do so with a new object.
$scope.copies = ContentPage.copies()
The answer from Guido is correct but I didn't get it at the first time.
If you add a custom method to your Angular $resource and using isArray: true and expecting to get an Array of something from your WebService you probably want to store the response in an Array.
Therefore you shouldn't use the instance method like this:
var ap = new Ansprechpartner();
$scope.nameDuplicates = ap.$searchByName(...);
But use the resource directly:
$scope.nameDuplicates = Ansprechpartner.searchByName(...)
Using following Angular resource:
mod.factory('Ansprechpartner', ['$resource',
function ($resource) {
return $resource('/api/Ansprechpartner/:id',
{ id: '#ID' },
{
"update": { method: "PUT" },
"searchByName": { method: "GET", url: "/api/Ansprechpartner/searchByName/:name", isArray: true }
}
);
}
]);
I am using Mean.js and this plagued for a few hours. There is a built in $update but it was not working when I tried to apply it to an object returned by a $resource. In order to make it work I had to change how I was calling the resource to update it.
For example, with a Student module, I was returning it with a $resource into $scope.student. When I tried to update student.$update was returning this error. By modifying the call to be Students.update() it fixed the problem.
$scope.update = function() {
var student = $scope.student;
var result = Students.update(student);
if(result){
$scope.message = 'Success';
} else {
$scope.error =
'Sorry, something went wrong.';
}
};

TypeError: undefined is not a function in Angular Resource

When trying to poll a custom method copies on an AngularJS Resource I get the following error at angular.js:10033: (The method copy works just fine.)
TypeError: undefined is not a function
at https://code.angularjs.org/1.3.0-beta.8/angular-resource.min.js:9:347
at Array.forEach (native)
at q (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:7:280)
at q.then.p.$resolved (https://code.angularjs.org/1.3.0-beta.8/angular-resource.min.js:9:329)
at J (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:101:5)
at J (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:101:5)
at https://code.angularjs.org/1.3.0-beta.8/angular.min.js:102:173
at g.$eval (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:113:138)
at g.$digest (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:110:215)
at g.$apply (https://code.angularjs.org/1.3.0-beta.8/angular.min.js:113:468)
Angular.js 10016 - 10035:
function consoleLog(type) {
var console = $window.console || {},
logFn = console[type] || console.log || noop,
hasApply = false;
// Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
// The reason behind this is that console.log has type "object" in IE8...
try {
hasApply = !!logFn.apply;
} catch (e) {}
if (hasApply) {
return function() {
var args = [];
forEach(arguments, function(arg) {
args.push(formatError(arg));
});
return logFn.apply(console, args); // This is line 10033 where the error gets thrown.
};
}
Simplified resource:
angular.module('vgm.content-pages')
.factory('ContentPage', function($resource, $http) {
return $resource('/api/content-page/:id', { id:'#page.id' }, {
copy: {
method: 'POST',
url: '/api/content-page/copy/:id'
},
copies: {
method: 'GET',
isArray: true,
url: '/api/content-page/copies/:id'
}
});
});
Simplified directive where I'm getting the error:
angular.module('vgm.genericForms')
.directive('vgmFormCopyPicker', function() {
return {
restrict: 'E',
replace: true,
templateUrl: '/static/common/generic-forms/widgets/view-copy-picker.html',
scope: {
resource: '=',
},
controller: function($scope, $element) {
$scope.pages = [];
$scope.loadCopies = function() {
$scope.resource.$copies()
.then(function(response) {
$scope.pages = response.data;
});
};
}
};
});
As soon as I run the loadCopies method, executing the line $scope.resource.$copies() throws the error above.
In the Chrome Inspector I see the call to my API is actually being done. But resolving the promise seems to throw some error...
How can I solve this error?
EDIT:
$scope.resource = ContentPage.get({id: $stateParams.id}).$promise
$scope.resource.$save() // Works
$scope.resource.$update() // Works
$scope.resource.$copy() // Works
$scope.resource.$copies() // Does not work!
Angular Resource is trying to overwrite my initial resource with an array of items. But the instance of Resource does not have a method push of course.
I found the answer:
A resource is supposed to represent the matched data object for the rest of its lifespan. If you want to fetch new data you should do so with a new object.
$scope.copies = ContentPage.copies()
The answer from Guido is correct but I didn't get it at the first time.
If you add a custom method to your Angular $resource and using isArray: true and expecting to get an Array of something from your WebService you probably want to store the response in an Array.
Therefore you shouldn't use the instance method like this:
var ap = new Ansprechpartner();
$scope.nameDuplicates = ap.$searchByName(...);
But use the resource directly:
$scope.nameDuplicates = Ansprechpartner.searchByName(...)
Using following Angular resource:
mod.factory('Ansprechpartner', ['$resource',
function ($resource) {
return $resource('/api/Ansprechpartner/:id',
{ id: '#ID' },
{
"update": { method: "PUT" },
"searchByName": { method: "GET", url: "/api/Ansprechpartner/searchByName/:name", isArray: true }
}
);
}
]);
I am using Mean.js and this plagued for a few hours. There is a built in $update but it was not working when I tried to apply it to an object returned by a $resource. In order to make it work I had to change how I was calling the resource to update it.
For example, with a Student module, I was returning it with a $resource into $scope.student. When I tried to update student.$update was returning this error. By modifying the call to be Students.update() it fixed the problem.
$scope.update = function() {
var student = $scope.student;
var result = Students.update(student);
if(result){
$scope.message = 'Success';
} else {
$scope.error =
'Sorry, something went wrong.';
}
};

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