Angular $http attach data as object - javascript

I'm trying to get list of tracks from soundcloud API with angularjs.
The parameters i'm trying to send are:
1) client_id (string)
2) duration (object with two properties).
Here's the code:
var CLIENT_ID = 'a81f01ef5d0036415431e8be76c8db0e';
var TRACKS_URL = 'https://api.soundcloud.com/tracks.json';
var app = angular.module('soundcloud', []);
app.controller('tracksController', function ($scope, $http) {
$http({
url: 'https://api.soundcloud.com/tracks.json',
method: 'GET',
data: {
client_id: CLIENT_ID,
duration: { // in milliseconds
from: 300000,
to: 400000
}
}
})
.success(function (data) {
$scope.trackList = data;
})
.error(function () { alert('error'); });
});
These parameters aren't recognized at all when I check the request in the broweser's debugger.
I tried to use 'params' instead of 'data', but this way it turns the 'duration' object to json --> then I get status 500 in response.
When I only send the client_id in params, it works fine because there's no object, only string.
jQuery's ajax method works fine: https://jsfiddle.net/oacwz1up/3/
What should I do ? How can I send the parameters normally ?
Help please! Thanks!

This happens because Angular serializes the request parameters differently than JQuery.
Angular 1.4 will address this problem introducing a paramSerializer property on the $http config object (and on $httpProvider.defaults). This can be used for arbitrarily serializing the requests parameters (for all or a particular request).
Note, that the feature is available only since v1.4.0-rc.0.
Besides the default serializer (which converts objects to JSON strings), there is an additional built-in serializer that emulates JQuery's param(): $httpParamSerializerJQLike.
You can use it like this:
$http.get(<API_URL>, {
params: {...},
paramSerializer: '$httpParamSerializerJQLike'
});
See, also, this short demo.
If you are using an older version of Angular, you could do one of the following:
Construct the whole URL (including the query string) yourself (possibly using an $http request interceptor to automatically apply this to all requests).
Keep the params in a flat format that will result in Angular's serializing them as expected:
var REQ_PARAMS = {
client_id: 'a81f01ef5d0036415431e8be76c8db0e',
'duration[from]': 200000,
'duration[to]': 205000
};

If you look at $http documentation, on request it applies a default transformation which is $http.defaults.transformRequest and as described will do the following (as you saw) :
If the data property of the request configuration object contains an object, serialize it into JSON format.
What you need to do is override this function by specifying your own transformRequest object.
function appendTransform(defaults, transform) {
// We can't guarantee that the default transformation is an array
defaults = angular.isArray(defaults) ? defaults : [defaults];
// Append the new transformation to the defaults
return defaults.concat(transform);
}
$http({
url: '...',
method: 'GET',
transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
return doTransform(value);
})
});
You need to find a way to get the same syntax as jQuery provide which is :
https://api.soundcloud.com/tracks.json?client_id=a81f01ef5d0036415431e8be76c8db0e&duration[from]=200000&duration[to]=205000
Use a condition is is an object and generate manually your String. Should not be difficult.

that's a strange API - I don't know why they don't do something like "duration_from" rather than requiring duration[from] - as suggested you could certainly transform the request, but if this is just a one off you could also try simply hard-coding it using url escaped values for [ and ]:
var dataToSend = {
client_id: 'a81f01ef5d0036415431e8be76c8db0e',
'duration%5Bfrom%5D': 200000,
'duration%5Bto%5D': 205000
};

$http.get('http://api.soundcloud.com/tracks.json', {params:dataToSend});
The params property is way to transform query in restful way. Just transform data in dataToSend object, and it will work.
This is a URL that should be created:
https://api.soundcloud.com/tracks.json?client_id=a81f01ef5d0036415431e8be76c8db0e&duration%5Bfrom%5D=200000&duration%5Bto%5D=205000

Related

Angular & ASP.NET MVC - Parameter is null when I pass a parameter to my backend Controller

