Passing object from AngularJS to ASP.NET MVC Controller not working - c#

I am sending data from angularJS to MVC controller (I see that angularJS function has argument value, but on MVC controller side that value is null, and I have no clue why is that so...)
//function in angularJS controller
//initialize model
var model = {};
model.Name = "some string";
save(model, onSaveSuccess, onSaveError);
...
//post to MVC controller...
function save(model, success, error) {
//name **has** value!
return $http.post('/Test/Save', { params: { model: model } })
.success(function (data) { errorHandler.jsonError(data, success, error); })
.error(function (data, status) { errorHandler.httpError(data, status, error); });
}
//MVC controller side (TestController)
[HttpPost]
public JsonResult Save(SaveModel model) //SaveModel is a class that has Name property of type string
{
//when debug reaches -> name is null...
//...
}
And in that solution, there are other cases where data are saved seemingly same way and it works there... Any idea what to check, or what might be wrong? I'm new to angularjs, maybe I am missing something really simple...
P.S.
1) If I change "params:" to "data:" as suggested result is the same
2) If I remove "params:" then I get 500 error. I.e. change:
{ params: { model: model } } -> { model: model }
PROBLEM/Solution
So, the problem turned out to be both client and server side
1) on client side I had to change { params: { model: model } } -> model as suggested by StriplingWarrior
2) on server side my model was inheriting from BaseModel which was setting some localization related stuff in it's constructor... and it was failing on my machine. The problem was that when debuging MVC controller -> this BaseClass code was performed before it, so it was hidden from me. I'm not yet common with browser Developer Tools, so I was not looking for information in the right place -> when I turned to Network tab (Chrome Developer tools), I could get exception information for my service call.
Here is a similar case answered on SO. I seem to follow same rules without success

The second argument to post is the actual data payload that you're posting, so you need to get rid of the params property and promote its object to the top level.
function save(name, success, error) {
//name **has** value!
return $http.post('/Test/Save', { name: name })
.success(function (data) { errorHandler.jsonError(data, success, error); })
.error(function (data, status) { errorHandler.httpError(data, status, error); });
}
Update
Based on your updated code, you should be passing the model directly as the body of your request:
function save(model, success, error) {
//name **has** value!
return $http.post('/Test/Save', model)
.success(function (data) { errorHandler.jsonError(data, success, error); })
.error(function (data, status) { errorHandler.httpError(data, status, error); });
}

I am almost 100% sure you need to use JSON.stringify() method on your name object, as passing it directly just like that will not put your string in apostrophes, which is needed for MVC to treat this value as string object.
Please look at what exactly is sent by browser in Developer tools' Network card to confirm my theory.

I'm not sure it's work but you can try to change this
public JsonResult Save(string name)
To
public JsonResult Save([FromBody]string name)
When the param name is not null, you can check it's value and change from the angular part accordingly

Related

Parse JSON from C# to AngularJS

