Binding Complex Types in MVC5 - binding

I'm a newbie developer trying to develope a web application with asp .net mvc 5 for personel usage. The website kind of a quiz maker that I can insert Russian words with meanings and prepare a quiz by using these words.
When I trying to code the quiz page and post the data the the action method I faced some problem that I couldn't get around. I iterated through the Model, read the data and wrote them to the page. Now, what I want to do is, when I post the form, I want to get each question string and selected answer (maybe in this format: imgur.com/QETnafx). Therefore, I can easily check the answer string whether it is true or not.
I checked the following tutorials out:
Model Binding To A List by Phil Haack and ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries by Scott Hanselman
I hope I explained the situation clearly. If you need more information I can happily provide.
ViewModel
public class QuizInitializationModel
{
public List<Question> Questions { get; set; }
}
public class QuestionString
{
public int Id { get; set; }
public string WordString { get; set; }
}
public class Question
{
public QuestionString QuestionString { get; set; }
public List<AnswerItem> Answers { get; set; }
}
public class AnswerItem
{
public int Id { get; set; }
public string WordString { get; set; }
}
VIEW
#using (Html.BeginForm("Begin", "Quiz", FormMethod.Post))
{
<table class="table table-striped table-condensed table-bordered">
#for (int i = 0; i < Model.Questions.Count; i++)
{
<tr>
<td>
#Html.Label(Model.Questions[i].QuestionString.WordString)
</td>
<td>
#for (int item = 0; item < Model.Questions[0].Answers.Count; item++)
{
#Html.Label(Model.Questions[i].Answers[item].WordString)#:
#Html.RadioButton("array" + "[" + #i + "]" + "." + Model.Questions[i].QuestionString.WordString, Model.Questions[i].Answers[item].Id)<br />
}
</td>
</tr>
}
</table>
<input type="submit" value="Send" class="btn btn-primary" />
}
OUTPUT
<form action="/Admin/Quiz/Begin" method="post">
<table class="table table-striped table-condensed table-bordered">
<tr>
<td>
<label for="">вулкан</label>
</td>
<td>
<label for="trade">trade</label>
<input id="array_0________" name="array[0].вулкан" type="radio" value="18" /><br />
<label for="volcano">volcano</label>
<input id="array_0________" name="array[0].вулкан" type="radio" value="24" /><br />
<label for="talk__conversation">talk, conversation</label>
<input id="array_0________" name="array[0].вулкан" type="radio" value="15" /><br />
<label for="time">time</label>
<input id="array_0________" name="array[0].вулкан" type="radio" value="13" /><br />
<label for="income">income</label>
<input id="array_0________" name="array[0].вулкан" type="radio" value="21" /><br />
</td>
</tr>
<tr>
<td>
<label for="">мама</label>
</td>
<td>
<label for="universe">universe</label>
<input id="array_1______" name="array[1].мама" type="radio" value="25" /><br />
<label for="peace">peace</label>
<input id="array_1______" name="array[1].мама" type="radio" value="2" /><br />
<label for="value">value</label>
<input id="array_1______" name="array[1].мама" type="radio" value="20" /><br />
<label for="mom__mama">mom, mama</label>
<input id="array_1______" name="array[1].мама" type="radio" value="17" /><br />
<label for="industry">industry</label>
<input id="array_1______" name="array[1].мама" type="radio" value="19" /><br />
</td>
</tr>
</table>
And how can i fix the ids of the labels like "array_1______" ?
They appeared when I added this code "array" + "[" + #i + "]" + "." to the RadioButton control for the purpose of assign an index for each answer.

