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(),
})
Related
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.
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
I have a javascript object and would like to use it in my C# function but I am unsure how I can use this object.
Javascript:
$.ajax({
data: data.model,
cache: false,
type: 'post',
url: 'Respondents/DownloadCSV',
});
Notice the data.model javascript object looks like so (taken from console.log):
[Object, Object, Object, Object, Object]
0: Object
1: Object
2: Object
3: Object
4: Object
Inside the 0:Object
Class: "respondent clickable gradeA"
Data: Object
Age: ""
City: ""
Email: null
Ethnicity: ""
Gender: ""
Id: 260619
LastActionDate: "<span class="dn">20131008175555</span>10/8 5:55 PM"
LastRecruiter: "Ben Miles"
Name: "Jerry Keys"
OwningRecruiter: "Ben Miles"
RecruitingGroup: "Competitive Users"
Source: "<span style="display:none;" >Database</span><i class="tipN icon-tasks" original-title="Database"></i>"
State: ""
Status: "<span style="display:none;" >Completed</span><i class="tipN icon-check" original-title="Completed"></i>"
class: "respondent clickable gradeA"
created: 1386790341009
url: "/Projects/644/Respondents/260619/Overview"
I am not sure what my C# method would have to look like? I would expect something like?
public ActionResult DownloadCSV(object model)
{
}
All I want to do is use the data from JavaScript in a controller method.
ASP.NET MVC provides automatic model binding from JSON notation to your Actions as long as your model matches the JSON in its format.
So, as an example, if you are posting some information about a person, your JSON would look like this...
{ "FirstName": "John", "LastName": "Doe" }
This can, of course, get considerably more complicated, but, I'm just doing this for an example.
Now, in your Action, you want to create an POCO that matches what you expect from your JavaScript so it can bind correctly. This object would look like this:
public class Person {
public string FirstName { get; set; }
public string LastName { get; set; }
}
Then, in your action, you do...
public class MyController {
[HttpPost]
public ActionResult MyAction(Person model) {
// Do what you need to here.
}
}
As long as your JSON aligns with the model, the binding will happen automatically.
Note, that if you need to convert your JavaScript object to JSON, then you can use the JSON.stringify(your_object) to do so and pass that to the data parameter of the ajax call.
Try this:
$.ajax({
data: JSON.stringify(data.model),
cache: false,
type: 'POST',
url: 'Respondents/DownloadCSV',
dataType: 'json',
contentType: 'application/json; charset=utf-8'
});
This should work with any object of any arbitrary depth.
You could create a class with properties that your json document has:
public class MyModel
{
//properties
}
Then, change your Action to take a list of these:
public ActionResult DownloadCSV(IEnumerable<MyModel> model)
{
}
The serializer will take care of mapping, but there are a few concerns:
LastActionDate: "2013100817555510/8 5:55 PM"
You'd want to pass in the actual date, not HTML representation, unless you want all your classes properties to be string
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
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)