Array to mvc controller with ajax - c#

I'm trying to get some data (multidimensional array) to my GET controller (for display in a modal/dialog box) from a list (the user checks some values and then gets sent to a modal/dialog box that should display the chosen values):
$('input:checkbox').each(function () {
if ($(this).is(':checked')) {
var prop= [];
prop['Name'] = "TEST";
prop['Id'] = "123"
data.push(prop);
}
});
When I log this (above) data, it looks fine. Then I use it the ajax call:
$.ajax({
type: "GET",
url: url,
data: JSON.stringify({ data }),
contentType: "application/json; charset=utf-8",
success: function () {
alert("OK");
}
});
I've a Model for using the data in the action (and the partial view):
public class MyClass
{
public string Name { get; set; }
public string Id { get; set; }
}
This is my action:
public ActionResult Merge(MyClass[] theData)
{
...
}
But in the action the 'theData' is always null. If I use 'POST' in the ajax, the POST action gets called, and I don't want to do that in this step. I want to use the POST action after, when the user have made some modifications (eg. Title change) and then saves. THEN I do a POST and saves the new data.

Please try this
$('input:checkbox').each(function () {
if ($(this).is(':checked')) {
var prop= {};
prop.Name = "TEST";
prop.Id = "123"
data.push(prop);
}
});

The parameter in your Merge() method is MyClass[] theData which means you need to send an array of objects, not an array of arrays. Start by changing the script to generate the data to
var data = [];
$('input:checkbox').each(function () {
if ($(this).is(':checked')) {
data.push({ Name: 'TEST', Id: '123' });
}
});
Next, you need to change the method to a [HttpPost] (rename it if necessary to avoid any conflict with any existing [HttpPost]public ActionResult Merge( .. ) method).
Then you need to change the type to "Post" and stringify the data with the name of the methods parameter before sending it
$.ajax({
type: 'Post', // change this
url: url,
data: JSON.stringify({ theData: data }), // change this
contentType: 'application/json; charset=utf-8',
success: function () {
alert("OK");
}
});
Side note: If you did want to do this to a [HttpGet] method, then it would be be necessary to send the data as .../Merge?[0].Name=TEST&[0].Id=123&[1].Name=TEST&[1].Id=123 etc (i.e. with indexers), however you should not generally be sending collections of objects to a GET method. Apart from the ugly query string, you could exceed the query string limit and throw an exception.

You're sending your data as a string. Don't use JSON.stringify
Change
data: JSON.stringify({ data })
to
data: data

Related

BindingProperty is null in handler method called by ajax

In a Razor page I want to change the data of an entity. The data is loaded in OnGet and saved in OnPost. The data that is loaded in OnGet is saved to a property called Person. At a later time I can simply retrieve them in OnPost (it's the identical object).
However, if I use a handler called by an Ajax call, the property object is initialized (integer properties are 0, object properties are zero) but it is not the original object anymore.
What do I have to do so that the original object is also available in the handler called by the Ajax call?
I already tried to use [BindProperty] attribute and to use a hidden input in the razor page. Or to access ViewData.Model But is does not work. Person and other data of the model is still kind of null.
Ajax-Call:
function addEntitlement() {
var vacationEntitlement = {};
vacationEntitlement["Year"] = $('#newEntitlementYear').val();
vacationEntitlement["Days"] = $('#newEntitlementDays').val();
vacationEntitlement["PersonID"] = $('#hiddenPersonID').val();
$.ajax({
type: "POST",
url: "./Edit?handler=AddEntitlement",
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
},
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify(vacationEntitlement)
}).fail(function () {
alert('error');
}).done(function () {
});
}
PageModel:
public IActionResult OnGet(int? id)
{
if (id == null)
{
return NotFound();
}
Person = _unitOfWork.PersonRepository.GetByID(id);
if (Person == null)
{
return NotFound();
}
return Page();
}
public JsonResult OnPostAddEntitlement([FromBody] VacationEntitlement vacationEntitlement)
{
///Tried to acces Person or ViewData.Model.Person here.
///But Person is just intialized but does not contain the expected data.
}
Try using TempData it allows you to pass data from one action to another action
Person = _unitOfWork.PersonRepository.GetByID(id);
TempData["Person"] = Person
then
public JsonResult OnPostAddEntitlement([FromBody] VacationEntitlement vacationEntitlement)
{
if(TempData.ContainsKey("Person")) {
var person = TempData["Person"] as Person; /* (as Person} I just assume the class name is Person */
// place your logic here
}
}
and also make sure to properly setup your TempData configuration
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-2.2#tempdata

Serializing form data into a model for an Ajax POST