Html helpers replace invalid characters with an underscore (a period is actually not invalid, but would cause problems with jquery so its also replaced an underscore). However id attribute is not the problem, although you are generating duplicates which is invalid html.
Your manually generating the name attribute for the radio buttons which have no relationship to any property in your model so wont be bound when you post back. Your model needs to include a property you can bind the selected answer to. Modify the Question model to
public class Question
{
public QuestionString QuestionString { get; set; }
public List<AnswerItem> Answers { get; set; }
public int SelectedAnswer { get; set; } // add this
}
and modify the view to
#using (Html.BeginForm()) // note parameter not necessary if your posting to the same controller/action
{
#for (int i = 0; i < Model.Questions.Count; i++)
{
...
#Html.HiddenFor(m => m.Questions[i].QuestionString.Id)
<h2>#Model.Questions[i].QuestionString.WordString)</h2>
...
#foreach(var answer in Model.Questions[i].Answers)
{
var id = string.Format("{0}-{1}", #i, answer.Id);
#Html.Label(id, answer.WordString)
#Html.RadioButtonFor(m => m.Questions[i].SelectedAnswer, answer.ID, new { id = id })
}
....
}
<input type="submit" value="Send" class="btn btn-primary" />
}
The radio buttons are now bound to the SelectedAnswer property and when you post back, the value will be the ID of the selected AnswerItem
Note also:
A hidden input has been added for the ID of the question so the
question can be identified on post back
The question text is in a heading tag (could be another tag), but a
label is not appropriate - a label is an element associated with a
control (for setting focus to the control) but you don't have an
associated control
A unique id is created in the foreach loop so you can give each
radio button a unique id (so the html is valid) and associate the
label (the answer text) with the button

Related

How to create object that contains a list of object in a single form?

public class Basket
{
public int Id { get; set; }
public string Sharp { get; set; }
public string Material { get; set; }
public List<Fruit> Fruits { get; set; }
}
public class Fruit
{
public int Id { get; set; }
public string Color { get; set; }
public string Taste { get; set; }
}
With the above example, how could I create both Basket and Fruit in the same asp-form without using any JavaScript?
<form method="post" asp-controller="Basket" asp-action="Create">
<input asp-for="Material" />
<input asp-for="Sharp" />
#*I would like to also create custom amounts of new Fruit in this form.*#
<input type="submit" value="Submit" />
</form>
If my razor form is defined as the above example, how could I create custom amounts of Fruit and create Basket at the same form? It is possible to avoid using JavaScript in this case?
It is possible to avoid using JavaScript in this case?
Based on your scenario and current architecture what you need to do is, there should be a table where you would be adding your fruit object as it's a List<Fruit> Fruit kind of. As per your given code, your output should be as below:
So, I would say, Javascript would make it easier. If you would like to avoid javascript it wouldn't be impossible but would be costly and complex.
how could I create custom amounts of Fruit and create Basket at the
same form?
You could follow the below steps to achieve what you are trying to implement.
View:
#model DotNet6MVCWebApp.Models.Basket
<form method="post" asp-controller="Yonny" asp-action="Create">
<div class="form-group">
<label asp-for="Material" class="col-md-2 form-label"></label>
<input asp-for="Material" class="col-md-6 form-control" />
<span asp-validation-for="Material" class="form-span-error"></span>
</div>
<div class="form-group" style="padding-bottom:20px">
<label asp-for="Sharp" class="col-md-2 form-label"></label>
<input asp-for="Sharp" class="col-md-6 form-control" />
<span asp-validation-for="Sharp" class="form-span-error"></span>
</div>
#*I would like to also create custom amounts of new Fruit in this form.*#
<div style="padding-bottom:20px">
<button type="button" class="btn btn-primary" onclick="AddRow()">Add Fruit</button>
</div>
<div id="dataTable">
<table>
<thead>
<tr>
<th>Id</th>
<th>Color</th>
<th>Taste</th>
</tr>
</thead>
<tbody id="FruitList" data-count="0">
</tbody>
</table>
</div>
<input type="submit" class="btn btn-success" value="Submit" />
</form>
#section Scripts {
<script>
/*
. Hidding table on load
*/
document.getElementById('dataTable').style.display ='none';
function AddRow()
{
var countVal = parseInt($('#FruitList').attr('data-count'));
var html = '';
html += '<tr>';
html += '<td><input type="text" name="Fruits[' + countVal + '].Id" class="form-control"/></td>';
html += '<td><input type="text" name="Fruits[' + countVal + '].Color" class="form-control"/></td>';
html += '<td><input type="text" name="Fruits[' + countVal + '].Taste" class="form-control"/></td>';
html += '</tr>';
$('#FruitList').append(html);
countVal += 1;
$('#FruitList').attr('data-count', countVal);
/*
. Showing table when adding item into
*/
document.getElementById('dataTable').style.display ='block';
}
</script>
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
[Bind("Id,Material,Sharp,Fruits")] DotNet6MVCWebApp.Models.Basket basket)
{
if (ModelState.IsValid)
{
//Save Basket
_context.Add(basket);
await _context.SaveChangesAsync();
//Add Fruits List
foreach (var item in basket.Fruits)
{
_context.Add(item);
await _context.SaveChangesAsync();
}
return RedirectToAction(nameof(Create));
}
return View(basket);
}
Note:
If you somehow got null data while sending request to controller make sure your binding property that is Bind("Id,Material,Sharp,Fruits") are same as name="Fruits[' + countVal + '].Id" inside the javascript function
Output:

ASP.NET MVC : bind form with List<Model> in view model

I have a view model with the following properties:
// I set the values from the database
public List<Document> AvailableDocuments { get; set; }
// I need to set the values from a front end <form>
public List<RequiredDocument> RequiredDocuments { get; set; }
The RequiredDocument model contains the following properties:
// This should be an Id, maybe a hidden input
public Document Document { get; set; }
// This should be a number input
public int RequiredCopies { get; set; }
// This should be a checkbox
public bool IsRequired { get; set; }
In my view I'm looping through AvailableDocuments and every iteration should bind to a RequiredDocument model (where the user may set the values for the RequiredCopies number).
The form is submitted via Ajax. How can I bind the form to RequiredDocuments?
#foreach (Document doc in Model.AvailableDocuments)
{
<div class="reqdoc">
<!-- RequiredDocument.Document -->
<input type="hidden" name="Document" value="#doc.Id" />
<div class="form-check">
<!-- RequiredDocument.IsRequired -->
<input class="form-check-input" type="checkbox" value="" />
<label class="form-check-label">
#doc.Name
</label>
</div>
<!-- RequiredDocument.RequiredCopies -->
<input class="form-control" type="number" />
</div>
}
You can use this kind of for loop I am doing similar in my projects & it works
<table>
#for (int i = 0; i < (int)ViewBag.Count; i++)
{
#Html.HiddenFor(model => model.AvailableDocuments.ToList()[i].ID)
<tr>
<td>
#Html.CheckBoxFor(model => model.RequiredDocuments.ToList()[i].IsRequired, new { id = "chk_" + i, #class = "custom-checkbox" })
</td>
<td>
#Html.DisplayFor(model => model.AvailableDocuments.ToList()[i].Name)
</td>
<td>
#Html.TextBoxFor(model => model.RequiredDocument.ToList()[i].RequiredCopies, new { id = "RequiredCopies" + i, #class = "form-control" })
</td>
</tr>
}
</table>
Just pass a ViewBag.Count from Your get method.
Work with index and modify the name attribute as:
#{
int i = 0;
foreach (Document doc in Model.AvailableDocuments)
{
<div class="reqdoc">
<!-- RequiredDocument.Document -->
<input type="hidden" name="RequiredDocuments[#i].Document.Id" value="#doc.Id" />
<div class="form-check">
<!-- RequiredDocument.IsRequired -->
<input class="form-check-input" type="checkbox" value="" name="RequiredDocuments[#i].IsRequired" />
<label class="form-check-label">
#doc.Name
</label>
</div>
<!-- RequiredDocument.RequiredCopies -->
<input class="form-control" type="number" name="RequiredDocuments[#i].RequiredCopies" />
</div>
i++;
}
}

Pass Object from view to controller using mvc4