I have an array of file names:
[HttpPost]
public JsonResult GetJSONFilesList()
{
string[] filesArray = Directory.GetFiles("/UploadedFiles/");
for (int i = 0; i < filesArray.Length; i++)
{
filesArray[i] = Path.GetFileName(filesArray[i]);
}
return Json(filesArray);
}
I need this in AngularJS as a list of objects so I can ng-repeat it out and apply filters. I'm unable to figure out how to get the JSON from the MVC controller to AngularJS.
I've tried the following to make it visible to the view for angular to grab, but I don't know how to make the ng-init see the function to return the list. It erros on "SerializeObject(GetJSONFilesList())" saying it doesn't exist in current context.
<div ng-controller="MyController" data-ng-init="init(#Newtonsoft.Json.JsonConvert.SerializeObject(GetJSONFilesList()),
#Newtonsoft.Json.JsonConvert.SerializeObject(Model.Done))" ng-cloak>
</div>
EDIT:
I've tried using http.get.
Test one:
alert('page load');
$scope.hello = 'hello';
$http.get('http://rest-service.guides.spring.io/greeting').
then(function (response) {
$scope.greeting = response.data;
alert($scope.greeting);
});
alert($scope.hello);
The alert in the http.get never fires, the other alerts do however.
Test two:
$http({
url: '/Home/testHello',
method: 'GET'
}).success(function (data, status, headers, config) {
$scope.hello = data;
alert('hi');
});
[HttpPost]
public string testHello()
{
return "hello world";
}
This causes the angular to break and nothing in the .js works.
Test three
alert('page load');
$scope.hello = 'hello';
$scope.GetJSONFilesList = function () {
$http.get('/Home/testHello')
.success(function (result) {
$scope.availableFiles = result;
alert('success');
})
.error(function (data) {
console.log(data);
alert('error');
});
alert('hi');
};
alert($scope.hello);
[HttpPost]
public string testHello()
{
return "hello world";
}
Alerts nothing from within it, other alerts work.
Fixed:
After some googling, I've found that using .success and .error are deprecated and that .then should be used. So by using .then this resulted in the C# being hit via debug.
Then after using console.log on the returned value found that to have anything be returned I needed to return the value from C# using "return Json(myValue, JsonRequestBehavior.AllowGet); "
And by viewing the object in the console in Chrome by using console.log, I could see my values were in the data part of the returned object.
It was stored in data as an array (as I was passing an array).
I could then get the data out of there by assigning the returned value.data to a scope and could call that in the view {{result[1]}} etc.
return Json(filesArray, JsonRequestBehavior.AllowGet);
$scope.fileList;
$http.get("/Home/GetFileList").then(function (result) {
console.log(result)
$scope.fileList = result.data;
})
Imagine that you divide your front end in three layers (MVC or MVVM) whatever you want.
When you need info from server, the best practice is to separate the logic that makes the request and the logic that manipulates the data.
More info about how to make the request you can find it reading about REST APIS in Consuming a RESTful Web Service with AngularJS.
Normally one of the layers requires the use of services and you can have your controllers and your services (the place where you get the raw data from the server and you make the request. For that you need to use the $http service from angularjs.
$http: The $http service is a core AngularJS service that facilitates communication with the remote HTTP servers via the browser's XMLHttpRequest object or via JSONP.
So basically it shows you how to make get, post and put requests. One example from the documentation is :
// Simple GET request example:
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Pay attention to the url because there is the place where you let your request knwow which method is going to be hit on the server to take the action. If your request is succesful, then you can use the parameter called response. From there, you can do whatever you want. If you decide to make that request part from your controller, you can assign it directly to a variable on your scope. Pay attention if you need to serialize the data. Something like
$scope.myResponseName = response.name ;
The first documentation link from above shows this example which does exactly what I tell you.
angular.module('demo', [])
.controller('Hello', function($scope, $http) {
$http.get('http://rest-service.guides.spring.io/greeting').
then(function(response) {
$scope.greeting = response.data;
});
});
After all the mentioned above, pay attention to what you want to display. Are you going to display the elements of an object array? The use on your HTML the ng-repeat directive. Are you going to display just a variable (No array nor object) then you use need to use an angular expression {{ }}
In summary:
By making an HTTP request, hit the correct method on server.
Make sure you are sending the JSON correctly and that the data is correct.
Retrieve the data on your response.
Assign the data to a variable on your scope and serialize the data if needed.
Display the data correctly depending if it is within an array, if it´s an object or if its just a variable.
I hope the explanation makes sense and check the documentation if you need more info.
You can build your viewmodel so that it contains the data you'd like to serialize and then pass it to angularJS in your view as follows:
<div ng-controller="MyController" data-ng-init="init(#JsonConvert.SerializeObject(myArrayData),
#Newtonsoft.Json.JsonConvert.SerializeObject(Model.Done))" ng-cloak>
and then in your angular controller have a function to receive the data as follows:
$scope.init = function (myArrayData) {
//do something with data
};
The above assumes you're trying to pass data from mvc to angularjs on page load. If you're trying to hit a controller and get data back to angularjs upon some event such as a button click, then you can write an angularjs function similar to the following (this will be an asynchronous request):
app.controller('MyController', function ($scope, $http, $window) {
$scope.ButtonClick = function () {
var post = $http({
method: "POST",
url: "/SomeController/SomeAjaxMethod",
dataType: 'json',
data: { path: $scope.Path},
headers: { "Content-Type": "application/json" }
});
post.success(function (data, status) {
//do something with your data
});
post.error(function (data, status) {
$window.alert(data.Message);
});
}
}
and your controller action would look something like:
[HttpPost]
public JsonResult SomeAjaxMethod(string path)
{
string[] filesArray = Directory.GetFiles(path);
for (int i = 0; i < filesArray.Length; i++)
{
filesArray[i] = Path.GetFileName(filesArray[i]);
}
return Json(filesArray);
}
other answers say to use .success in the angular function, .success and .error are deprecated, instead .then should be used.
Working result:
MVC:
public JsonResult GetFileList()
{
//form array here
return Json(myArray, JsonRequestBehavior.AllowGet);
}
The function needs to be of type JsonResult, and the returned value of Json using JsonRequestBehavior.AllowGet.
AngularJS:
$scope.fileList;
$http.get("/Home/GetFileList").then(function (result) {
console.log(result)
$scope.fileList = result.data;
})
This is in my AJS controller, using .then instead of .success. If you use console.log the result returned from the mvc controller and view it in the browser inspect you'll see the object with lots of other info and the values you want are in the .data section of the object.
So to access the values you need to do result.data. In my case this gives me and array. I assign this to a scope. Then in my view I can access the values by doing {{fileList[1]}} etc. This can also be used in ng-repeat e.g:
<div ng-repeat="file in fileList">
{{fileList[$index]}}
</div>
Each value in the array in the repeat can be accessed using $index which is the number of the repeat starting at 0.

AngularJS WebAPI $http.get Object values are Null

I am relatively new to WebAPI/AngularJS and am having a problem passing an object using $http.get() to a WebAPI controller from an AngularJS controller.
AngularJS Controller Code:
$http.get('api/Employee/GetVacancyStatus?emp='+thisData[0])
.then(function (response, success) {
if (response.data == false){
alert("cube/office occupied");
}
else // continue with employee move
{
do some stuff...
}
}, function (response, error) {
alert(response);
});
Web Api Controller Code:
[System.Web.Http.HttpGet, System.Web.Http.ActionName("GetVacancyStatus")]
public bool GetVacancyStatus([FromUri] employee emp)
{
//return repository.GetVacStatus(moveQuery);
return false;
}
When I debug the code, the object appears to be populated in the AngularJS Controller (at least from looking at my Chrome developer tools):
On the other hand, when execution hits the WebAPI controller, it seems that the object is uninitialized and I can't determine what I am doing wrong.
I do have an $http.put() that seems to be working passing an object like this:
$http.put('api/Employee/PutEmployee', emp)
Thanks in advance for any assistance.
Each property of the employee object you want to pass in has to be a separate query parameter. You have to do the following in your javascript:
$http.get('api/Employee/GetVacancyStatus?Id='+thisData[0].Id + '&fName=' + thisData[0].fName)
You don't have to change your controller with this method.

How to return errors when Ajax passes parameter to controller and model is invalid due to data annotations?

I have an ajax post that calls a controller action and passes a parameter that was entered by the user (me).
I'm using data annotations to validate the model in the controller action. If I enter an invalid value (or no value) and run the ajax:
The controller action is called
The model is found to be invalid (as expected)
The variable "result" is defined as expected and contains the model, errors, etc.
Problem: The return is seemingly ignored and 500 (Internal Server Error) is the response.
At this point I would be okay setting the status code to 200 to handle the errors in the success call back, but I have not been able to override it with this (in controller action):
HttpContext.Response.TrySkipIisCustomErrors = true;
HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.OK;
and this (in web.config):
<httpErrors errorMode="Custom" existingResponse="PassThrough"/>
I would also be fine returning an object to the error callback provided I could retrieve the model's errors from it, but that doesn't seem possible from what I have read.
I have searched through so many posts and blogs that seem applicable only to find the same handful of solutions that I'm not quite able to apply (either technically or conceptually).
Question: How can I keep data annotations on the parameter that I pass to the controller and enforce the return of some kind of object containing the model errors?
Ajax:
$('#apply-theme-color-btn').click(function (e) {
e.preventDefault();
var themeColor = $('#ThemeColor').val();
$.ajax({
url: '/Manage/ChangeThemeColor/' + themeColor,
type: "POST",
//dataType: "text",
data: {"themeColor": themeColor}, //.serialize(),
beforeSend: function () {
$('#apply-theme-color-btn').toggleClass('active');
},
success: function (data, textStatus, jqXHR) {
if (data.IsValid) {
console.log('Success');
}
else {
console.log(data);
}
},
error: function (jqXHR, textStatus, errorThrown) {
console.log('Error');
},
complete: function (jqXHR, textStatus) {
$('#apply-theme-color-btn').toggleClass('active');
}
});
});
Controller:
[HttpPost]
[Route("Manage/ChangeThemeColor/{themeColor:string}")]
public ActionResult ChangeThemeColor(string themeColor, IndexViewModel model) {
if (!ModelState.IsValid) {
var result = Json(new { IsValid = false, model = model, errors = ModelState.Values.Where(i => i.Errors.Count > 0) });
return result;
}
else {
//Do some different stuff
}
}
Model:
...
[Required]
[StringLength(7, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 7)]
public string ThemeColor { get; set; }
500 (Internal Server Error) means that your throwing an exception in the controller method, in your case a circular reference exception as noted in the comments.
To return a collection of errors for invalid properties, use
errors = ModelState.Keys.Where(k => ModelState[k].Errors.Count > 0)
.Select(k => new { propertyName = k, errorMessage = ModelState[k].Errors[0].ErrorMessage })
which will return the property name and the associated error message of all invalid property values.
Side notes:
url: '/Manage/ChangeThemeColor/' + themeColor, should be just
url: '/Manage/ChangeThemeColor', since you already passing
themeColor in the data: option (or better still
#Url.Action("ChangeThemeColor", "Manage")',)
Since IndexViewModel contains property ThemeColor, there is not
need for the parameter in your method and it should be just public
ActionResult ChangeThemeColor(IndexViewModel model)
There is no need to send back the same model back to the client that
the client just sent to the controller (remove model = model" from
the return Json() statement.

Viewmodel binding for Edit View

Having some trouble with the "GET" part of the Edit View, and can't really find anything online. So far, this is my POST section:
[HttpPost]
public ActionResult Edit(ContactsEditViewModel viewModel)
{
if (ModelState.IsValid)
{
var Contacts = TheContactContext.tblContacts.Find(viewModel.ID);
Contacts.Company = viewModel.Company;
Contacts.Contact = viewModel.Contact;
Contacts.Contact2 = viewModel.Contact2;
Contacts.Email1 = viewModel.Email1;
Contacts.Email2 = viewModel.Email2;
Contacts.IsSupplier = viewModel.IsSupplier;
Contacts.Telephone = viewModel.Telephone;
Contacts.Link = viewModel.Website;
Contacts.Notes = viewModel.Notes;
TheContactContext.SaveChanges();
return RedirectToAction("~/Contacts/Index");
}
return View(viewModel);
}
I've only ever done this using EntityFramework and letting it scaffold everything, so it's the first time using Viewmodels to do it personally.
Any help in whether my POST action is correct, and some guidance on the GET action would be appreciated :)
I believe you're on the right track with POST. GET is much more simplier:
public ActionResult Create()
{
return View(new ContactsCreateViewModel() { ... your initial settings, may be contained within constructor of view model directly ... });
}
The GET request requests server to provide empty form, which is filled by user, and filled data are sent back via POST, and processed within your provided function.
EDIT
If you are talking about Edit, then it is similar with one more step:
public ActionResult Edit(int id)
{
var data_model = TheContactContext.tblContacts.Get(id); // get model probably from database
var view_model = new ContactsCreateViewModel() {
Company = data_model.Company,
...
}; // copy all data into view model
return View(view_model); // and display view
}
When your page loads for a first time it sends GET request and retrieves model with collection of items from Db. After you've updated some of these items your app sends post request (most likely using Ajax) containing json data. You update your database in controller method and now it's time to refresh your page data. Simplest way is to use ajax.
$.ajax({
url: "http://" + location.host + "/CTRL/Action",
type: "POST",
data: yourdata,
}).done(function (html) {
location.reload(); (or other method to update UI)
}).fail(function (err) {
alert(err.statusText);
});
It's for client side. Server side is smth like :
lock (obj)
{
try
{
update database here...
}
catch(Exception ex)
{
return new HttpStatusCodeResult(System.Net.HttpStatusCode.ServiceUnavailable, ex.Message);
}
return new HttpStatusCodeResult(System.Net.HttpStatusCode.OK, "Update completed");
}

How to make Angular POST to C# Asp.Net MVC Controller?

I have looked around, but have not found anything (Angular post) that can actually make a successful call to a MVC Controller. I know there are a lot of Angular/.Net devs out there. Can I get some help?
Let's keep answers bare bones simple!!!
If I set a linebreak on the controller, I can see that this code is not actually hitting the controller.
HTML
<!-- I click this button -->
<input type="button" value="click" onclick="postit()" />
Javascript/Angular Post
function postit() {
$http({
method: 'POST',
url: 'Home/Give/',
data: { id: 4 }
}).success(successFn).error(errorFn);
}
function successFn() {
alert("success");
}
MVC C# Controller
[AcceptVerbs("OPTIONS")]
public ActionResult Give(int id)
{
var response = "some response" + id.ToString();
return Json(new JavaScriptSerializer().Serialize(response));
}
king Puppy, I've seen a few responses that dictate that the controller parameters should be an object that matches the object that is being sent, however, it seems that it's a little more forgiving than that. Consider the following example (I've updated your function a little):
Javascript:
$scope.postIt = function() {
var data = {
id = 4
};
$http
.post('Home/Give', data)
.success(function(data, status, headers, config) {
successFn();
})
.errors(function(data, status, headers, config) {
errorFn();
});
};
function successFn() {
alert("success");
};
function errorFn() {
alert("error");
};
MVC:
public ActionResult Give(int id)
{
var response = "some response" + id.ToString();
return Json(new JavaScriptSerializer().Serialize(response));
}
If you set a breakpoint, you will see that the id passed in is 4.
If you needed to pass in an object (so more than just one id), you could either create a matching class or struct on the controller side, or have multiple parameters (provided that they are simple value types)
ie:
public JsonResult Give (int id, int reasonId)
{
...
}
Anyway, I realize the post is old, but perhaps it will help you or others.
#kingPuppy this is my way to how to make angularjs post to mvc controller
first, html button for passing the angular js button click function;
<button class="btn btn-info" id="runButton" ng-click="runService('Hi')">Run</button>
so runService angular click (ng-click) function;
// Operation Type is my mvc controller's param
$scope.runService = function (optionType) {
$http({
url: '/System/RunService',
method: "POST",
data: {operationType : optionType}
}).then(function onSuccess(response) {
// Handle success
console.log(response);
}).catch(function onError(response) {
// Handle error
console.log(response);
});
}
And finally this is system controller's action;
NOT : Dont forget to [HttpPost] attribute
[HttpPost]
public ActionResult RunService(string operationType)
{
// Codes
Response.StatusCode = 200;
return Json(JsonRequestBehavior.AllowGet);
}
Hope this could help to you for how to make angular post to mvc controller. Thanks.
There is nothing special you have to do to get Angular to post to a standard MVC controller, and in fact I have several Angular/MVC apps that are using code almost identical to what you have above to POST to controllers that work fine.
I would use Firebug to confirm that your app is posting to the right place. One thing I noticed is that you might not want that trailing / at the end of your URL (so Home/Give instead of Home/Give/)
Good luck!

Categories