AngularJS ngResource custom params passing two '&' to odata service - angularjs

I swear this was working before I upgraded ngResource to 1.3.11...
So my issue is that when I apply a custom filter param to my odata resource request, the filter appends an extra $ before it write the parameter.
http://myservice/odata/Data?&format=application%2Fjson&$filter=year(Date)+lt+2012
The above works just fine through fiddler and is the correct request.
I'm using ngResource to make the get request
data.factory('DataService', ['$resource', function ($resource) {
var odataUrl = 'http://myservice/odata/Data?&format=application%2Fjson';
var d = $resource('', {
filter: '#filter'
}, {
queryByYear: {
method: 'GET',
url: odataUrl, isArray: false
}
});
return d;
}]);
and controller function is as follows:
$scope.getByYear = function (op) {
DataService.queryByYear({ filter: 'year(PublishedDate) ' + op }, function (data) {
$scope.events = data;
});
};
For some reason the output of this request is as follows:
http://myservice/odata/Data?&format=application%2Fjson&$$filter=year(Date)+lt+2012
Notice the second and completely unnecessary & beside the filter param...
any thoughts?

After looking into what was happening when converting my odata request to the proper format, I realized that I had added a third party angular-odata module that was preforming the conversion. The newer angularjs $http already handles this conversion. Doh!

Related

How to contact a non-standard API using Angular ngResource

The API I am using requires a non-standard where clause if I try to search for a particular non-id field. The endpoint I need is:
http://127.0.0.1:4001/api/testusers/findOne?userName=Anton
So this will find me the first record in the testusers table whose column (userName) = 'Anton'.
My standard service is:
angular.
module('shared.testUser').
factory('TestUser', ['$resource',
function($resource) {
return $resource('http://127.0.0.1:4001/api/testusers/:id', {id:'#id'},//parameters
{
update: {
method: 'PUT' // To send the HTTP Put request when calling this custom update method.
}
});
}
]);
and my calling function is:
self.checkUsersEntryDirection = function(){ //NOT WORKING
self.testuser = TestUser.get({ username: 'anton' }, function() {
console.log(angular.toJson(self.testuser));
}); // get() returns a single entry
}
Clearly this doesn't work and I can't use the standard get approach. Can anyone think how this can be achieved?
You could create a secondary factory TestUserByName, and make the following changes:
angular.
module('shared.testUser').
factory('TestUserByName', ['$resource',
function($resource) {
return $resource('http://127.0.0.1:4001/api/testusers/findOne?userName:username', null,
{
update: {
method: 'PUT' // To send the HTTP Put request when calling this custom update method.
}
});
}
]);
Call the get action method with two parameters:
var params = {id: "findOne", username: "anton"};
self.checkUsersEntryDirection = function(){
self.testuser = TestUser.get(params, function() {
console.log(angular.toJson(self.testuser));
}); // get() returns a single entry
}
The id parameter will override the default and username parameter will be added as a query string.
From the DOCS:
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.
--AngularJS ngResource $resource Service API Reference

how to have angularJS post data to MVC controller which redirects to a view

I am posting some data to an MVC action method using AngularJS. This action method will either show its backing view or redirect to another page. Currently all that is happening is the data is getting posted but the redirect is not happening via MVC. I am getting this done using angular's window.location method. I want to know if there is a better way or if I need to post differently using Angular.
On page A I have angular scripts posting data to page B like below:
serviceDataFactory.POST('http://localhost:1234/home/B', someData, pageConfig).then(function () {
//on success
window.location = 'http://localhost:1234/home/Index';
},
function() {
//on error
window.location = 'http://localhost:1234/home/B';
});
This is my service factory
app.factory('serviceFactory', function($http, $q) {
var service = {};
//POST
service.POST = function (url, postData, conf) {
var d = $q.defer();
$http({
method: 'POST',
url: url,
data: postData,
config: conf
}).success(function(data) {
d.resolve(data);
}).error(function(error) {
d.reject(error);
});
return d.promise;
}
return service;
}
);
On Page B I want to redirect to another page. This is my page B in MVC
[HttpPost]
public ActionResult B(string someData)
{
//recieve string someData and perform some logic based on it
.
.
.
if(boolCondition)
return RedirectToAction("Index", "Home");
else
return View();
}
Here once Angular posts to the action method B, it executes all the code all the way till the if(boolCondition) statement. Since I am unable to have that redirect affected via MVC, I do that in Angular itself using the success or error block that the promise returns to.
I want to know if there is a better way to do this or if I am doing something wrong here or if this is the only acceptable way. How do I get angular to hand-off to the MVC action method and let further redirects continue from there only?
You should not use the .success() / .error() pattern with $http, because this has been deprecated. Instead, use then() with two arguments, the first argument being the success function and the second being the error function.
The $http legacy promise methods success and error have been
deprecated. Use the standard then method instead. If
$httpProvider.useLegacyPromiseExtensions is set to false then these
methods will throw $http/legacy error.
You do not need to promisify the result of $http, because $http returns a promise. Just return $http from your service.
app.factory('serviceFactory', function($http, $q) {
var service = {};
//POST
service.POST = function (url, postData, conf) {
return $http({
method: 'POST',
url: url,
data: postData,
config: conf
});
}
return service;
});
Your Page A controller will work the same as before with this new simplified code. At the server, be sure to emit a 500 http status code in cases where you want to trigger the
function() {
//on error
window.location = 'http://localhost:1234/home/B';
}
to run. The 500 in the headers of the response will cause the AngularJS promise to run the second function in your controller.