I have an MVC web application, with this model
public class PersonViewModel
{
public Guid SchoolId { get; set; }
public string Name { get; set; }
}
public class StudentViewModel : PersonViewModel
{
}
I have this controller method to take a StudentViewModel and create a Student in my database:
[HttpPost]
public async Task<IActionResult> CreateStudent(StudentViewModel viewModel)
{
// ... do stuff
}
I'm doing a lot of dynamic UI stuff with my form, and I might be posting to different endpoints with different values, so I decided to just submit the form using javascript and decide where I'm posting to based on some conditionals.
So that's basically the reason I'm not going the normal route with the strongly typed helper methods - this is what I have in my view:
<form id="form">
<input name="SchoolId" value="#Model.Id" type="hidden" />
<input name="Name" type="text" />
<button type="submit">Create</button>
</form>
<script>
$(document).ready(function () {
$('#form').on('submit', function (e) {
e.preventDefault();
var formData = $('#form').serialize()
console.log(formData);
$.ajax({
url: '/CreateStudent',
type: "POST",
data: formData,
contentType: "application/json"
});
});
});
</script>
I can see in my console log that the form data is serialized correctly, and it hits my controller method. But the view model parameter doesn't have the values that I passed to it.
I've tried setting the form data this way:
var formData = JSON.stringify($('#form').serializeArray());
I even tried just hardcoding the values:
var formData = '{"SchoolId":"c65fc8ad-7ad2-e811-b37f-9cb6d0b709c2","Name":"Testing"}';
But no matter what I try, the view model values don't get set.
Am I formatting the form data wrong? Or is there a different way completely that I need to do this?
When you use .serialize(), it generates the data in a 'query string' format - i.e. SchoolId=someValue&Name=AnotherValue, which needs to be sent using the default contentType which is 'application/x-www-form-urlencoded; charset=UTF-8', not as JSON.
Either remove the contentType option or specify contentType: 'application/x-www-form-urlencoded; charset=UTF-8'
$('#form').on('submit', function (e) {
e.preventDefault();
var formData = $('#form').serialize()
$.ajax({
url: '#Url.Action("CreateStudent")', //recommended
type: "POST",
data: formData,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8' // optional
});
});
Note that if you were to use contentType: "application/json", then you would generate the data using (assumes you give the inputs the appropriate id attribute)
var formData = JSON.stringify({
SchoolId: $('#SchoolId').val(),
Name: $('#Name').val(),
})

POST data is null with ASP.NET API Controller

