I have added a custom action to a $resource:
var myResource = $resource('some/url/:someParam',
{
someParam: '#someParam'
},
{
update: {
method: 'PUT'
}
});
I am attempting to have a function run on completion. According to Angular's documentation, I'd do it in the following way:
non-GET "class" actions: Resource.action([parameters], postData, [success], [error])
Yet the following never invokes the success function even though the request is successful:
myResource.update({ someParam: 'a value' }, { myPostData: 'goes here' }, function(a,b) {
// this function never gets invoked
});
The documentation also says this:
The Resource instances and collections have these additional properties:
$promise: the promise of the original server interaction that created this instance or collection.
But this doesn't work either:
myResource.update({ someParam: 'a value' }, { myPostData: 'goes here' }).$promise.then(function() {
// do stuff
});
Am I missing something here? Any help would be greatly appreciated.
Related
I'm using ngResource to get the data from the server side into my view, and I have a custom action that I defined that gets items count, it looks like this in the code (controller):
$scope.totalItems = Item.item_count().total
And in my view :
This customer bought {{totalItems}}
In my view I just get the This customer bought without other part being interpolated. However also when I do this :
console.log(Item.item_count().total)
console.log(Item.item_count())
I get the undefined for first statement but for second one I get this :
Why am I unable to access the total count from the view, and what do I do to fix this?
Update (service code) :
app.factory('Item', function($resource) {
return $resource(
window.config.API_URL + '/:apiPath/:id', {
id: '#id',
apiPath: '#apiPath'
}, {
query: {
method: 'GET',
params: {
apiPath: 'items'
},
isArray: true
},
item_count: {
method: 'GET',
params: {
apiPath: 'item_count'
},
isArray: false
}
}
);
});
You need to get the $promise and pass in a callback like so:
Item.item_count().$promise.then(function(data) {
$scope.totalItems = data.total;
});
If you need to have the data resolved before loading your view, you can use $stateProvider to resolve it. This should give you a good idea how and some implications of using it: http://www.codelord.net/2015/06/02/angularjs-pitfalls-using-ui-routers-resolve/.
look like you get a promise returned.
Try calling console.log(Item.item_count().then(function(data) {
console.log(data);
});
Without seeing your other code my guess is the line
$scope.totalItems = Item.item_count().total is getting resolved before the promise is getting resolved. That line should be in sort of then catch for the promise. However this is just a guess since you haven't actually posted this code.
Because Item.item_count() returns a promise you have to wait until it is resolved before you set $scope.totalItems:
Item.item_count().then(function(item_count) {
$scope.totalItems = item_count.total;
});
Try removing the scope. from your {{}} in the view.
This customer bought {{totalItems}}
Simple problem with hopefully simple enough solution.
I have defined multiple services to perform CRUD operations with tags.
myApp.factory('GetTags', ['$resource', function ($resource) {
return $resource('/myApp/API/Service/GetTagList', {}, {
query: { method: 'GET', params: { groupId: 'groupId' }, }, isArray: true,
});
}]);
myApp.factory('GetTag', ['$resource', function ($resource) {
return $resource('/myApp/API/Service/GetTag', {}, {
query: { method: 'GET', params: { tagId: 'tagId' }, }, isArray: true,
});
}]);
myApp.factory('SaveTag', ['$resource', function ($resource) {
return $resource('/myApp/API/Service/CreateTag', {}, {
query: { method: 'POST', params: {/*createObj*/}, }, isArray: true,
});
}]);
myApp.factory('UpdateTag', ['$resource', function ($resource) {
return $resource('/myApp/API/Service/UpdateTag', {}, {
query: { method: 'POST', params: {/*updateObj*/}, }, isArray: true,
});
}]);
Later on in my controller I want to do something like this in my tags function:
myApp.controller('myCtrl', ['$scope', '$routeParams', 'GetTags', 'GetTag', 'SaveTag', function ($scope, $routeParams, GetTags, GetTag, SaveTag) {
...
// the goal of this function to keep a copy of
// tags collection in client memory that mimics database
// as well as adding selected tags to forms object
// eg: myForm = {... Tags: [], ...}
$scope.addtag = function (tag, subTag){
...
if (!($scope.tags.length > 0)) {
// skip checking and just add tag
SaveTag.save({ Name: tag, Desc: "", ParentId: null, GroupId: 12, }, function (data) {
console.log('save tag: ', data);
//todo: should wait for data of save operation to comeback
// before moving on to requesting a full object
GetTag.get({ tagId: data.Id, groupId: 12, }, function (data) {
console.log(' get tag: ', data);
tagObj = data.tag;
});
});
// push newly created tag into tags collection
$scope.tags.push(tagObj);
...
};
...
});
I know a skipped a lot of details from my controller and function in question but basically the reason why I am calling save followed by get is because of the tag + subTag scenario. I didn't want to complicate the processing logic if I was to pass a complicated object to the server for processing. Say if I had to create a tag followed by a subTag the javascript logic would look like this:
...
// skip checking and just add tag and subTag
SaveTag.save({ Name: tag, Desc: "", ParentId: null, GroupId: 12, }, function (data) {
//todo: should wait for data of save operation to comeback
// before moving on to requesting a full object
GetTag.get({ tagId: data.Id, groupId: 12, }, function (data) {
tagObj = data.tag;
});
});
// push newly created tag into tags collection
$scope.tags.push(tagObj);
SaveTag.save({ Name: subTag, Desc: "", ParentId: tagObj.ParentId, GroupId: 12, }, function (data) {
//todo: should wait for data of save operation to comeback
// before moving on to requesting a full object
GetTag.get({ tagId: data.Id, groupId: 12, }, function (data) {
tagObj = data.tag;
});
});
// push newly created sub tag into tags collection
//todo: find parent index
$scope.tags[parent_index]["Elements"].push(tagObj);
...
But in case you wonder, yes, I could return full object from save operation which I probably will in the near future. It's better to reduce number of async calls because they impact overall performance.
But for now I have a few questions:
At the moment I have four different services declared as per Angular documentation. But it would be more efficient if it was a single factory with multiple functions. Can someone point me in the right direction here please.
Is it possible to somehow stop and wait for data.$resolved property to turn true when I call save service so that I can then call get services with returned value? Or perhaps there is an alternative method of doing this?
I am digging into $q of Angular Documentation to see if I can utilise something from here.
Just in case people wonder I have come across a few examples where people utilised resolve property with $routeProvider. Unfortunately my scenario is done in real time during user interactions.
Any help and all advice is greatly appreciated.
References:
AngularJs Docs - $q
Very good explanation and examples - Using and chaining promises in AngularJS
AngularJs Docs - $resource (last example shows use of $promise)
Update:
It seem that my hunch was right. While I haven't got it to work yet I feel the answer lies with $q and chaining promises. Now I just need to get it to work.
I hate to answer my own question but I have found the solution that works.
Update:
OK, after digging about and scratching my head I came up with this code:
// skip checking and just add tag
SaveTag.save({ Name: tag, Desc: "", ParentId: null, GroupId: 12, }).$promise.then(function (data) {
console.log('save tag: ', data);
return data;
}).then(function (data) {
console.log('data: ', data.Id);
GetTag.get({ tagId: data.Id, groupId: 12, }).$promise.then(function (data) {
console.log(' get tag: ', data);
return data;
}).then(function (data) {
console.log('data: ', data.tag);
$scope.tags.push(data.tag);
tagObj = data.tag;
//todo: check for duplicate records
// because repeater will complain and
// it's pointless to have duplicate tags
// on an item to begin with
$scope.myForm.Tags.push(tagObj);
});
});
Notice the $promise after each service call. $promise exposes the raw $http promise that is returned by $resource. This allows me to use .then() to chain additional logic.
So, in the end all I ended up doing is change the way I call my services. That is all.
How can I access object properties from functions which will be called as callback function. Please see code below. How can I access config property from processData function which will called when data is received from the server in ajax call.
MyClass: {
config: {
name: "",
id: ""
},
loadData: function() {
MyApi.getData(
this.config,
this.processData, //sucess
this.failureHandler //failure
);
},
processData: function() {
// how to access config object here?
}
}
Probably you can create an anonymous handler function and use call or apply to pass the this scope to actual handler function but is there a better solution than that?
This is possibly just the way I am accessing the resource object but I have the service below:
angular.module('appointeddPortalApp')
.factory('Salon', function ($resource) {
// Service logic
// ...
// Public API here
return $resource('http://api.appointeddcore.dev/organisation/:id', {id: '#id'}, {
update: { method: 'PUT' },
query: { method: 'GET', isArray: false}
});
});
I'm using the query method like this:
var data = Salon.query($scope.options);
console.log(data);
From the console.log() :
Resource {$get: function, $save: function, $query: function, $remove:
function, $delete: function…}
offices: Array[20]
total: 33
__proto__: Resource
My problem is trying to access total or offices I get undefined
console.log(data.total); // undefined
Because Salon.query() returns immediately with an empty object and updates the variable data if the data is present, try this:
var data = Salon.query(function(callbackdata){
//function is called on success
console.log(callbackdata);
console.log(callbackdata.total);
});
Does anyone know how you can check to see that a resource failed to be fetched in AngularJS?
For example:
//this is valid syntax
$scope.word = Word.get({ id : $routeParams.id },function() {
//this is valid, but won't be fired if the HTTP response is 404 or any other http-error code
});
//this is something along the lines of what I want to have
//(NOTE THAT THIS IS INVALID AND DOESN'T EXIST)
$scope.word = Word.get({ id : $routeParams.id },{
success : function() {
//good
},
failure : function() {
//404 or bad
}
});
Any ideas?
An additional callback function after your first callback function should fire when there is an error. Taken from the docs and group post:
$scope.word = Word.get({ id : $routeParams.id }, function() {
//good code
}, function(response) {
//404 or bad
if(response.status === 404) {
}
});
HTTP GET "class" actions: Resource.action([parameters], [success], [error])
non-GET "class" actions: Resource.action([parameters], postData, [success], [error])
non-GET instance actions: instance.$action([parameters], [success], [error])
Just to answer #Adio 's question too.
The second callback will be called when any http response code is considered to be an error by AngularJS (only response codes in [200, 300] are considered success codes). So you can have a general error handling function and don't care about the specific error.
The if statement there can be used to do different actions depending on the error code, but it's not mandatory.
This is just to inform.
From angular 1.6.x, success and failure is deprecated. So please now follow the then and catch on behalf of success and failure.
So, the above code look like in angular 1.6.x is as below:
$scope.word = Word.get({ id : $routeParams.id }).then(=> () {
//this is valid, but won't be fired if the HTTP response is 404 or any other http-error code
}).catch(=> () {
// error related code goes here
});