POST KnockoutJS data to mvc controller not binding - javascript

I'm trying to do a post of the mapped KnockoutJS model. I can see when debugging it, the JSON is correct. But the server shows that Product is 0 (empty). While it does contain 1 item.
MVC Controller:
[HttpPost]
public ActionResult Test(MyModel model, FormCollection fc)
{
return RedirectToAction("index");
}
The AJAX submit:
$('#btnSubmit').click(function (event) {
var theModel = ko.mapping.toJSON(viewModel);
debugger;
$.ajax({
type: 'POST',
url: "#Url.Action("Test", "Home")",
data: theModel,
contentType: 'application/json; charset=utf-8',
success: function (result) {
if (!result.success) {
//alert(result.error);
}
else { }
}
});
});
This is a partial JSON object:
"Products":[{"Id":2,"Name":"bread"}]
What am I doing wrong?
EDIT:
public class MyModel
{
public int User { get; set; }
public string Address { get; set; }
public string ZipCode { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}

Here is a full working tested example (sending a model back from the controller and posting):
Controller
public ActionResult Test()
{
var model = new MyModel();
model.Products = new List<Product> { new Product { Id = 2, Name = "bread" } };
return View(model);
}
[HttpPost]
public ActionResult Test(MyModel model, FormCollection fc)
{
// Count equals one
var count = model.Products.Count();
return RedirectToAction("index");
}
Model
public class MyModel
{
public int User { get; set; }
public string Address { get; set; }
public string ZipCode { get; set; }
public List<Product> Products { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
View
#model MyModel
<form method="post">
<input id="btnSubmit" type="submit" value="submit" />
</form>
<script src="~/Scripts/jquery-1.8.2.js"></script>
<script src="~/Scripts/knockout-2.2.0.js"></script>
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<script type="text/javascript">
var Product = function (Id, Name) {
self = this;
self.Id = Id;
self.Name = Name;
}
var mapping = {
'Products': {
create: function (options) {
return new Product(options.data.Id, options.data.Name);
}
}
}
function MyModel(data) {
var self = this;
ko.mapping.fromJS(data, mapping, self);
}
var viewModel = new MyModel(#Html.Raw(Json.Encode(Model)));
$('#btnSubmit').click(function (event) {
event.preventDefault();
var theModel = ko.mapping.toJSON(viewModel);
debugger;
$.ajax({
type: 'POST',
url: "#Url.Action("Test", "Home")",
data: theModel,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (result) {
if (!result.success) {
//alert(result.error);
}
else { }
}
});
});
</script>

After investigating some more with fiddler, it turned out that I was getting a 500 error with this message:
System.MissingMethodException: No parameterless constructor defined for this object.
After adding the parameterless constructor in the model, I still got the error message. This was caused because I had a few SelectList in my model. So that's why It couldn't be bound to the model.
Found the solution on this SO post (look for the answer of Chris S). Hope it helps others when facing this issue too.

Related

Having trouble passing complex model to controller through ajax call

I'm trying to update my model whenever a button is clicked. When I log the data I'm sending in the razor file to the console, the data is all there. However, when the controller method is called, the model is empty.
My onclick method:
function addCoupon() {
var code = document.getElementById("coupon-entry").value;
$.ajax({ // Validate coupon first. This is working.
method: "post",
url: `/cart/validate-coupon/` + code,
contentType: "application/json; charset=utf-8"
}).done(result => {
if (result.success) {
var model = #Html.Raw(Json.Serialize(Model));
console.log(model); // Countries and CheckoutData are correct here
$.ajax({
method: "post",
url: `/cart/add-coupon/` + code,
contentType: "application/json; charset=utf-8",
data: model
}).done(result => {
console.log("It worked");
});
}
});
}
My model:
public bool IsPos { get; set; }
public List<CountryDto> Countries { get; set; }
public CheckoutData CheckoutData { get; set; }
public string Payment ProcessorError { get; set; }
public bool DisplayRequiredErrors { get; set; }
public List<string> ValidationErrors { get; set; } = new List<string>();
public PaymentInformationModel PaymentInformation { get; set; } = new PaymentInformationModel();
public bool UseSavedCard { get; set; }
My controller:
[HttpPost("add-coupon/{code}")]
public async Task<IActionResult> AddCouponAsync(string code, CheckoutModel model)
{
// Countries and CheckoutData are null here
}
There were several similar questions posted on here, but they all either had simple models, or had solutions that didn't work for me when I tried them.
Thanks!
You should try changing it to this (otherwise it isn't valid javascript):
(Notice that it needs to be encapsulated in '')
var model = '#Html.Raw(Json.Serialize(Model))';

How to pass the fields (which are dynamically created) from the view to the controller?

I am new to MongoDB. My application is dynamic form builder that lets users add dynamic fields on the form. None of the field on the form is fix or static. The user can add any number and any type of fields such as Textbox, Textarea, Dropdown, Checkbox, Radiobutton etc fields on the form and save the form.
Now want to store the created fields into Database. then saved forms will be used to collect data from the users.
How to pass the Fields from the View to the controller? and to describe a Model and Controller for this (to handle fields)?
I’m using ASP.NET MVC as a frontend MongoDB as a backend
I'm pushing all the fields here:
$('.field').each(function() {
var $this = $(this);
//field type
var fieldType = $this.data('type');
//field label
var fieldLabel = $this.find('.field-label').val();
//field required
var fieldReq = $this.hasClass('required') ? 1 : 0;
//check if this field has choices
if($this.find('.choices li').length >= 1) {
var choices = [];
$this.find('.choices li').each(function() {
var $thisChoice = $(this);
//choice label
var choiceLabel = $thisChoice.find('.choice-label').val();
//choice selected
var choiceSel = $thisChoice.hasClass('selected') ? 1 : 0;
choices.push({
label: choiceLabel,
sel: choiceSel
});
});
}
fields.push({
type: fieldType,
label: fieldLabel,
req: fieldReq,
choices: choices
});
});
Saving Form with Dynamic fields:
var formArray=[];
formArray.push({ name: "formID", value: "formID" }, { name: "formFields", value: "fields" });
var formObject = JSON.stringify(formArray);
$.ajax({
method: "POST",
url:'#Url.Action("Index", "Home")',
data: formObject,
dataType: 'JSON',
success: function (data)
{
console.log(data);
$('.alert').removeClass('hide');
$("html, body").animate({ scrollTop: 0 }, "fast");
//Demo only
$('.alert textarea').val(JSON.stringify(fields));
}
});
Can anyone suggest a Model and Controller for this?
Update
As #stom suggested, I did the following corrections:
Model
namespace Simple_jQuery_Form_Builder.Models
{
public class ControlsAttribute
{
public string id { get; set; }
public string value { get; set; }
public string name { get; set; }
}
public class FormControl:ControlsAttribute
{
public object textBox { get; set; }
public object textArea { get; set; }
public object checkBox { get; set; }
public object radioButton { get; set; }
public object agreeBox { get; set; }
public object selectBox { get; set; }
public object datePicker { get; set; }
}
public class Simple_jQuery_Form_Builder_Model
{
public List<FormControl> controls { get; set; }
public List<FormControl> formEditor { get;set; }
}
}
View
<form method="post" action="~/Controllers/Home/Index">
<div id="sjfb" novalidate>
<div id="form-fields">
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
#Html.DisplayFor(model => model.controls);
#Html.DisplayFor(model => model.formEditor);
}
</div>
<button type="submit" class="submit">Save Form</button>
</div>
</form>
Controller
[HttpPost]
public ActionResult Index(Simple_jQuery_Form_Builder_Model model)
{
if (model != null)
return Json("success");
else
return Json("An error has occured");
}
It's supposed to show the saved form in the desired link/div. It shows "success" message. Now I've to show the created form in another view (like the preview)
First create your View Models.
public class FormViewModel
{
public string name { get; set;}
public string value { get; set;}
}
Action methods
This is to load your form in GET request.
public ActionResult Index()
{
return View();
}
This is to post your data in POST request.
[HttpPost]
public ActionResult SaveForm(IEnumerable<FormViewModel> model)
{
if (model != null)
{
return Json("Success");
}
else
{
return Json("An Error Has occoured");
}
}
Index View
Add this in your Index.cshtml View
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script>
$(function () {
$('#saveInMongo').click(function (e) {
var formArray=[];
formArray.push({ name: "formID", value: "formID" }, { name: "formFields", value: "fields" });
var formObject = JSON.stringify(formArray);
$.ajax({
url: "#Url.Action("Home","SaveForm")",
type: "POST",
data: formObject,
contentType: "application/json; charset=utf-8",
dataType: "json",
error: function (response) {
alert(response.responseText);
},
success: function (response) {
alert(response);
}
});
});
});
</script>
<input type="button" value="Save In Mongo" id="saveInMongo" />
This should work.
Check this for more.

Form Data not serialized in asp.net core mvc controller model

C# model class
public class SubCategoryTwoViewModel
{
public long Id { get; set; }
public string SubCatTwoName { get; set; }
public CategoryViewModel Category { get; set; }
public SubCategoryOneViewModel SubCatOne { get; set; }
public string PictureUrl { get; set; }
public List<IFormFile> File { get; set; }
public UserViewModel AddedBy { get; set; }
public DateTime AddedOn { get; set; }
public UserViewModel Updated_By { get; set; }
public DateTime? UpdatedOn { get; set; }
public bool Active { get; set; }
}
Here is the CategoryViewModel
public class CategoryViewModel
{
public long CategoryId { get; set; }
}
Here is the controller method
[HttpPost]
public JsonResult AddEditSubCategoryTwo(SubCategoryTwoViewModel model)
{
}
Here is the ajax call which use the Form Data to serialized the data send to the controller method.
var ajaxUrl = ApplicationRootUrl("AddEditSubCategoryTwo", "Category");
var formData = new FormData();
var totalFiles = document.getElementById("subCatFile").files.length;
for (var i = 0; i < totalFiles; i++) {
var file = document.getElementById("subCatFile").files[i];
formData.append("File", file);
}
formData.append('SubCatTwoName', self.subCatTwoName());
var category = {
CategoryId: self.selectCategory()
};
formData.append('Category', category);
var subCatOne= {
SubCategoryOneId: self.selectCategorytwo()
};
formData.append('SubCatOne', subCatOne);
formData.append('Active', self.active());
$.ajax({
type: "POST",
contentType: false,
processData: false,
url: ajaxUrl,
data: formData,
dataType: "json",
success: function (data) {
}
});
I,am getting some field data in the controller but the java script value is not serialized to the controller model.
Here is the screenshot of the sample
In the screen shot I am getting the Category & SubCatOne field as null. But active, SubCatTwoName and File field has receive the value. I want to get Category & SubCatOne value in the controller model object. How can I achieved this.
Trying to add the object directly will not work. Instead you can make use of model binding by adding the nested fields with the correct name (separate the property hierarchy with dots). e.g.
formData.append('Category.CategoryId', self.selectCategory());

Posting JSON with a array object to MVC controller

My ViewModel is:
public class CFUViewModel
{
public int RID { get; set; }
public int Order { get; set; }
public List<VCItem> VItems{ get; set; }
}
where VCItem is:
public class VCItem
{
public string Name{ get; set; }
public string Category { get; set; }
}
In my view, I am collecting the data from form elements. Thus,
$('#submit').click(function () {
console.log(gatherData());
transmitData(gatherData());
});
function gatherData() {
var vc = dataObject.getVCItems();
//This is a knockout function that gives an array of VCItems
var data = new Object();
data.RID = $('#rid').val();
data.VItems = vc;
return data;
};
function transmitData(dataToSend) {
$.ajax({
url: '../CFUDashboard/ReceiveData',
type: 'post',
dataType : 'json',
success: function (data) {
alert('success');
},
data: dataToSend
});
};
In the controller, I have:
[HttpPost]
public ActionResult ReceiveData(CFUViewModel viewModel)
{
//...
}
The problem is, though RID is ok, VItems list is not serialized. I suspect, the reason is, the array attached to the JS object is actually string (array wrapped in quotes). This is confirmed by the console.log(gatherData())
It is outputted as:
VItems : "[{"Name":"OPV3","Category":"FUGY"},{"Name":"OPV7","Category":"FUGX"}]"
What is the reason for not getting the list serialized? What am I missing?

Sending LIst<t> via ajax to complex model

I know I've done this before but I can't seem to get this to work.
I have the following JavaScript;
$("#btnTestVouchers").click(function () {
var postData = {
"workplaceGiverId": $(".wpgDropdownList").val(),
"fromMemberId": $(".wpgFromMemberDropdownList").val(),
"toMemberId": $(".wpgToMemberDropdownList").val(),
"voucherExpiryDate": $("#expiryDatePicker").val(),
"recipients": JSON.stringify("[{'firstname':'a','lastname':'b','email':'c','voucheramount':'d'}]")
};
console.log(postData);
$.ajax({
type: "POST",
url: "/Admin/TestVoucherCreationEmails",
contentType: 'application/json; charset=utf-8',
dataType: "json",
data: JSON.stringify(postData),
success: function (d) {
alert("OK");
},
error: function (xhr, textStatus, errorThrown) {
alert("Error:" + errorThrown);
}
});
});
In my model I have;
public class postDataObject
{
public int workplaceGiverId { get; set; }
public int fromMemberId { get; set; }
public int toMemberId { get; set; }
public string voucherExpiryDate { get; set; }
public IEnumerable<BulkVoucherRecipient> recipients { get; set; }
}
public class BulkVoucherRecipient
{
public string firstname { get; set; }
public string lastname { get; set; }
public string email { get; set; }
public string voucheramount { get; set; }
}
In my controller I have;
[HttpPost]
public void TestVoucherCreationEmails(postDataObject postedData)
{
string g = "";
}
However when I post, the list of recipients is always empty.
If I don't Stringify the list of recipients I get the same result.
Anyone know what I'm doing wrong?
edit
The other values all come through ok, just the List is empty.
You don't need to JSON.stringify the recipients.
"recipients": JSON.stringify("[{'firstname':'a','lastname':'b','email':'c','voucheramount':'d'}]")
Remove JSON.stringify form here and it should work.
var postData = {
"workplaceGiverId": $(".wpgDropdownList").val(),
"fromMemberId": $(".wpgFromMemberDropdownList").val(),
"toMemberId": $(".wpgToMemberDropdownList").val(),
"voucherExpiryDate": $("#expiryDatePicker").val(),
"recipients": [{'firstname':'a','lastname':'b','email':'c','voucheramount':'d'}]
};
Try this, It should work
[HttpPost]
public void TestVoucherCreationEmails([FromBody]postDataObject postedData)
{
string g = "";
}

Categories

Resources