Passing and retrieving complex objects from Angularjs to Web Api - angularjs

I have a form in angular where a user enters various criteria which I then want to pass to Web Api and get a result after queries are run. I originally thought of this as a "Get" but had trouble passing complex objects to the Web Api. With some advice, I then used a Post and was able to pass the criteria run the query in the Web Api but I had trouble getting the result back in Angular. The Web Api method is run and gets the results. But I don't see the results in the data service.
What is the best approach where the criteria for a query is multiple fields and some are lists? I haven't been able to find any good examples.
Here is the Web Api method:
[HttpPost]
public IEnumerable Post([FromBody] FrequentPawnerReportCriteria criteria)
{
var repo = new FrequentPawnerReport();
var result = repo.GetReport(criteria);
return result;
}`
Here is the dataservice:
function getFrequentPawner(criteria) {
return $http.post("/api/FrequentPawner/Post", criteria)
.then (getFrequentPawnerComplete)
.catch(getFrequentPawnerFailed);
function getFrequentPawnerComplete(response) {
var x = response
return response.data.results;
}
function getFrequentPawnerFailed(error) {
alert("XHR failed for frequent pawner report: " + error.responseText);
}
}
And here is the controller code:
function getTopPawnerResults(criteria) {
return DataContext.getFrequentPawner(criteria)
.then(
function (result) {
vm.frequentPawnerReport = result.data;
return vm.frequentPawnerReport;
});
}

Simply use JSON. Use JSON.stringify() to parse JSON object to string and POST it. Similarly, return JSON string from server, and assign it to variable in Angular. It will be automatically converted to JSON object.

I think when you make your post request, you need to have a callback function that would get invoked when your Web Api returns. Within that callback function you can update your $scope variables which will make your web ui show the response from the server. You can find an example of what I mean here: https://docs.angularjs.org/api/ng/service/$http
The gist of it:
$http({
method: 'POST',
url: '/path/to/your/web/api',
function(success) {
console.log('Successfully executed the api call');
$scope.response = response; // change this to match the data you are expecting from the server response
},
function(failure) {
console.error('There was an error');
$scope.failure = failure; // change this to match your failure response
}
);

Thanks for responding. The project is a mix of web forms and angularjs. I am migrating the app and didn't notice this form had a conflict which was causing a post back and making it look like the result was not being returned. I took the form into a separate project and was able to get the results I was going for.

Related

Angularjs $http put request to REST api using mongoose mongodb

Could someone share an alternative update api with me? I have a model named "performers" which has a “score” property. I want to increment the score with a request from an Angularjs controller to the Mongo DB. I can't quite find a way to do the incrementing.
I've looked a ton of places .. here is the mongoose documentation
http://mongoosejs.com/docs/2.7.x/docs/updating-documents.html
this another giving some clues about argument syntax
http://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
and mongo speaking directly to the $inc
https://docs.mongodb.org/v3.0/reference/operator/update/inc/#up._S_inc
heres is the request I am trying (from client side angular controller) with
example data passed ( the keys are mongodb ids)
performerScoreDeltas = { 343l88jkjkjkuuiui: -20, iii88909jkmmmkjjkj: 13, iooiuo8889ee888: -9, popopbi99r8765: 7, 898989kv908098: 5, 798v8978fw79798: 25, a90va089898as9: 9 }
//from my angularjs controller for the page
$scope.updateDbScores = function(performerScoreDeltas){
for(var eventScore in performerScoreDeltas){
$http.put('/api/performers/' + eventScore, {$inc:{score: performerScoreDeltas[eventScore]}})
}
}
// from my api for the performer model
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Performer.findById(req.params.id, function (err, performer) {
if (err) { return handleError(res, err); }
if(!performer) { return res.status(404).send('Not Found'); }
var updated = _.merge(performer, req.body);
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.status(200).json(performer);
});
});
};
I've been getting 200 responses for the updates, yet the score property isn't changed. The requests get to the right place though and looking at my chrome dev tools I see the request is carrying a "payload" of {$inc:{score:-20}} for an id I want to decrease the score by by 20 etc.
Question: Do I need to make a special API route and somehow hardwire more of the $inc into it and only have the request pass data?
Here is the angularjs $http documentation
https://docs.angularjs.org/api/ng/service/$http#put
I'm stuck. The needed information seems to be getting there but maybe it is because my api controller is using the save method instead of the update method?
Could someone share with me an alternative RESTFUL api code that uses update instead? I'll admit that the block I use comes from a yeoman package. It does work well for normal updates I have implemented and even worked editing the _.merge (hmm... maybe I could change the merge to include the $inc? ...but it would need data from the params)

How to authenticate a HTTP request to an OrientDB function on AngularJS?

I have the following OrientDB function:
http://localhost:2480/function/Application/getPassFailCount/9:600
And it returns the following JSON result:
{"result":[{"#type":"d","#version":0,"pass":16.0,"fail":2.0,"#fieldTypes":"pass=d,fail=d"}]}
What I need to do is to get the values of "pass" and "fail" to use in my web page.
So far I have done this with AngularJS:
$http.get('http://localhost:2480/function/Application/getPassFailCount/9:600').
success(function(data) {
$scope.data = data.result;
// $scope.passCount = ;
// $scope.failCount = ;
});
Currently it gives the error "401 Unauthorized". How do I authenticate the request?
And if possible, can anyone give some tips on how to get the passCount and failCount from the JSON result returned?
The OrientDB HTTP API documentation states that you have to use HTTP Basic authentication for issuing commands. That means you have to include an Authorization header along with your request.
There are a few ways to achieve this, here is a simpler one. Use the configuration object parameter for $http.get to set the header on the request:
function base64(str) {
return btoa(unescape(encodeURIComponent(str)));
}
$http.get('http://...', {
headers: { 'Authorization': 'Basic ' + base64(user + ':' + password) }
}).success(...);
You should definitely move all your database logic to an Angular service, so you can keep this code in one place instead of polluting your controllers.
To make it even cleaner, you could look into $http interceptors and write a request interceptor that adds the header to every HTTP call.
Regarding the JSON question: you can see that the result object contains an array with a single element. Use indexing to get the actual record.
var result = data.result[0];
$scope.passCount = result.pass;
$scope.failCount = result.fail;
If you wrote a service as I mentioned, you could hide this implementation detail from your controller.
function getCount() {
return $http.get(...).then(function (data) {
var result = data.result[0];
// the caller will only see this simpler object
return { pass: result.pass, fail: result.fail };
});
}

angularjs custom REST action and error handling

I'm having some trouble with error handling in a little angularjs application. I'm interacting with a Flask backend and a Postgres DB.
I have a factory service
appointServices.factory('Appointments', ['$resource', function($resource){
return $resource(someUrl, {}, {
query: { ... }
,
create: {
method: 'POST'
,url: 'http://somedomain:port/new/:name/:start/:end/:treatment'
,params: { start: '#start', end: '#end', name: '#name', treatment: '#treatment' }
,isArray:false
}
});
}
]);
Inside a controller I'm making the following call
Appointments.create($scope.appointment, function(value, responseHeaders) {
// success handler
console.debug('success: ', JSON.stringify(value));
}, function(httpResponse) {
// error handler
console.debug('error: ', JSON.stringify(httpResponse));
});
Here $scope.appointment contains the relevant parameters for the create action.
Now, in the backend I'm able to catch DB errors involving constraints and I'm trying to return an error code with a 'meaningful' message. So I have a python method
def create(name, start, end, treatment):
try:
...
transaction_status = 'ok'
code = 200
except IntegrityError as e:
...
transaction_status = 'IntegrityError'
code = 500
finally:
...
return make_response(transaction_status, code)
Everything works fine, I'm able to talk to the backend, create new data and insert this in the DB. As I said, any violation of the constraints is detected and the backend responds
curl -X POST "http://somedomain:port/new/foo/bar/baz/qux" -v
...
< HTTP/1.0 500 INTERNAL SERVER ERROR
...
IntegrityError
So, the problem is, no matter whether the action create was successful or not, the intended error handler specified inside the controller is always fired. Moreover, I always end up with a status code 404 in the httpResponse. Firebug shows correctly the code 500 as above, though.
Anybody has any idea of why I'm getting this behavior?
Any suggestions on how to improve the error handling mechanism are also welcome.
Thx in advance.
P.S. Following the documentation on $resource I have also tried variations on the factory service call, e.g.
Appointments.create({}, $scope.appointment, successCallback, errorCallback);
Appointments.create($scope.appointment, {}, successCallback, errorCallback);
with the same results.
Update:
Forgot to mention the important fact that I'm interacting with the backend via CORS requests. The POST request in create above is having place with the OPTIONS method instead. As I mentioned everything is working correctly except for the error response.
Under further investigation, I tried to isolate the factory service, in case I did something wrong, and I also tried the approach shown in the credit card example ($resource docs), but with no positive result.
However, I came up with two workarounds. Firstly, I was able to create a plain JQuery POST request, as in the example shown in the docs. This time, the request is not replaced by OPTIONS and I got the error code correctly.
I also managed to connect to the backend with the low-level $http service as follows:
var urlBase = 'http://somedomain:port/new/:name/:start/:end/:treatment';
var url = urlBase.replace(/:name/g, $scope.appointment.name);
url = url.replace(/:start/g, $scope.appointment.start);
url = url.replace(/:end/g, $scope.appointment.end);
url = url.replace(/:treatment/g, $scope.appointment.treatment);
// force method to be POST
var futureResponse = $http({ method: 'POST', url: url });
futureResponse.success(function (data, status, headers, config) {
console.debug('success: ', JSON.stringify(data));
});
futureResponse.error(function (data, status, headers, config) {
console.group('Error');
console.debug(JSON.stringify(status));
console.debug(JSON.stringify(data));
console.groupEnd();
});
This time, as in the case of JQuery, the request is done effectively with POST and error codes are correctly received.
Notice also that I'm not calling $http.post but I set the method to POST as part of the object parameter to $http, otherwise the connection takes places with OPTIONS as before.
Still trying to figure out what is happening with $resource.

AngularJS Execute function after a Service request ends

I am using AngularJS Services in my application to retrieve data from the backend, and I would like to make a loading mask, so the loading mask will start just before sending the request. but how can I know when the request ends?
For example I defined my servive as:
angular.module('myServices', ['ngResource'])
.factory('Clients', function ($resource) {
return $resource('getclients');
})
.factory('ClientsDetails', function ($resource) {
return $resource('getclient/:cltId');
})
So I use them in my controller as:
$scope.list = Clients.query();
and
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
});
So the question would be, how to know when the query and get requests ends?
Edit:
As a side note in this question I've been using using angularjs 1.0.7
In AngularJS 1.2 automatic unwrapping of promises is no longer supported unless you turn on a special feature for it (and no telling for how long that will be available).
So that means if you write a line like this:
$scope.someVariable = $http.get("some url");
When you try to use someVariable in your view code (for example, "{{ someVariable }}") it won't work anymore. Instead attach functions to the promise you get back from the get() function like dawuut showed and perform your scope assignment within the success function:
$http.get("some url").then(function successFunction(result) {
$scope.someVariable = result;
console.log(result);
});
I know you probably have your $http.get() wrapped inside of a service or factory of some sort, but you've probably been passing the promise you got from using $http out of the functions on that wrapper so this applies just the same there.
My old blog post on AngularJS promises is fairly popular, it's just not yet updated with the info that you can't do direct assignment of promises to $scope anymore and expect it to work well for you: http://johnmunsch.com/2013/07/17/angularjs-services-and-promises/
You can use promises to manage it, something like :
Clients.query().then(function (res) {
// Content loaded
console.log(res);
}, function (err) {
// Error
console.log(err);
});
Another way (much robust and 'best practice') is to make Angular intercepting your requests automatically by using interceptor (see doc here : http://docs.angularjs.org/api/ng.$http).
This can help too : Showing Spinner GIF during $http request in angular
As left in a comment by Pointy I solved my problem giving a second parameter to the get function as following:
$scope.datails = ClientsDetails.get({
date:$scope.selectedId
}, function(){
// do my stuff here
});

AngularJS unit testing: $httpBackend.when dynamic responses

I'm playing with AngularJS unit testing when developing Feed (RSS) application. The Feed can fetch remote rss data parse it and save parsed items. For testing rss fetching I use $httpBackend mock:
beforeEach(inject(function (_$httpBackend_, _feedMock_, _mockConfig_) {
_$httpBackend_.when('GET', _mockConfig_.FEED_URL).respond(_feedMock_);
}));
and then below
$httpBackend.expectGET(mockConfig.FEED_URL);
feed.fetch();
$httpBackend.flush();
it works fine.
But I need to make the Feed can actualize it's state by fetching updated rss data and appending new items. So, the Feed make the same request, but got new updated data. I'm trying to recreate server definition by something like this:
$httpBackend.when('GET', _mockConfig_.FEED_URL).respond(feedMockUpdated);
and then make the same operation with expect and flush, but $httpBackend response with old data (feedMock), not new (feedMockUpdated). How can I make $httpBackend to response with the different data on the same request?
You can set up respond with a function, not just a static data set.
From the docs:
http://docs.angularjs.org/api/ngMock.$httpBackend
respond – {function([status,] data[, headers])|function(function(method, url, data, headers)} – The respond method takes a set of static data to be returned or a function that can return an array containing response status (number), response data (string) and response headers (Object).
So as I understand in your case, you want to have the same endpoint return different data on later calls, you can try something like the following. This is just implementing a simple counter that will switch the data on a subsequent call.
var rssCalled = 0;
$httpBackend.when('GET', _mockConfig_.FEED_URL).respond(function(method, url, data, headers){
if(rssCalled === 0) {
rssCalled++;
return [200, feedMock, {}];
} else {
return [200, feedMockUpdated, {}];
}
});

Resources