View
#if (weekMaster != null)
{
using (Html.BeginForm("UpdatePlan", "generalPlan", FormMethod.Post, new { }))
{
<table class="table-bordered">
<tr>
#foreach (TermMaster obj in weekMaster.ToList())
{
<td align="center">
<span> #obj.termStartDate.ToString("dd MMM") - #obj.termEndDate.ToString("dd MMM")</span>
<br />
<input type="hidden" name="ObjHid" value="#obj" />
<input type="hidden" name="startDate" value="#obj.termStartDate" />
<input type="hidden" name="endDate" value="#obj.termEndDate" />
<input type="text" style="width:80%" name="weekSession" />
</td>
}
<td>
<input type="submit" value="Update" class="btn-primary" />
</td>
</tr>
</table>
} }
Controller
[HttpPost]
public ActionResult UpdatePlan(List<DateTime> startDate, List<DateTime> endDate, List<int> weekSession, List<TermMaster> ObjHid)
{
return View();
}
I am trying pass Class Object from View to Controller above TermMaster class Object pass using input method <input type="hidden" name="ObjHid" value="#obj" /> but showing NULL value if pass single value like startDate and endDate then it work fine.
What is wrong in my code? how to pass class object List in Post Method?
Please refer Image
You can not bind objects to controller from your input. You can serialize object to json. In the controller you can take your inputs value as string and deserialize it.
You have to do it by below approach.
Create a model instead of multiple parameters, and use index in cshtml.
public class model
{
public List<DateTime> startDate { get; set; }
public List<DateTime> endDate { get; set; }
public List<int> weekSession { get; set; }
public List<TermMaster> ObjHid { get; set; }
}
CSHTML
#{ int i = 0; }
#foreach (TermMaster obj in weekMaster.ToList())
{
<td align="center">
<span> #obj.termStartDate.ToString("dd MMM") - #obj.termEndDate.ToString("dd MMM")</span>
<br />
<input type="hidden" name="ObjHid[#i].termStartDate" value="#obj.termStartDate.ToString("dd MMM")" />
<input type="hidden" name="ObjHid[#i].termStartDate" value="#obj.termStartDate.ToString("dd MMM")" />
<input type="hidden" name="startDate[#i]" value="#obj.termStartDate" />
<input type="hidden" name="endDate[#i]" value="#obj.termEndDate" />
<input type="text" style="width:80%" name="weekSession[#i]" />
</td>
i++
}

Sending table datas to Controller without using ajax

I have a table in view which is static in nature.I am trying to post data to Controller without using Jquery Ajax. Is it possible?How can I retrieve all these data in Action "bulk_insert". I am able to send this bulk data to controller using jquery ajax but I am unable to send it directly.
My view :
#using entity_framework.Models
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="container">
<h1>Static data</h1>
<div>
#using (Html.BeginForm("bulk_insert", "Form", FormMethod.Post, new { enctype = "multipart/form-data", id = "postForm" }))
{
<table id="myTable" class="table table-striped">
<thead>
<tr>
<td>Menu Item Id</td>
<td>Menu Item Name</td>
<td>Menu Qty</td>
<td>Menu Rate</td>
<td>Total</td>
</tr>
</thead>
<tbody>
<tr>
<td><input type="hidden" name="menu_item_id[0]" value="1" /><span>1</span></td>
<td><input type="hidden" name="menu_item_name[0]" value="chaumin" /><span>chaumin</span></td>
<td><input type="hidden" name="data[0].rate" value="100" /><span>100</span></td>
<td><input type="hidden" name="data[0].qty" value="2" /><span>2</span></td>
<td><input type="hidden" name="data[0].total" value="200"><span>200</span></td>
</tr>
<tr>
<td><input type="hidden" name="data[1].menu_item_id" value="2" /><span>2</span></td>
<td><input type="hidden" name="data[1].menu_item_name" value="Mo:Mo" /><span>Mo:Mo</span></td>
<td><input type="hidden" name="data[1].rate" value="100" /><span>100</span></td>
<td><input type="hidden" name="data[1].qty" value="2" /><span>2</span></td>
<td><input type="hidden" name="data[1].total" value="200"><span>200</span></td>
</tr>
</tbody>
</table>
<input type="submit" value="Submit" />
}
</div>
I am just testing with static data right now,but I need to do it with dynamic data i.e, When a user clicks a button, a row gets added using javascript .So,I didn't use Htmlhelper. My model:
public class tabledata
{
public int menu_item_id { get; set; }
public string menu_item_name { get; set; }
public string rate { get; set; }
public string qty { get; set; }
public string total { get; set; }
}
I don't know how can I get table datas from form but my Controller method:
[HttpPost]
public ActionResult bulk_insert(IEnumerable<tabledata> data)
{
//How can I get table datas here
}

