Post falsy value with angular resource - angularjs

I'm trying to post the scalar value 0 with angular resource, e.g.
myResource.save({}, 0, onSuccess, onError};
This gives a HTTP 400 error for a POST attempt with empty body.
Looking into the angular sources I see that my post data ultimately becomes the argument to the constructor of Resource, which looks like
function Resource(value){
shallowClearAndCopy(value || {}, this);
}
So it seems all falsy values will be replaced by an empty object and thus cannot be posted. Is this by design (and then: which part of the documentation did I miss?) or a bug?

The point of $resource is best illustrated when the instance methods are used instead of the class methods. Take this example from the docs:
var CreditCard = $resource('/user/:userId/card/:cardId',
{userId:123, cardId:'#id'}, {
charge: {method:'POST', params:{charge:true}}
});
...
var newCard = new CreditCard({number:'0123'});
newCard.name = "Mike Smith";
newCard.$save();
Essentially newCard is just a plain object representing a model, but enriched with some functionality. $resource provides a layer of abstraction. It's an implementation of the Active Record Pattern.
That said, if you want to post a single scalar value, then chances are that you are dealing with a service rather than a resource. In that case it's somewhat obvious that $resource (sic!) isn't the right choice.

Related

Passing a Complex Parameter into a Query in AngularJs

