I have this for that fills an array, where the cont came from View
function GenerateJSON(cont) {
var songs= [];
for (var i = 0; i <= cont; i++) {
songs[i] = {
id: i,
genre: $(".genre" + i).val(),
song: $(".song" + i).val()
}
}
$.ajax({
url: "Generate",
type: 'POST',
dataType: "json",
data: { songs: JSON.stringify(songs) },
success: function (data) {
}
})
}
On Controller, i got only one block music
example:
songs= [{"id":0,"genre":"","song":"","id":1,"genre":"","song":"","id":2,"genre":"","song":""}]
I want this:
songs=[{"id":0,"genre":"","song":""},{"id":1,"genre":"","song":""},{id":2,"genre":"","song":""}]
Controller
public JsonResult Generate(string[] songs)
{}
Using a string array as the parameter does not make sense to me. Now you have to make client side code to build some sophisticated string and in server side you have to parse it to get each item's property values.
IMHO, The clean approach is to use a view model and take advantage of MVC model binding. Start by creating a view model to represent your data.
public class SongVm
{
public int Id { set; get; }
public string Genre { set; get; }
public string Song { set; get; }
}
Now use this as the parameter of your action method. Since you want to send a collection of your objects, use List<SongVm> as your parameter.
[HttpPost]
public ActionResult Gerar(List<SongVm> songs)
{
// Simply returning the data with a message property as well
return Json(new { messge ="success", data = songs });
}
Now from the client side code, send the JSON string version of your array, while specifying application/json as the value of contentType property of the option we are passing to the $.ajax method.
var songs = [];
for (var i = 0; i <= cont; i++) {
songs[i] = {
id: i,
genre : "test genre" + i,
song : "test song" + i
};
}
$.ajax({
url: "/Home/Gerar",
type: 'POST',
contentType: "application/json",
data: JSON.stringify(songs),
success: function (data) {
console.log(data);
},
error: function (a, b, c) {
console.log(c);
}
})
Now $.ajax will add the request header Content-Type to the call with the value application/json. As part of model binding, the default model binder will read this request header value and then decide to read the data from the request body(Request payload)
I would also recommend leveraging the Url helper methods like Url.Action to generate the correct relative path to the action method.
Related
I am learning about POST with Ajax and I've noticed a lot of examples using stringify on static arrays.
In my example I am building array via jQuery objects extracted from <input> values.
Jquery
function PostEdit(vData){
var content = JSON.stringify(vData); //content = "[{"Id":"1","Date":"7/12/2017 12:00:00 AM"}]"
$.ajax({
url: '#Url.Action("Index", "PostViewData")',
type: "POST",
data: JSON.stringify({ ViewData : vData }),
contentType: "application/json",
success: function (result) {
alert("success" + result);
},
error: function (result) {
alert("failure" + result);
}
});
return;
}
Controller
[HttpPost]
public ActionResult Index(List<DataViewModel> ViewData )
{
if(ViewData != null)
{
var json = new { success = true };
return Json(json);
}
return Json(false);
}
ViewModel
public class DataViewModel
{
public DataViewModel(string id, string date)
{
Id = id;
Date = date;
}
public string Id { get; set; }
public string Date { get; set; }
}
Where DataViewModel is a class that consists of two values, Id, and Date which correspond to my JS object.
Since vData enters the function as value vData = [Object], When I call .stringify(vData) it returns content = "[{"Id":"1","Date":"7/12/2017 12:00:00 AM"}]"
The value of content above is how I see most examples structured before stringify such as the example here.
Once in this format, this is when most responses, examples call stringify anyway.
Now when I call .stringify(ViewData : vData) I dont even make it to the controller. Ajax fails to post. On the contrary when I call .stringify(ViewData : content), I make it to the controller, but with a null value. What am I missing / misunderstanding?
I'm looking for a better explanation of Ajax Post and Stringify, and how it interacts with the controller parameter.
Just add parameterless constructor to your model. With that change, your code will work as it is. ASP is unable to parse your json as DataViewModel since it's trying to call default constructor that doesn't exists.
public class DataViewModel
{
public DataViewModel()
{
}
//...other methods and members here
}
Please try the solution below.
There's another way of doing this
function PostEdit(vId, vDate){
$.ajax({
url: '/PostViewData/Index' // it should be '/controller/method'
type: "POST",
data: JSON.stringify({ Id: vId, Date: vDate}),
contentType: "application/json",
success: function (result) {
alert("success" + result);
},
error: function (result) {
alert("failure" + result);
}
});
return;
}
[HttpPost]
public ActionResult Index(string Id, string Date )
{
if(Id != null)
{
var json = new { success = true };
return Json(json);
}
return Json(false);
}
Update: In the controller method, it should be string id, string date
Update: The constructor should be parameterless. From the answer mentioned below
public DataViewModel()
{
}
I have one ViewModel that contains three Collection of ExternalProjectViewModel, CertificateUniverSityViewModel, CertificateInstitutionsViewModel.
CreateFreelancerProfileViewModel.cs
public class CreateFreelancerProfileViewModel : BaseViewModel
{
// More ...
public List<ExternalProjectViewModel> ExternalProjects { get; set; }
public List<CertificateUniverSityViewModel> CertificateUniverSitys { get; set; }
public List<CertificateInstitutionsViewModel> CertificateInstitutions { get; set; }
}
My Ajax code:
$('#Controller').on('click','#SaveProfile',
function() {
debugger;
var CertificateInstitutions =
JSON.parse(localStorage.getItem("CertificateInstitutionsListLocal"));
var CertificateUniverSitys =
JSON.parse(localStorage.getItem("CertificateUniverSitysListLocal"));
var ExternalProjects =
JSON.parse(localStorage.getItem("ExProjectListLocal"));
$.ajax({
url : '#Url.Action(MVC.Freelancer.Profile.CreatePrfile())',
method: "POST",
data: {
ExternalProjects,
CertificateUniverSitys,
CertificateInstitutions
}
});
});
When I Want Send Objects to Controller, First Get It from LocalStorage
And After Get it Send it to Controller Action:
public virtual ActionResult CreatePrfile(CreateFreelancerProfileViewModel viewModel)
When I see viewModel Values Show My Objects Count That is 2 but Objects Properties is null.so that my server object properties name equally with the client Object properties name.
LocalStorage Values
[{"ExternalProjects":{"Name":"wqeqwe","Body":"wqewqe","Url":"wqewqe"}}]
[{"CertificateUniverSity":{"Name":"sad","Description":"sadas","DateOfGets":"sad","CertificateType":"2","Field":"sadasd","UniName":"sad","CertificateUniverSityLevel":"2"}}]
You could send them as JSON payload:
$.ajax({
url : '#Url.Action(MVC.Freelancer.Profile.CreatePrfile())',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({
externalProjects: ExternalProjects,
certificateUniverSitys: CertificateUniverSitys,
certificateInstitutions: CertificateInstitutions
}),
success: function(result) {
alert('data sent successfully');
}
});
This assumes that the instances you got from the localStorage of those 3 variables represent javascript arrays with the corresponding objects in it.
I know I'm missing something in the details here.
Problem
Despite Googling, trying examples, different formats, etc, the AJAX request that I send always is validated as having all fields empty, but not being null.
I think I'm not sending things in the proper format for the controller to recognize it as an object but I'm not sure what.
Fiddler: What my request looks like
With some dummy data:
Code: Model Class
public class ContactUsMessage
{
public string Email { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public string Message { get; set; }
}
Code: WebAPI Controller
[HttpPost]
public HttpResponseMessage NewMessage(ContactUsMessage messageToSend)
{
if (messageToSend == null)
{
var sadResponse = Request.CreateResponse(HttpStatusCode.BadRequest, "Empty Request");
return sadResponse;
}
var messageValidator = new ContactUsMessageValidator();
var results = messageValidator.Validate(messageToSend);
var failures = results.Errors;
var sadString = "";
if (!results.IsValid)
{
foreach (var error in failures)
{
sadString += " Problem: " + error.ErrorMessage;
}
var sadResponse = Request.CreateResponse(HttpStatusCode.NotAcceptable, "Model is invalid." + sadString);
return sadResponse;
}
else
{
SendContactFormEmail(messageToSend.Email, messageToSend.Name, messageToSend.PhoneNumber, messageToSend.Message);
}
Code: JavaScript on Page
function sendSubmissionForm() {
var dataObject = JSON.stringify(
{
messageToSend: {
'Email': $('#inpEmail').val(),
'Name': $('#inpName').val(),
'PhoneNumber': $('#inpPhone').val(),
'Message': $('#inpMessage').val()
}
});
$.ajax({
url: '/api/contactus/newmessage',
type: 'POST',
done: submissionSucceeded,
fail: submissionFailed,
data: dataObject
});
}
When you JSON.stringifyied your data object you converted it to JSON. But you forgot to set the Content-Type request header and the Web API has no way of knowing whether you are sending JSON, XML or something else:
$.ajax({
url: '/api/contactus/newmessage',
type: 'POST',
contentType: 'application/json',
done: submissionSucceeded,
fail: submissionFailed,
data: dataObject
});
Also when building the JSON you don't need to wrap it in an additional property that matches your method argument name. The following should work as well:
var dataObject = JSON.stringify({
'Email': $('#inpEmail').val(),
'Name': $('#inpName').val(),
'PhoneNumber': $('#inpPhone').val(),
'Message': $('#inpMessage').val()
});
Here is the javascript code I use to create the array and send it on it's way:
<script type="text/javascript" language="javascript">
$(document).ready(function () {
$("#update-cart-btn").click(function() {
var items = [];
$(".item").each(function () {
var productKey = $(this).find("input[name='item.ProductId']").val();
var productQuantity = $(this).find("input[type='text']").val();
items[productKey] = productQuantity;
});
$.ajax({
type: "POST",
url: "#Url.Action("UpdateCart", "Cart")",
data: items,
success: function () {
alert("Successfully updated your cart!");
}
});
});
});
</script>
The items object is properly constructed with the values I need.
What data type must my object be on the backend of my controller?
I tried this but the variable remains null and is not bound.
[Authorize]
[HttpPost]
public ActionResult UpdateCart(object[] items) // items remains null.
{
// Some magic here.
return RedirectToAction("Index");
}
If you are going to send JSON to the server you need to JSON.stringify the data and specify the contentType as application/json to play nice with the MVC3 model binder:
$.ajax({
type: "POST",
url: "#Url.Action("UpdateCart", "Cart")",
data: JSON.stringify(items),
success: function () {
alert("Successfully updated your cart!");
},
contentType: 'application/json'
});
And as the datatype on the server you can use strongly typed classes eg:
public class Product
{
public int ProductKey { get; set; }
public int ProductQuantity { get; set; }
}
[HttpPost]
public ActionResult UpdateCart(Product[] items)
{
// Some magic here.
return RedirectToAction("Index");
}
But you need to tweak the items list a bit:
var items = [];
$(".item").each(function () {
var productKey = $(this).find("input[name='item.ProductId']").val();
var productQuantity = $(this).find("input[type='text']").val();
items.push({ "ProductKey": productKey, "ProductQuantity": productQuantity });
});
Basically the JSON object structure should match the C# model class structure (also the property name should match) and then the model binder in MVC takes care to populate your server side models with the JSON data that you've sent. You can read more about model binders here.
I am trying to submit all the items in a select list to an MVC 3 action method. Here is my Action method:
[HttpPost]
public ActionResult SaveList(int SettingID, List<SelectListItem> items)
{
// blah...
}
and here is the simplified version of how I am trying to send the data:
var items = $("#listItems > option").map(function ()
{
var arr = [];
arr.push({ Value: $(this).val(), Text: $(this).text() });
return arr;
}).get();
$.ajax({
type: "POST",
url: "/CompanyAdmin/SaveSettingList/",
dataType: 'json',
data: { items: items, SettingID: SettingID},
success: function (msg)
{
alert(msg);
}
});
The setting ID param gets populated correctly. And the List gets populated with the correct AMOUNT of items, but both the Text and Value properties come out as null.
Do I have to install one of the JSON plugins for jQuery to be able to submit an array of items?
EDIT 1:
Following Darin Dimitrov's advice I used the stringify method and sent both the SettingID and list of options as JSON.
At first this was not hitting my Action method, so I changed the method to this:
public ActionResult SaveList(SaveSettingListModel json)
{}
with the param being defined like so: (to match the JSON)
public class SaveSettingListModel
{
public int SettingID { get; set; }
public List<SelectListItem> items { get; set; }
}
This now fires the correct method. However, the contents of json on the server side are SettingID = 0 (when I verified that it is 2 on the client side), and items = null.
Try posting them as JSON.
Start by defining a view model:
public class MyViewModel
{
public int SettingID { get; set; }
public List<SelectListItem> Items { get; set; }
}
then modify your controller action to take this view model as argument:
[HttpPost]
public ActionResult SaveList(MyViewModel model)
{
// blah...
}
and finally in your script:
var items = $('#listItems > option').map(function ()
{
var arr = [];
arr.push({ value: $(this).val(), text: $(this).text() });
return arr;
}).get();
$.ajax({
type: 'POST',
url: '/CompanyAdmin/SaveSettingList/', // <-- use an URL Helper to generate this url instead of hardcoding it or you will cry when you deploy your application into a virtual directory
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({
items: items,
settingID: SettingID
}),
success: function (msg) {
alert(msg);
}
});
And if you want to use the JSON.stringify method in legacy browsers in which it is not natively supported make sure you include the json2.js script.
All this being said I see where you are going with this controller action and with this AJAX request and I must say that I wouldn't do it this way (send the options in the request). I mean in order to render this view you fetched this list from somewhere? Presumably a datastore? So instead of fighting with the HTML <form> (which sends only the selected value of a dropdownlist to the server when submitted) why don't you simply use this very same data store in your SaveList action in order to fetch those very same options? This will avoid you round-tripping them and save bandwidth. Don't worry databases are fast. And if they are not in your case simply cache the results of expensive queries.
Completely ignore my previous remark if those options are subject to change on the client side and you want to fetch the new values.
Take a look at this it will help you in this case.
http://community.codesmithtools.com/CodeSmith_Community/b/tdupont/archive/2009/08/10/mvc-custom-json-binder.aspx