Accepting params or raw data in controller?

I was wondering if it would be possible having a "params" argument in a controller function, or something similar which would allow me to process X amount of entries in my form.
For instance, I have a form which has X amount of "name" elements, which are auto-generated through jQuery. An example of these name elements could be the following:
<input type="text" name="studentName1"></input>
<input type="text" name="studentName2"></input>
<input type="text" name="studentName3"></input>
Now, there's a different amount of student names every time, so this makes it quite complex for me to handle the form data in my controller. I had something like the following 2 examples in mind, but of course they wouldn't work in reality.
[HttpPost]
public ActionResult PostStudentNames(params string[] studentNames)
Or:
[HttpPost]
public ActionResult PostStudentNames(string[] formValues)
Can I achieve something similar to that?
I just want to chime in with a different approach you can use for this. If it's more convenient, you can model bind directly to collections of primitive or complex types. Here's 2 examples:
index.cshtml:
#using (Html.BeginForm("ListStrings", "Home"))
{
<p>Bind a collection of strings:</p>
<input type="text" name="[0]" value="The quick" /><br />
<input type="text" name="[1]" value="brown fox" /><br />
<input type="text" name="[2]" value="jumped over" /><br />
<input type="text" name="[3]" value="the donkey" /><br />
<input type="submit" value="List" />
}
#using (Html.BeginForm("ListComplexModel", "Home"))
{
<p>Bind a collection of complex models:</p>
<input type="text" name="[0].Id" value="1" /><br />
<input type="text" name="[0].Name" value="Bob" /><br />
<input type="text" name="[1].Id" value="2" /><br />
<input type="text" name="[1].Name" value="Jane" /><br />
<input type="submit" value="List" />
}
Student.cs:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
HomeController.cs:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult ListStrings(List<string> items)
{
return View(items);
}
public ActionResult ListComplexModel(List<Student> items)
{
return View(items);
}
}
ListStrings.cshtml:
#foreach (var item in Model)
{
<p>#item</p>
}
ListComplexModel.cshtml:
#foreach (var item in Model)
{
<p>#item.Id. #item.Name</p>
}
The first form simply binds a list of strings. The second, binds the form data to a List<Student>. By using this approach, you can let the default model binder do some of the tedious work for you.
Updated for comment
Yes you can do that too:
Form:
#using (Html.BeginForm("ListComplexModel", "Home"))
{
<p>Bind a collection of complex models:</p>
<input type="text" name="[0].Id" value="1" /><br />
<input type="text" name="[0].Name" value="Bob" /><br />
<input type="text" name="[1].Id" value="2" /><br />
<input type="text" name="[1].Name" value="Jane" /><br />
<input type="text" name="ClassId" value="13" /><br />
<input type="submit" value="List" />
}
Controller action:
public ActionResult ListComplexModel(List<Student> items, int ClassId)
{
// do stuff
}
Mathias,
This works perfectly well without recourse to the params object. your form controls:
<input type="text" name="studentName" />
<input type="text" name="studentName" />
<input type="text" name="studentName" />
<input type="text" name="professorName" />
You would use the FormCollection object, which will contain all your form elements as either comma separated lists (if a control array) or as single properties. In the above example, this is what we'd get:
[HttpPost]
public ActionResult PostStudentNames(FormCollection formValues)
{
// basic check for rogue commas inside input controls
// would need far more sophistication in a #real# app :)
var valueStudents = formValues["studentName"].Split(',')
.Where(x => x.Length > 0).ToArray();
var valueProfessor = formValues["professorName"];
// other stuff
}
etc... At least, this is my recollection of this from a recent project. :)
<input type="text" name="studentName[0]"></input>
<input type="text" name="studentName[1]"></input>
<input type="text" name="studentName[2]"></input>
public ActionResult PostStudentNames(string[] studentName)
{
}

Resources