If you look at the parameter of my ASP.NET MVC Controller clientId, it's always null.
the only way i can get it to not be null and actually pass the data through successfully is to create a class... but that gets tedious and I can't create a class for every backend function i make just to get this to work.
Is there a way to pass data successfully without creating a class?
Thank you for any help
Angular Factory
PlaylistsFactory.getUsersForClient = function (clientId) {
return $http({
method: 'POST',
url: '/Show/GetUsersForClient',
data: JSON.stringify(clientId)
});
};
Angular Controller
PlaylistsFactory.getUsersForClient(clientId)
.success(function (userList) {
console.log('success!');
});
ASP.NET MVC Controller
public JsonResult GetUsersForClient(string clientId) //clientId is always null unless i create an object
{
...
}
Try making your JSON parameter match the name of your C# parameter as well as encasing that in the data payload as JSON:
return $http({
method: 'POST',
url: '/Show/GetUsersForClient',
data: {clientId: JSON.stringify(clientId)}
});
};
i would recommend that you follow the rules of a RESTful API.
This means you should use the HTTP verbs like GET (getting data), POST (updating data), PUT (creating data), DELETE (deleting data). See http://www.tutorialsteacher.com/mvc/actionverbs-in-mvc
Then you could also add the parameter you want to pass into the route of your API: /Show/GetUsersForClient/{clientId}. See http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx
In this case you disengage the problem of sending data in the body without having a ViewModel on the MVC-Controller side.
When you want to proceed with your solution, then try creating the Object before sending it:
PlaylistsFactory.getUsersForClient = function (clientId) {
var payload = { clientId: clientId }
return $http({
method: 'POST',
url: '/Show/GetUsersForClient',
data: payload
});
};
MVC / WebAPI also sometime choke when the content-type in the request header is text/plain or application/json. For example: a json-object will not be recognized properly by .Net when sent in text/plain.

Generating a unique value for a header on each $http call

I have a fairly large AngularJS application and for logging purposes, I am being tasked with adding a custom header to all of our HTTP requests from the app that contain a unique ID for each request. This really is more valuable to our API calls, but as of now I'm just aiming for all requests (getting templates, styles, etc.)
I am currently using a provider decorator to patch each of the methods exposed by $HttpProvider (implementation based on this post), to attempt to call the ID method each time one of those $http methods runs, and add the appropriate header:
module.config([
'$provide',
function ($provide) {
$provide.decorator('$http', [
'$delegate',
function addUniqueIdHeader($http) {
var httpMethods = ['get', 'post', 'put', 'patch', 'delete'];
/**
* Patched HTTP factory function that adds a request ID each time it is called.
* #param {string} method - A valid HTTP method.
* #return {function} A function that sets various request properties.
*/
function httpWithHeader(method) {
return function(url, data, config) {
config = config || {};
config.headers = config.headers || {};
// the magic
config.headers['My-Custom-Header'] = aUniqueId();
data = data || {};
config.method = method.toUpperCase();
// return `$http` with a modified config, adding the URL and data passed in
// `_.extend()` is lodash, not underscore.
return $http(_.extend(config, {
url: url,
data: data
}));
}
};
// back up the orginal methods and patch
_.each(httpMethods, function (httpMethod) {
var backupMethod = '_' + httpMethod;
$http[backupMethod] = $http[httpMethod];
$http[httpMethod] = httpWithHeader(httpMethod);
});
return $http;
}
]);
}
]);
What I have so far works some of the time, but doesn't seem to work consistently (some API requests have it, some don't). I should note that we are using a quite old version of AngularJS (1.0.6) and no, I cannot upgrade (as much as I would love to) so the use of request interceptors is not possible. Additionally, we use Restangular for the majority of our API interactions.
My question is, is using a provider decorator the right way to go? If so, is there a cleaner way to add the header without having to override/patch each individual HTTP method that I'm overlooking?
Thanks in advance.
I ended up solving my issue by utilizing Restangular's request interceptors, since the version of Angular we use doesn't have them baked in.

transform request data with resource service in angular