I am trying to migrate to an ASP.NET 6 API controller and my POST data from my JS function is showing as null.
I have the following json request:
// searchTerms is an array with 2 properties, Value and Type
$.ajax({
type: 'Post',
contentType: 'application/json',
dataType: "json",
data: JSON.stringify({
searchTerms
})
if I POST this to an ASP.NET 6 MVC controller:
public ActionResult Search(List<SearchTerm> searchTerms)
Then my searchTerms list is properly populated.
If I POST to an API Controller
[System.Web.Http.HttpPost]
[System.Web.Http.Route("api/search")]
public IHttpActionResult Search([FromBody] List<SearchTerm> searchTerms)
Then searchTerms is null.
I have tried to change the contentType, dataType, remove the stringify function to no avail.
I have tried to changed the signature to
public IHttpActionResult Search([FromBody] dynamic value)
And see the following, so obviously I'm not binding properly?
Here is my SearchTerm model:
public class SearchTerm
{
public string Value { get; set; }
public string Type { get; set; }
}
Instead of:
data: JSON.stringify({
searchTerms
})
Change your $.ajax call to use just:
data: JSON.stringify(searchTerms)
You are sending JSON that looks something like:
{
"seachTerms": [
...
]
}
With the change I suggested, this would just be a simple JSON array, which should work in your example.

Send model from view to controller onchange in mvc razor

I have the below code
<script type="text/javascript">
$(function () {
$('#cboDeptID').change(function () {
var selectedDepartmentId = $(this).val();
var depmodel = {
Employees:'#Model.Employees',
Departments:'#Model.Departments',
DepartmentId:'#Model.DepartmentId',
DepartmentName:'#Model.DepartmentName',
SelectedDepartmentId:'#Model.SelectedDepartmentId'
};
$.ajax({
url: '#Url.Action("GetEmployees","DepartmentController")',
contentType: 'application/json; charset=utf-8',
type: 'POST',
dataType: 'json',
data: JSON.stringify({ model: depmodel })
})
.success(function (result) {
alert('Success!');})
.error(function (xhr, status){ alert('The provider could not be updated.');});
});
});
</script>
on this combo change i want to pass model to the controller. The model contains above members such as employees,departments,departmentid etc. The controller action will be fired on combo change but model member values are null. Am I doing it wrong? pls help.
Controller method is as shown below
public ActionResult GetEmployees(DepartmentModel model)
{
// code goes here
return View ("DepartmentView",model);
}
You need to change your JSON.stringify line to:
data: JSON.stringify(depmodel)
The automatic model binding when you post this data to the controller is getting confused because you're posting your array of key value pairs (employees, departments etc) inside another array when you call JSON.stringify. So what it's receiving is something like:
{ model: { Employees: <value>, ... } }
But what it wants is only the key value pairs of the properties inside the model to which it is trying to bind

Similar property names in ViewModel not binding in controller action method

Recently we updated our application from MVC3 to MVC4. In MVC4 we have discovered that having property names like Studio and StudioExecutive in our ViewModel will cause problems when posting. In the controller method we will always get Studio = null when StudioExecutive is being populated.
Here is an example of our issue and hope there is an answer for this problem.
Data Classes:
public class TestContact
{
public List<TestContactItem> Studio { get; set; }
public List<TestContactItem> StudioExecutive { get; set; }
public TestContact()
{
Studio = new List<TestContactItem>();
StudioExecutive = new List<TestContactItem>();
}
}
public class TestContactItem
{
public int Id { get; set; }
public string Name { get; set; }
}
Controller Methods:
public ActionResult TestContactView()
{
var vm = new TestContact();
vm.Studio.Add(new TestContactItem(){Id=1, Name = "Studio Contact ID=1"});
vm.StudioExecutive.Add(new TestContactItem() { Id = 2, Name = "Studio Exec Contact ID=2" });
return View(vm);
}
[HttpPost]
public ActionResult SaveTestContact(TestContact model)
{
return Content("success");
}
View / JavaScript with Ajax POST:
#using System.Web.Script.Serialization
#model TestContact
<button type="button" onclick="SaveTestContact();">Click Here to Post</button>
<script type="text/javascript">
$(document).ready(function() {
globalTestModel = #Html.Raw(new JavaScriptSerializer().Serialize(Model));
});
function SaveTestContact() {
// passing additional studio & studio executive parameter to controller because it was not mapping correctly to the server side viewmodel without it
$.ajax({
type: "POST",
url: "/Test/SaveTestContact",
data: JSON.stringify(globalTestModel),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (msg) {
}
});
}
</script>
In this example we are populating our object with at least one Studio and one StudioExecutive and render the view. When the button in the view is clicked, we POST the same object to the controller method but the ViewModel is not binding correctly the Studio property is set to null.
*Unfortunately I'm not able to post images I have a screenshot of the object showing that the Studio count was 0 and the StudioExecutive count was 1
We did put a breakpoint before the POST to make sure the serialization on the JavaScript was correct and the object was populated.
We have concluded that this has to do with the naming convention of the 2 properties one being a substring of the other. Any one who has encounter the same problem and can point us on the right direction.
I know this is not the "complete answer" to the question but I feel that this will give additional insight to the issue, that (I hope) will eventually lead to the "complete solution".
Say we have a single item in each of Studio and StudioExecutive fields as shown in the example of the OP, and do the following in js:
globalTestModel = '#Html.Raw(new JavaScriptSerializer().Serialize(Model))';
$.ajax({
type: "POST",
url: "/Test/SaveTestContact",
data: JSON.stringify(globalTestModel),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (msg) {
}
});
The controller method will only receive StudioExecutive as already mentioned. Now if we do the same and build the object in js as shown below, we get the same result.
o = {
Studio: [{
Id: 1,
Name: 'Studio Contact ID=1',
}],
StudioExecutive: [{
Id: 2,
Name: 'Studio Exec Contact ID=2',
}]
};
$.ajax({
type: "POST",
url: "/Test/SaveTestContact",
data: JSON.stringify(o),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (msg) {
}
});
Now here comes the interesting part. If we add another item to the Studio field and post the same way we will get the same result.
// Do this in the controller and do the same ajax post
public ActionResult TestContactView()
{
var vm = new TestContact();
vm.Studio.Add(new TestContactItem(){Id=1, Name = "Studio Contact ID=1"});
vm.Studio.Add(new TestContactItem(){Id=3, Name = "Studio Contact ID=3"});
vm.StudioExecutive.Add(new TestContactItem() { Id = 2, Name = "Studio Exec Contact ID=2" });
return View(vm);
}
But if we will build the object in js as shown below the controller will receive both the Studio, with 2 items, and the StudioExecutive, with one item.
o = {
Studio: [{
Id: 1,
Name: 'Studio Contact ID=1',
},
{
Id: 2,
Name: 'Studio Contact ID=2',
}],
StudioExecutive: [{
Id: 2,
Name: 'Studio Exec Contact ID=2',
}]
};
// then do the ajax post
So what have we learned
This tells us two things:
There is something wrong with the default model binder in that it cannot bind two, or more, fields that starts with the same name
JSON.stringify can also NOT do it if there is only one item, but it can do it if there are more than one item in the field that has the shortest name (e.g. Studio)

Categories

Resources