I have seen 100 examples of passing an ID into $resource.get() in order to query information out of a back-end in Angular. What I have not been able to find is how to pass a complex object.
If I have a table of objects to return, and I wish to run a search against them using multiple items of filter, I need to pass those items as parameters or as one complex parameter. For example, say I have a table of people's names and their cities, states, etc. I want to be able to say something like this:
var myResource = $resource(url);
myResource.get({name : "Mike", state : "Texas"});
The return may be a single row or multiple rows. But the point is how do I get the parameters off to the API call?
The way I have other methods set up that are simpler is by creating a repository in which I return like so:
return resource('/api/broker/productionInfo/');
Then in my API I do this (after the [RoutePrefix("api/broker")] setup:
[HttpGet]
[Route("productionInfo")]
public IHttpActionResult GetProductions()
{}
That's all awesome but I want to be able to add the search criteria in the repository call and then in the API method (i.e. extract from a querystring or however it is to be passed).
If I understand what you are asking correctly, you just want to pass additional parameters into an angular resource get request. It is as simple as what you have already suggested:
resource.get({id: 1, custom_param_1: "param1", custom_param_2: "param2"});
This would result in an http request that looks like this:
/resource/1?custom_param_1=param1&custom_param_2=param2
You can then extract these parameters on the API side of things.
Something to note is that get requests have a maximum length, and if you are attaching lots of different parameters to the request, it may be better to use a post or put instead.
The only thing I'm seeing that you're missing is a [FromUri] decorate attribute, in your GetProduction API method. Since Get supports only params binding through a query string (no body binding).
Your params:
options: {
StartDate: _startDate
EndDate: _endDate
TextSearch: "some search query....",
Page: 1,
PageSize: 25,
et...
}
Then, calling your repository from your controller:
repository.get(options).$promise.then(function (data) {
// data = response payload from backend
});
reposiroty
....
return resource('/api/broker/productionInfo/');
....
API
[HttpGet]
[Route("productionInfo")]
public IHttpActionResult GetProductions([FromUri] SearchCriteriaModel criteria) {
....
}
Hope that helps.

Accessing promise's elements which are objects and not simple types

I have a study, which has multiple cases and multiple executionMessages. Also each case has multiple executionSteps. I am able to access the study, the case and even each case's executionSteps. I cannot figure out why I cannot access the complete executionMessages. By that I meant each executionMessage has a type, message which are accessible but any objects inside executionMessage is not accessing. Here it the code
StudyService.studies.get({id: $routeParams.studyIdentifier}).$promise.then(function(study) {
$scope.study = study;
StudyService.executionMessagesForStudy.get({id: study.id}).$promise.then(function(executionMessages){
$scope.study.executionMessages = executionMessages;
});
for(var i=0;i<study.cases.length;i++){
var callback = callbackCreator(i);
StudyService.executionstepsForCase.get({id: $routeParams.studyIdentifier,caseId:study.cases[i].id})
.$promise.then(callback);
}
});
function callbackCreator(i) {
return function(executionSteps) {
$scope.study.cases[i].executionSteps = executionSteps;
}
}
It looks like you are using $resource for your service types (a guess at the .get().$promise pattern you are using), From the docs:
...invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view.
see: https://docs.angularjs.org/api/ngResource/service/$resource
So your code can be simplified like this:
// get() returns an object that will "fill-in" with the attributes when promise is resolved
$scope.study = StudyService.studies.get({id: $routeParams.studyIdentifier})
$scope.study.$promise.then(function(study) {
// again, executionMessages gets a placeholder that will be filled in on the resolved promise
$scope.study.executionMessages = StudyService.executionMessagesForStudy.get({id: study.id});
angular.forEach(study.cases, function(case, idx) {
// again, executionSteps is a placeholder that will be filled in on the resolved promise
var executionSteps = StudyService.executionstepsForCase.get({id: $routeParams.studyIdentifier, caseId: case.id})
// not sure if ordering is important, the service calls can return in any order
// so using push can produce list with elements in any order.
$scope.study.cases.push(executionSteps);
});
});
You still need to use the study.$promise to get the nested data after the first service call resolves.

restangular filter object to array

I have this big resource object that has lots of methods inside of it with more objects, etc. I'm also converting xml into json for this.
One in particular is a resourceType object that sometimes has more than one resourceTypes. When 2 or more are present, it's an Array. When it's 1 it's just an object and I can't loop through it consistantly in my view.
I made a filter that checks if it's just an object then casts it to an array if it's not already one i'm just having trouble how to put it in my service call. Right now I have it has:
getResources: function(resourceId){
var self = this;
return Restangular.one('resource/resourceState', resourceId).get().then(function(response){
$filter('castToArray')(response.resources.resourceState.resourceTypes)
self.resources = response;
return self.resources;
});
},
This doesn't work. I want to maintain the integrity of the entire resources object but I want the resourceTypes method/object to cast to an array if only 1 is present.
In case anyone stumbles on this the fix was quite simple.
I just set the above to:
getResources: function(resourceId){
var self = this;
return Restangular.one('resource/resourceState', resourceId).get().then(function(response){
self.resources = response;
self.resources.resourceState.resourceTypes = $filter('castToArray')(response.resourceState.resourceTypes);
return self.resources;
});
},
I just needed to set the the self.resources method to the response method with the filter casted on it. Okay I'm done.

angularjs trying to understand and save a resource state with params

here's the relevant snippet of code:
$scope.submit = function() {
console.log(this);
weekly_habits = $resource('/api/users/:user_id/weekly_habits/:id/', {user_id: '#user'});
entry = weekly_habits.save({name: $scope.newAccomp, count: 0});
$scope.accomplishments.unshift(entry);
$scope.newAccomp = '';
}
my error is that no route matches /api/users/weekly_habits... My project is built in rails and I don't understand what this line means {user_id: '#user'}. Where is this #user object supposed to be? It would be much appreciated if someone explained what's going on here.
Thanks!
From the documentation:
If the parameter value is prefixed with # then the value of that parameter is extracted from the data object (useful for non-GET operations).
And later on they have an example
var User = $resource('/user/:userId', {userId:'#id'});
So the second argument to the resource service are the default parameters. In this case, by default, the userId will be extracted from the resource object you call a method on.
Previously I had an example of a GET operation, which doesn't really make sense in this case. But imagine you were doing a POST ($save) request with a data object. Angular would automatically extract the userId from the data object and inject it into the URL for you. You don't have to manually specify the value for the URL.
More info can be found on the doc page.
http://docs.angularjs.org/api/ngResource.$resource
Quoting the documentation: "If the parameter value is prefixed with # then the value of that parameter is extracted from the data object (useful for non-GET operations)."
The '#user' tells Angular to extract the value of user_id from the 'user' property of the object. So if you call .save() on an object that has an 'user' property of 'abc123', then the :user_id in your URL will be replaced with 'abc123'.

Simultaneously retrieving multiple data using $q in angular

Folks ..
I need to retrieve data from around 8 difference resources when my program begins.
These resources are independent of each other can can be called parallelly. (i.e I don't need data from one to determine what to retrieve from another resource)
However anything ahead needs to ensure that I have all the data from all the resources on-hand since they operate on each other.
So at the beginning of my controller I call a init function with the following code :
*EDIT To be more specific my exact code is as below
$scope.init = function () {
return $q.all([
Factory1.getCarData.query(), // returns a resource object like [$resolved: false, $then: function]
Factory2.getOtherData.query(), // returns a resource object like [$resolved: false, $then: function]
Factory3.getSomeOtherData.query() // returns a resource object like [$resolved: false, $then: function]
....,
resource8.query()]).then(result) {
$scope.data1 = result[1];
$scope.data2 = result1[2];...
$scope.data8 = result[3];
console.log($scope.data1); //prints as [$resolved: false, $then: function]
console.log($scope.data1[1]);
prints as undefined
doSomethingonData1($scope.data2);
doSomethingonData2($scope.data3, $scope.data4);..etc etc
}
}
Where Factory1 is defined as :
angular.module('app').factory('Factory1', function (Factory1Resource) {
var carPromise = Factory1Resource.query();
return {
getCarData: function(){ return carPromise;}
}
and Factory1Resource is defined as :
.factory('Factory1Resource', ['$resource', function($resource) {
return $resource(myURL, {}, {} );
}])
The whole point of using the factories is to ensure that the data manipulation for all 8 resources is done outside the controller in a indiviual units.
My point is .. I thought that the ".then" function would be invoked only once all resources have resolved. which means that my variables $scope.data1, $scope.data2 etc should have the actual data and not be resource objects.
This is not the case as when I do console.log($scope.data1) .. it prints as [$resolved: false, $then: function]
This breaks the flow of my program.
Now I thought I had done a lot of reading on promises and resources and I was now a enlightened person but apparently i am missing something here.
What I want is that my variables ($scope.data1, $scope.data2 etc) all contain actual data.
Any hints ? Alternately feel free to suggest any better ideas you may have as to how I should lay out my code.
then() takes a function, do it like this
.then(function(result) {
$scope.data1 = result[1];
...
});
Created a demo for you and I simplified a bit of your code. It works fine. Demo
#sza thank you so much for the advice and help here. This does seem the right way to do it however to have a fool proof system in place I did the below :
Set flags in the ".then" section of all Factory.getData.query() - so now code looks like:
Factory1.getCarData.query().then(function(){Data1Retrieved == true})
After this, I keep a watch on all flags, once all flags turn true I start rest of the processing. Again I don't know if this is the right way to do it but it sure seems to be effective.
Thank you everyone for your input again.
Instead of passing the object returned by resource.query directly to $q.all( you should pass resource.query().$promise.
According to angular $resource documentation
The Resource instances and collection have these additional
properties:
$promise: the promise of the original server interaction that created
this instance or collection.
http://docs.angularjs.org/api/ngResource.$resource
update
I decided to create a test by myself using an existing app I developed. I'm going to paste the code that worked for me. Maybe you have a different structure when you're calling the resources:
$provide.factory('GenericResource', ['$resource','$q', function($resource,$q){
var AccountResource = $resource('/user/accounts/balances'),
BankResource = $resource('/banks');
$q.all([AccountResource.query().$promise, BankResource.query().$promise]).then(function(data){
console.log(data);
});
}]);
What output of console.log is:
[
Array[3]
0: Resource
1: Resource
2: Resource
$promise: Object
$resolved: true
length: 3
__proto__: Array[0]
,
Array[4]
0: Resource
1: Resource
2: Resource
3: Resource
$promise: Object
$resolved: true
length: 4
__proto__: Array[0]
]
As you can see the the data returned contained 3 and 4 resource objects representing the data fetched. Is this result similar to the one of yours?

Resources