I'm trying to send just the array of my data to the server. I found this post discussing how to send an array, but I can't get it to work.
AngularJS: ngResource and array of object as params to URL
The problem I am having is my resource gets sent back to me as JSON like this
{
Results: []
}
So when I ask for my resources,
var collaboratorResource = api.CollaboratorList.get({Id: Id });
but then, if I try something like
collaboratorResource.$save($scope.collaborators);
When I look at firebug, it shows that my data is being sent as
{
Results: []
}
when in reality, I don't want to send the data as an object with the array as a Results property. I want to send it just as an array []. I need to do that since the api is legacy and expects that.
I've been trying to see if transformRequest works, like if I did
collaboratorResource.$save({data: $scope.collaborators, transformRequest: function (data, headers) { return data.results; }});
collaboratorResource.$save({}, $scope.collaborators);
collaboratorResource.$save($scope.collaborators);
But that doesn't seem to work either. Is this even possible? As an aside, if I use $http like this, it works:
$http({ method: "POST", data: $scope.collaborators, url: collaboratorUrl });
I'm just not sure how to use the $resource service properly since I'd prefer to wrap everything in $resource if possible and not have a hybrid of both if possible. Thanks.

should I return the entire record again as a response from server to a POST when I do $update on an angular resource?

In my factory
return $resource('rest/records/:id', {}, {
query: {
method: 'GET',
isArray: true,
// RestSQL has an extra struct around the array
transformResponse: function(data) {
return angular.fromJson(data).records;
}
},
create: { method: 'POST' },
update: { method: 'PUT', params: {id: '#id'},
});
Which is used by a service which is in turn used by a controller.
I get the correct record in the controller.
Then when I edit and want to save the record
I do record.$update();
my rest POST is successful as I get a valid response from server.
But the response from server is just some kind of success message e.g. "{ "number": 2 }".
Now I have an angular template which uses this records properties e.g. {{record.image}}
But as soon as I do record.$update();
records properties disapper and record now simply looks like
record = {"number" : 2}
So should I return the entire record as a response to the POST action or should I update in some other fashion so as to the record doesnt get its properties overwritten by the response from server?
A temporary way of getting around this of course is
$scope.temp = jQuery.extend(true, {}, $scope.record);
$scope.temp.$update();
I am returning the updated object on POST's, PUT's etc. (see also this: Should a RESTful 'PUT' operation return something) all the time. So,
no problem with always returning the updated object in the response
don't use tricks and more code to sove a problem that can be solved by applying a very simple solution (in this case, returning the updated object). I love those fixes: Simple solution, problem solved and code removed instead of adding things here and padding things there etc., making code unreadable und unnecessarily complex.

Data is not saved properly in MogoDB from Angular js through Node.js

I am building an application with Angular.js,Node.js and MongoDB.
I am sending a set of data to MongoDB as below.
$http({
method: 'POST',
url:'http://localhost:2000/postsharedata', data:({event:$scope.event,date:$scope.eventdate,expenselist:$scope.expenses,expense:$scope.totalAmount,sharelist:$scope.sharelists,shareno:$scope.share()}),
}).success(function(data) {
console.log(data);
});
expenselist while sending to MongoDB is as follows:
[Object { text="a", done=true,$$hashKey="00I",$oldValue="5000",$value="5000"}]
But in mongoDb,data is like [ { "text" : "a", "done" : true }]
Why $oldValue and $value are not getting stored?
Please Advice
If you use the $http post service to submit your data to your server, angular converts your data and strips all properties that are prefixed with at least one $ character. This is documented in the toJson function (http://docs.angularjs.org/api/angular.toJson).
If you want to submit your data regardless the leading $ character you have to change the way how angular transforms your data before the submit happens. This can easily be done by using the transformRequest config parameter:
$http({
method: 'POST',
url:'http://localhost:2000/postsharedata',
data:(... your data ...),
transformRequest: function(data){return data}
})
More information in the $http service documentation.

Categories

Resources