Getting a single result with angularjs factory in MEAN stack

I'm trying to grab a single result from my expressjs api from within my AngularJS factory.
The factory looks like this and grabs all posts from my api(written in expressjs and getting data from mongodb), which is working fine:
angular.module('bonsaiService', ['ngResource']).
factory('bonsaiService', function($q,$resource) {
var bonsaiResource = $resource('http://localhost:8888/api/bonsais/:bonsaiId',{},{
get:{
method: 'GET',
params:{bonsaiId:''},
isArray: true
}
});
return {
get:function(){
var q = $q.defer();
bonsaiResource.get({
},
function(resp){
q.resolve(resp);
},function(httpResponse){
q.reject(httpResponse);
});
return q.promise;
}
//find by id
};
});
What i've tried so far is adding :bonsaiId after the $resource url and adding params for that id like this: params:{bonsaiId: ''}.
The server part (expressJS) look like this:
router.route('/bonsais/:bonsaiId')
.get(function(req,res){
Bonsai.findOne(req.params.bonsaiId,function(err,bonsai){
if(err)
res.send(err);
res.json(bonsai)
})
})
When I call a local url (with and existing _id from mongodb) it works fine and returns my data in json :
http://localhost:8888/api/bonsais/536be2e2ae54668818000001
Now in the controller im trying to get this data in my scope, which is not working.
bonsaiService.get({bonsaiId:$routeParams.bonsaiId}).then(
function(data){
$scope.trees = data;
console.log(data);
});
How do I make this work?
You could use a more simple approach here.
The query method for $resource already defines a GET on an array, which is QUERY.
Why not write your service this way :
.factory('bonsaiService', ['$resource',
function($resource) {
return $resource('http://localhost:8888/api/bonsais/:bonsaiId', {
bonsaiId: '#bonsaiId'
});
}
])
And in your controller, it would work like this :
bonsaiService.query({
bonsaiId: $routeParams.bonsaiId
}, function success() {
//Your code
}, function err() {
//Your code
});
Don't forget to inject the service in the controller or the app file, if it's not done already.

AngularJS ngResource not sending custom header

I'm attempting to use ngResource to query a REST API. I need to specify my API key in a custom header. I've tried it like so:
angular.module('ApiService', ['ngResource'])
.factory('Api', ['$resource', function($resource) {
this.apiEndpoint = '';
this.apiKey = '';
return {
init: function(apiEndpoint, apiKey) {
this.apiEndpoint = apiEndpoint;
this.apiKey = apiKey;
},
get: function(collection) {
return $resource(this.apiEndpoint + 'api/1/' + collection, {},
{
get: {
method: 'JSONP',
headers: {'api_key': this.apiKey},
params: {callback: 'JSON_CALLBACK'}
}
}
).get();
}
};
}]);
which I then use in my controller like:
app.controller('MyCtrl', function ($scope, Api, ENV) {
Api.init(ENV.apiEndpoint, ENV.apiKey);
var widgets = Api.get('widgets');
});
My custom header isn't set when I inspect the XHR. Also, why will the XHR not run until I call an empty .get() after the initial $resource:get() method?
I've also tried to set the headers in $httpResource directly:
.config(function($httpProvider) {
$httpProvider.defaults.headers.get = {'api_key': 'abc123'};
})
but this still doesn't set the custom header when I inspect the network request. What am I missing?
This issue is, of course, that I was using JSONP in this request, which doesn't include the ability to craft headers when making a request. See how to change the headers for angularjs $http.jsonp.
Specifically, JSONP simply includes a <script> tag at the bottom of the DOM to load cross-domain javascript, so it's up to your browser to send the default headers.

query string in $resource url

my service has to use a query string due to limitations on the server that runs classic ASP:
angular
.module('myServices', ['ng', 'ngResource'])
.factory('Item', ['$resource',
function ($resource) {
return $resource('/api/?p=item/:id');
}]);
and I want to add extra query string parameters to it:
Item.query({test: 123}, on_success, on_error);
but the resulting url is
/api/?p=item?test=123
apparently there is a bug, but how to get around it?
EDIT: filed this at https://github.com/angular/angular.js/issues/1511
You can use resource parameters. If you haven't specified placeholders in the path, they would automatically be converted into query string params. Like that:
angular
.module('myServices', ['ng', 'ngResource'])
.factory('Item', [
'$resource',
function ($resource) {
return $resource('/api');
}]);
Item.query({p: 'item/1'});
This would result in a request to /api?p=item/1.
P.S.
I suppose you already know that, but you don't like it. But I still think this is the correct way in your case. Considering the bad API design you are dealing with that back-end you could wrap the AngularJS resources with another service which does this for you.
var deferred = $q.defer();
api.api_name.query({
'param':param_value
},
function(response) {
deferred.resolve(response);
},
function(response) {
deferred.reject(response);
}
);
//
angular
.module('module_name')
.factory('api',function($resource){
var api_var={};
api_var.api_name = $resource('url?param_key=:param', {
param: '#param'
}, {
'query': {
method: 'get'
}
});
return api_var;
});

Resources