Json Array return null - javascript

Array in for loop returns null.
When I do this:
abc.a = "1";
abc.b = "1";
abc.c = "1";
It's all fine.
But this returns null:
for (var i = 0; i < 3; i++) {
abc[i] = "1";
}
The Controller Of Mvc:
public class abcController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(abc val)
{
return View();
}
}
MODEL mvc abcCLASS:
public class abc
{
public string a {get;set;}
public string b { get; set; }
public string c { get; set; }
}
Html + Jquery
I want that the for will work too.
why the in the for loop the object return null?
<input type="button" id="bbb"/>
<script>
var abc = { a: "", b: "", c: "" };
$("#bbb").click(function () {
// This not working:
for (var i = 0; i < 3; i++) {
abc[i] = "1";
}
// This working:
abc.a = "1";
abc.b = "1";
abc.c = "1";
$.ajax({
url: '/abc/index',
type: "POST",
dataType: 'json',
traditional: true,
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(abc),
success: function (response) {
console.log(response);
}
});
});
</script>

You're attempting to assign some value to each element of an object. The problem is, you attempt to treat that object like an array, thinking that JS will magically understand what you want to do. It won't.
One possible way to actually do what you want:
for (var i in abc) if (abc.hasOwnProperty(i)) {
abc[i] = "1";
}
This way a, b, c properties of abc object will get "1" value. As it stands, you change 0, 1, 2 properties instead (yes, those are valid names for Object properties in JS; they're much more often used in Arrays, however).

Related

How to fix Ajax call "The parameters dictionary contains a null entry........."

I'm trying to send my data via ajax call to the controller but I check all my code I debug it all values are filled but it continuously shows me this error. event its also show me ON MVC call As Well
The parameters dictionary contains a null entry for parameter 'BrandId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Forms(System.Collections.Generic.IEnumerable1[System.Web.HttpPostedFileBase], System.String, Int32, Int32, Boolean, Int32)'in'AdminDevVersion.Controllers.HomeController'`. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
i have already debugged the code and I have checked if all the values are filled.
My Ajax Code
function uploadSubmitHandler() {
var PName = $("#ProductName").val();
var Category = $("#CategoryDropDown").val();
var Brand = $("#BrandDropDown").val();
var SubCategory = $("#SubCategory").val();
var ProductDescription = $('textarea.textarea-editor').val();
var NewOROldRadio = $("input[name='ProductStatus']:checked").val();
if (state.fileBatch.length !== 0) {
var data = new FormData();
for (var i = 0; i < state.fileBatch.length; i++) {
data.append('files', state.fileBatch[i].file, state.fileBatch[i].fileName);
}
data.append('ProductName', PName);
data.append('CategoryId', Category);
data.append('BrandId', Brand);
data.append('IsNewStatus', NewOROldRadio);
data.append('SubCategoryId', SubCategory);
data.append('Description', ProductDescription);
$.ajax({
type: 'POST',
url: options.ajaxUrl,
data: data,
cache: false,
contentType: false,
processData: false
});
}
}
Controller Code
[HttpPost]
public ActionResult Forms(IEnumerable<HttpPostedFileBase> files, String ProductName, int CategoryId, int BrandId, bool IsNewStatus, int SubCategoryId)
{
List<string> FileNames = new List<string>();
string ImageName = null;
TblProduct InsertProduct = new TblProduct();
if (ModelState.IsValid == true)
{
InsertProduct.Name = ProductName;
InsertProduct.IsActive = IsNewStatus;
InsertProduct.BrdId = BrandId;
InsertProduct.CatId = SubCategoryId;
InsertProduct.Image = ImageName;
InsertProduct.Created = DateTime.Now;
InsertProduct.IsActive = true;
_RepoProduct.Insert(InsertProduct);
_RepoProduct.Save();
TblProRelImg RelatedImages = new TblProRelImg();
foreach (HttpPostedFileBase file in files)
{
string _path = System.IO.Path.Combine(Server.MapPath("~/Content/Images/"), file.FileName);
file.SaveAs(_path);
if (file == files.First())
{
ImageName = file.FileName.ToString();
}
else
{
RelatedImages.PrdID = InsertProduct.ID;
RelatedImages.Image = file.FileName;
_ReporRelatedImages.Insert(RelatedImages);
_ReporRelatedImages.Save();
}
FileNames.Add(file.FileName);
}
ViewBag.CategoryId = Logics.Category();
ViewBag.BrandInfo = new SelectList(DbContext.TblBrands, "Id", "Name");
}
return View();
}
I have expected to send data to the controller
parse your BrandId to int like blow and append to your data
parseInt(Brand)

Object array in FormData passing 0 objects to controller

I'm making an ajax call to the controller passing the FormData(), which has an array of objects along with few other properties. In my controller, the list array which I'm passing seems to have 0 elements. Please help!
Script in cshtml view -
var _getFormDataToJson = function () {
var applyDetail = [];
$(_tb).find('tbody tr').each(function (i, v) {
var trans = {
effectiveDate: $(this).find('.effectiveDate').val(),
amount: $(this).find('.amount').val(),
empLeaveHdID: $('#tx-leaveHdID').val(),
//attachmentUrl: $(this).find('.leaveAttachment')[0].files[0]
}
applyDetail.push(trans);
});
var formObj = new FormData();
formObj.append('remark', $('#tx-remark').val());
formObj.append('leaveAppType', $('#hdnLeaveAppType').val());
formObj.append('applyDetail', applyDetail); //this collection has 0 items in controller
return formObj;
}
var _sumbitForm = function () {
var formData2 = _getFormDataToJson();
$.ajax({
url: '#Url.Action("ApplyLeave", "Leave")',
type: 'POST',
processData: false,
contentType: false,
data: formData2,
//data: { data: formData2 },
success: function (data) {
if (data.success) {
_myToastr.success(data.msg[0], true, function () {
location.reload();
});
$(_modal).modal('close');
}
else {
_myToastr.error(data.msg[0]);
}
},
complete: function () {
}
});
}
Controller -
[HttpPost]
public JsonResult ApplyLeave(Hr_LeaveApplyHd data)
{
foreach (var detail in data.applyDetail) //applyDetail count is 0 here
{
//to DO:
}
return new JsonResult();
}
EDIT:
Hr_LeaveApplyHd model -
public class Hr_LeaveApplyHd
{
public Hr_LeaveApplyHd()
{
applyDetail = new List<ApplyDetail>();
}
[Key]
public int applyID { get; set; }
public string remark { get; set; }
public virtual List<ApplyDetail> applyDetail { get; set; }
public LeaveAppType leaveAppType { get; set; }
}
applyDetail model -
public class ApplyDetail
{
[Key]
public int applyDetialID { get; set; }
public DateTime effectiveDate { get; set; }
public decimal amount { get; set; }
public int empLeaveHdID { get; set; }
}
You cannot append arrays and/or complex objects to FormData. You need to append name/value pairs for each property of ApplyDetail and for each item in the collection, and with indexers, for example
formObj .append('applyDetail[0].effectiveDate', '09/19/2017');
which you could do in your $.each loop, for example
var formObj = new FormData();
formObj.append('remark', $('#tx-remark').val());
formObj.append('leaveAppType', $('#hdnLeaveAppType').val());
$(_tb).find('tbody tr').each(function (i, v) {
var name = 'applyDetail[' + i + '].effectiveDate';
var value = $(this).find('.effectiveDate').val();
formObj.append(name, value);
... // ditto for other properties
});
However, if you have generated your form correctly using the strongly typed HtmlHelper methods, including generating the controls for the collection property using a for loop of EditorTemplate for typeof ApplyDetail so they have the correct name attributes to match your model, then all you need is
var formObj = new FormData($('form').get(0));
which will correctly serialize all the form controls

How can I prevent passing in multiple parameters to controller?

Right now I have a form that contains 6 security questions and 6 security answers. I've been trying to refactor my code and I'm running into an interesting situation that I'm not sure on how to proceed.
Here's my view:
var RequestSecurityQuestions_Submit = function () {
ValidationAttribute.BlankValue(true);
var form = $('form#RequestSecurityQuestions');
$.validator.unobtrusive.parse(form);
var d = $('form#RequestSecurityQuestions').serialize();
SecurityQuestionsValid = true;
var inputs = $('form#RequestSecurityQuestions').find('input[data-val]');
$.each(inputs, function (index) {
var input = inputs[index];
if (!$(input).valid()) {
SecurityQuestionsValid = false;
}
});
var dataObject = {}, dropdowns = $("input.customdropdownlist");
for (var i = 0; i < 6; i++) {
dataObject['question' + i] = $(dropdowns[i]).data("kendoDropDownList").value()
}
for (var i = 1; i < 7; i++) {
dataObject['answer' + i] = $('#idAnswer' + i).val();
}
var dataToPass = JSON.stringify(dataObject);
if (SecurityQuestionsValid) {
$.ajax({
url: Url.getFullUrl('Account/RequestSecurityQuestions_Submit'),
type: 'Post',
data: { securityInfo: dataToPass },
dataType: 'json',
cache: false,
success: function (data) {
//Next Dialog
},
error: AjaxLog.HandleAjaxCallFail
});
}
return SecurityQuestionsValid;
}
I get a dataObject which contains all the values from my view and I want to pass it to the controller.
Currently this works:
[AllowAnonymous]
[HttpPost]
public ActionResult RequestSecurityQuestions_Submit(string answer1, string answer2, string answer3, string answer4, string answer5, string answer6, string question1, string question2, string question3, string question4, string question5, string question6)
{
SecurityQuestions securityQuestions = new SecurityQuestions();
if (!string.IsNullOrEmpty(answer1))
{
securityQuestions.ChallengeA1 = answer1;
}
if (!string.IsNullOrEmpty(answer2))
{
securityQuestions.ChallengeA2 = answer2;
}
if (!string.IsNullOrEmpty(answer3))
{
securityQuestions.ChallengeA3 = answer3;
}
//etc....
}
However, I am passing in 12 parameters to my controller which sounds like a no-no to me. Is there another way to pass in my data from my view to my controller without having to pass in 12 parameters?
EDIT:
New controller attempt:
/*Problem: securityInfo array looks like: ""{\"question0\":\"2\",\"question1\":\"3\",\"question2\":\"4\",\"question3\":\"5‌​\",\"question4\":\"7\",\"question5\":\"1\",\"answer1\":\"fgfg\",\"answer2\":\"fgf‌​gf\",\"answer3\":\"fgfg\",\"answer4\":\"fgfgfg\",\"answer5\":\"fgfg\",\"answer6\"‌​:\"fggf\"}"" */
[AllowAnonymous]
[HttpPost]
public ActionResult RequestSecurityQuestions_Submit(string[] securityInfo)
{
SecurityQuestions securityQuestions = new SecurityQuestions();
}
There are multiple ways to achieve this of course, the following is one of them. A better one would be to restructure your code as per other answers/comments.
// Creating your array
var dataObject = [],
dropdowns = $("input.customdropdownlist");
//Populate your array. [0-5] will be questions and [6-11] will be answers .
for (var i = 0; i < 6; i++) {
if (i < 6){
dataObject[i] = $(dropdowns[i]).data("kendoDropDownList").value()
}
else {
var d = i - 5;
dataObject[i] = $('#idAnswer' + d).val();
}
}
// Your AJAX call with contentType
$.ajax({
url: Url.getFullUrl('Account/RequestSecurityQuestions_Submit'),
type: 'Post',
data: JSON.Stringify(dataObject), //Change data format to JSON array, will be received as array in backend
contentType: "application/json",
cache: false,
success: function (data) {
//Next Dialog
},
error: AjaxLog.HandleAjaxCallFail
});
And simply receive the array in your backend
public ActionResult RequestSecurityQuestions_Submit(List<String> data)
{
//Your code here
}
Mate, Well, I don't understand why you have N parameters which has the same type of data--- that's really where the class should come for.
Short Answer will be you can create a Model which contains these parameters, then on your controller, just RequestSecurityQuestions_Submit(Model postedModel)
then access your parameters inside like postedModel.parameter1 ... In you ajax call , it looks like
$.ajax({
url: Url.getFullUrl('Account/RequestSecurityQuestions_Submit'),
type: 'Post',
data: {parameter1:'',parameter2:''...},
cache: false,
success: function (data) {
//Next Dialog
},
Multiple parameter(n>3) on any function is bad in theory and practice,
You can simplify all this by correctly using a model, binding to a model and posting back you model.
View models
public class SecurityAnswerVM
{
[Required(ErrorMessage="Please select a question")]
[Display(Name = "Question")]
public int? QuestionID { get; set; }
[Required(ErrorMessage = "Please enter an answer")]
public string Answer { get; set; }
}
public class SecurityLoginVM
{
public SelectList QuestionList { get; set; }
public List<SecurityAnswerVM> SelectedQuestions { get; set; }
}
Controller
[HttpGet]
public ActionResult Index()
{
SecurityLoginVM model = new SecurityLoginVM();
// Populate SelectedQuestions and QuestionList from the database
return View(model);
}
[HttpPost]
public ActionResult Index(SecurityLoginVM model)
{
// model.SelectedQuestions contains the 6 objects containing the QuestionID and the users Answer
....
}
EditorTemplate (/Views/Shared/EditorTemplates/SecurityAnswerVM.cshtml)
#model yourAssembly.SecurityAnswerVM
#Html.LabelFor(m => m.QuestionID)
#Html.DropDownListFor(m => m.QuestionID, (SelectList)ViewData["options"], "Please select", new { #class = "question" })
#Html.ValidationMessageFor(m => m.QuestionID)
#Html.LabelFor(m => m.Answer)
#Html.TextAreaFor(m => m.Answer)
#Html.ValidationMessageFor(m => m.Answer)
Main view
#model yourAssembly.SecurityLoginVM
#using(Html.BeginForm())
{
#Html.EditorFor(m => m.SelectedQuestions, new { options = Model.QuestionList })
<button id="save type="submit">Save</button> // or type="button" is posting via ajax
}
And if you want to use ajax to post the data
var url = '#Url.Action("Index")';
$('#save').click(function() {
$.post(url, $('form').serialize(), function(data) {
// Next Dialog
});
});
Far less code, strongly typed model binding, client and server side validation and all the other beneficial features of using MVC!
Why not doing something like this -
public class QA {
public int QId {get;set;}
public string Answer {get;set;}
}
[AllowAnonymous]
[HttpPost]
public ActionResult RequestSecurityQuestions_Submit(QA[] answers)
{
for (var qa in answers){
//do what ever you like
}
}
and then in js -
// Creating your array
var dataObject = [],
var dropdowns = $("input.customdropdownlist");
for (var i = 0; i < 6; i++) {
dataObject[i] = {
'QId': $(dropdowns[i]).data("kendoDropDownList").value(),
'Answer': dataObject[i] = $('#idAnswer' + i).val();
};
}
$.ajax({
url: Url.getFullUrl('Account/RequestSecurityQuestions_Submit'),
type: 'Post',
data: JSON.stringify(dataObject),
contentType: "application/json",
cache: false,
success: function (data) {
},
error: function(data){
}
});

How can I map the names of an array of objects to change the field names?

I am sending data from my client to a method on my WebApi controller that accepts a class of ResponseFromClient. As it's WebApi this handles all the conversion from JSON and changes any field names to match the case convention differences between javascript and C#:
public class ResponseFromClient
{
public int QuestionId { get; set; }
public ICollection<AnswerDetail> Answers { get; set; }
public string Task { get; set; }
}
public class AnswerDetail
{
public int AnswerId { get; set; }
public string Text { get; set; }
public bool? Correct { get; set; }
public bool? Response { get; set; }
}
In Javascript I have an array called ans with each element looking like this:
qs.ans[index].id = element.answerId;
qs.ans[index].c = null;
qs.ans[index].r = false;
qs.ans[index].text = element.text;
I need to send the contents of a Javascript object responseFromClient to the C# server program:
var responseFromClient = {
questionId: qs.q,
answers: qs.ans,
task: task
};
$http({
method: 'POST',
url: url,
data: responseFromClient
})
Is there a simple way that I can set the answers field in the javascript variable responseFromClient so that it receives an array that contains objects that map to the AnswerDetail class. In other words I need to change it so that the answers gets and array of objects that are like this:
old > new
id > AnswerID
c > Correct
t > Text
r > Response
I was thinking to do it like I do where I remap qs.q > questionId. However ans is an array so I am not sure how to do this.
Is it not easier allround to map the object onto the class in your JavaScript
qs.ans[index].answerId = element.answerId;
qs.ans[index].correct = null;
qs.ans[index].response = false;
qs.ans[index].text
or
in the ajax call
data: { "answerId" : responseFromClient.id,
"correct": responseFromClient.c,
"response": responseFromClient.t,
"text": responseFromClient.text }
You can simply remap the values you want with arrays of old => new keys. There are more advanced ways of doing this, but I don't know all the details of your case...
function remap(obj, old_keys, new_keys){
var len = old_keys.length;
var new_obj = {};
for (var i = 0; i < len; i++) {
new_obj[new_keys[i]] = obj[old_keys[i]];
}
return new_obj;
}
var len = ans.length;
var old_keys = ["id", "c", "r", "text"];
var new_keys = ["AnswerId", "Correct", "Response", "Text"]
for (var i = 0; i < len; i++) {
ans[i] = remap(ans[i], old_keys, new_keys);
}

Binding MVC model in controller which has a complex array

Most of my model is populated using $('form').serializeArray(), however an array of objects that form a paged grid need to be populated from its manager.
For example:
public JsonResult SubmitForm(MyViewModel input)
{
...
public class MyViewModel
{
[Display(Name = "Name")]
public string GridName { get; set; }
[Display(Name = "Description")]
public string GridDescription { get; set; }
public GridRow[] GridRows { get; set; }
The name and description would be picked up by serializeArray(), no issues there. If the GridRow is a string[], then it accepts me simply pushing multiple instances to it into the serialized array that jquery made:
var data = $('form').serializeArray();
for (var i in gridData) {
data.push({ name: 'GridRows', value: gridData[i].id });
}
$.ajax({
type: "POST",
url: '/Central/Results/SubmitForm',
dataType: "json",
data: data,
This way I can at least get an array of the IDs. However, it does not allow me to push the entire object into it (gridData[i]) when I want to populate the proper data type. I always get a null value when it reaches the controller.
Any idea how I need to handle the data in order for MVC to populate the model correctly? Thanks.
I'm pretty sure this is related to having to set the traditional option to true in your Ajax post. jQuery handles arrays a little differently than you'd expect, in terms of when they are posted to MVC controller actions.
So do this:
$.ajax({
type: "POST",
url: '/Central/Results/SubmitForm',
dataType: "json",
traditional: true,
data: data,
...
See this answer for more details.
Turns out just need to add a line and property reference, and add each variable separately.
for (var i in gridData) {
for (var j in gridData[i]) {
data.push({ name: 'GridRows[' + i + '].' + j, value: gridData[i][j] });
}
}
Edit: Just thought I'd post my updated helper method I wrote a while ago for this.
function serializeArray(obj, data, name) {
/// <summary>Turns an object into an MVC standard array of values </summary>
/// <param name="obj" type="Object">Object to serialise (required)</param>
/// <param name="data" type="Array">Array to append data to end of (optional)</param>
/// <param name="name" type="String">Name prefix for all entries (optional/ignore)</param>
if (!data || !(data instanceof Array)) {
data = [];
}
if (obj instanceof Array) {
if (!name) {
throw "Need an object name when converting an array";
}
for (var index = 0; index < obj.length; index++) {
data = serializeArray(obj[index], data, name + '[' + index + ']');
}
} else if (obj instanceof Object) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
data = serializeArray(obj[property], data, name ? (name + '.' + property) : property);
}
}
} else {
data.push({ name: name, value: obj });
}
return data;
}

Categories

Resources