ASP.NET MVC Binding to a Dictionary in View - asp.net-mvc

I have an ASP.NET MVC 3 application that binds data to a Model. Simple objects within the Model are working perfectly like: ContactName and Telephone.
I am doing this by simply having: #Html.TextBoxFor(m => m.ContactName)
The Model contains a Dictionary object which isn't getting binded with my current implementation.
Can anyone give me an example of how I can do this? The current code I have for binding to a Dictionary is:
<div>
#*TABS*#
<ul class="nav nav-tabs">
#for (var i = 0; i < Model.Translations.Count; i++)
{
<li class="#i == 0 ? 'active' : ''">#Model.Translations.Keys.ToList()[i]</li>
}
</ul>
#*TABCONTENT*#
<div class="tab-content" style="overflow: visible;">
#foreach (var translation in Model.Translations)
{
for (var i = 0; i < Model.Translations.Count; i++)
{
<div class="#i == 0 ? 'active' : ''" id="#translation.Value.CultureCode">#translation.Value.Title</div>
}
#Html.TextBoxFor(m => translation.Value.Title[]);
#Html.TextBoxFor(m => translation.Value.FullDescription);
#Html.TextBoxFor(m => translation.Value.PreviewDescription);
}
</div>
</div>
Any help is highly appreciated.

use "your list property"[index].value.id
example
if you have:
public IList<KeyValuePair<int,string>> Properties { get; set; }
you should write in view:
for (int i = 0; i < Model.Properties.Count; i++)
{
#Html.EditorFor(item=>Model.Properties[i].Value)
}
updated:
#for (var i=0; i < Model.Translations.Count; i++) {
<div id="tabs-#(i)">
<div class="#i == 0 ? 'active' : ''" id="#Model.Translations[i].Value.CultureCode">#Model.Translations[i].Value.Title</div>
#Html.TextBoxFor(m => Model.Translations[i].Value.Title);
#Html.TextBoxFor(m => Model.Translations[i].Value.FullDescription);
#Html.TextBoxFor(m => Model.Translations[i].Value.PreviewDescription); }
</div>
</div>
</div>

Try this, I am considering translation is the Dictionary Object:
#foreach (var category in translation) {
#Html.TextBoxFor(m => category.Value.Title);
#Html.TextBoxFor(m => category.Value.FullDescription);
#Html.TextBoxFor(m => category.Value.PreviewDescription);
}

Related

How can i pass multiple radio button values to controller in ASP.NET MVC?

I've a model that contains 3 tables in my view.
public class InExam
{
public AutoTests TheTest { get; set; }
public List<InTest> TheQuestions { get; set; }
public IEnumerable<Result> SingleQuee { get; set; }
}
First one made to get the detailed page, like "admin/AutoTests/id"
Second one made to get a list of questions linked to the page
Third one is to save radio button strings to post it back into the controller
my plan is to get (say) 20 questions that are linked with the detailed page, Adding 4 radio buttons for each question, and post back every selected button to the controller.
my view form :
#using (Html.BeginForm("Test", "Exams", new { id = Model.TheTest.id }, FormMethod.Post))
{
foreach (var item in Model.TheQuestions)
{
Kafo.Models.Result singleQuee = Model.SingleQuee.Where(x => x.Question == item.Question).FirstOrDefault();
<div class="container" style="padding-top:50px;direction:rtl;">
<h4 style="text-align:right;font-weight:bold;">#item.Question</h4>
<div class="container">
<div class="row" style="direction:rtl;">
<div class="col-lg-7" style="text-align:right;margin-right:10px;">
<div class="row">
#Html.RadioButtonFor(x => singleQuee.Question, new { #class = "form-control dot", #Name = singleQuee.Question, #Value = "1" })
<h5 style="padding-top:3px;padding-right:8px;">#item.RightAnswer</h5>
</div>
</div>
<div class="col-lg-7" style="text-align:right;margin-right:10px;">
<div class="row">
#Html.RadioButtonFor(x => singleQuee.Question, new { #class = "form-control dot", #Name = singleQuee.Question, #Value = "2" })
<h5 style="padding-top:3px;padding-right:8px;">#item.Answer2</h5>
</div>
</div>
<div class="col-lg-7" style="text-align:right;margin-right:10px;">
<div class="row">
#Html.RadioButtonFor(x => singleQuee.Question, new { #class = "form-control dot", #Name = singleQuee.Question, #Value = "3" })
<h5 style="padding-top:3px;padding-right:8px;">#item.Answer3</h5>
</div>
</div>
<div class="col-lg-7" style="text-align:right;margin-right:10px;">
<div class="row">
#Html.RadioButtonFor(x => singleQuee.Question, new { #class = "form-control dot", #Name = singleQuee.Question, #Value = "4" })
<h5 style="padding-top:3px;padding-right:8px;">#item.Answer4</h5>
</div>
</div>
#Html.HiddenFor(m => singleQuee.Question)
</div>
</div>
</div>
}
<button class="btn botton" type="submit" onclick="return confirm('');">END</button>
}
i used this line "Kafo.Models.Result singleQuee = Model.SingleQuee.Where(x => x.Question == item.Question).FirstOrDefault();" in my view because i can't use tuple foreach ( C# ver. 5 )
This is my controller code :
[HttpGet]public ActionResult Test(int? id)
{
using (KafoEntities db = new KafoEntities())
{
InExam model = new InExam();
model.TheTest = db.AutoTests.Where(x => x.id == id).FirstOrDefault();
model.TheQuestions = db.InTest.Where(x => x.UserEmail == currentUser.Email && x.ExamId == model.TheTest.id).OrderByDescending(x => x.id).Take(Convert.ToInt32(model.TheTest.QuestionsNumber)).ToList();
model.SingleQuee = db.Result.ToList();
return View(model);
}
}
[HttpPost]
public ActionResult Test(int? id, List<Result> singleQuee)
{
using (KafoEntities db = new KafoEntities())
{
int result = 0;
foreach (Result item in singleQuee)
{
Result sets = db.Result.Where(x => x.id == item.id).FirstOrDefault();
sets.Question = item.Question;
db.SaveChanges();
var check = db.InTest.Where(x => x.Question == item.Question).FirstOrDefault();
if (check != null)
{
if (item.Question == "1")
{
result++;
}
}
}
return RedirectToAction("Results", "Exams", new { Controller = "Exams", Action = "Results", id = done.id });
}
}
I first save the new string that came from the radio button value into the result record, then i call it back in the if condition to check it's value
The problem here is i get a
Object reference not set to an instance of an object.
when i post the test, it means that the list is empty, so i need to know what makes the radio buttons not working,
Thanks.
If you want to bind a List of object in Mvc, you should name the controller like "ModelName[indx].PropertyName". In your case it should be "singleQuee[0].Question".
Code Sample
var Indx = 0;
foreach (var item in Model.TheQuestions)
{
.....
var radioName = $"singleQuee[{Indx}].Question";
<div class="col-lg-7" style="text-align:right;margin-right:10px;">
<div class="row">
<input type="radio" name="#radioName" value="1" />
<h5 style="padding-top:3px;padding-right:8px;">#item.RightAnswer</h5>
</div>
</div>
.....
}
Action Method

#Html.HiddenFor returning null value

I am trying to return the results of a table back to the controller for further manipulation. Once returned to the controller the value shows as null. In the past I have been able to use #Html.HiddenFor to return the values but it doesn't seem to be working in this instance. Not sure what I am doing wrong here. Any help is greatly appreciated.
#model IEnumerable<Project.Models.Item>
#{
ViewBag.Title = "Welcome to The Project";
}
#using (Html.BeginForm("UpdateQuality", "Home", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
<div class="row">
<div class="form-group">
<table class="table table-bordered">
<tr>
<th>#Html.DisplayNameFor(m => m.Name)</th>
<th>#Html.DisplayNameFor(m => m.SellIn)</th>
<th>#Html.DisplayNameFor(m => m.Quality)</th>
</tr>
#for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>#Html.DisplayFor(m => m.ElementAt(i).Name)</td>
<td>#Html.DisplayFor(m => m.ElementAt(i).SellIn)</td>
<td>#Html.DisplayFor(m => m.ElementAt(i).Quality)</td>
#Html.HiddenFor(m => m.ElementAt(i).Name)
#Html.HiddenFor(m => m.ElementAt(i).SellIn)
#Html.HiddenFor(m => m.ElementAt(i).Quality)
</tr>
}
</table>
<div class="form-group">
<div style="margin-top: 50px">
<input type="submit" class="btn btn-primary" value="Advance Day"/>
</div>
</div>
</div>
</div>
}
And here is the controller which returns null.
public ActionResult UpdateQuality(List<Item> Items )
{
return View("Index", (object)Items);
}
You cannot use ElementAt() in a HtmlHelper method that generates form controls (look at the name attribute your generating - it does not match your model).
Either change the model to be IList<T>
#model List<Project.Models.Item>
and use a for loop
#for (int i = 0; i < Model.Count; i++)
{
....
#Html.HiddenFor(m => m.[i].Name)
....
or change use a custom EditorTemplate for typeof Item, and in the main view, use #Html.EditorFor(m => m) to generate the correct html for each item in the collection.

send Dynamic list to Controller from View

I am getting quite responses when I add/delete rows.
When page is loaded the default rows 3 will be added.(But user can delete those rows)
When I am trying to add new row then 4 value rows are coming in controller but when I am trying to delete any default row then it is not showing the newly added row.
Here is my below code.
#model ProductRegistration.Models.InstalledProductInformationModel
#using (Html.BeginForm())
{
#for (int i = 0; i < ((Model != null && Model.listInstallProducts != null) ? Model.listInstallProducts.Count : 3); i++)
{
<tr>
<td>
#Html.TextBoxFor(m => m.listInstallProducts[i].SerialNumber, new { #class = "form-control input-sm input-Serial", #placeholder = "Enter serial number", #maxlength = 50, #OnKeypress = "return alphanumeric_only(event);" })
#Html.ValidationMessageFor(m => m.listInstallProducts[i].SerialNumber)
<div class="SerialErrorDisplay" style="color: red"></div>
</td>
<td>
#Html.DropDownListFor(m => m.listInstallProducts[i].ModelNumber, new SelectList(string.Empty, "Value", "Text"), "--Select Model--", new { #class = "form-control input-sm input-Model" })
#Html.ValidationMessageFor(m => m.listInstallProducts[i].ModelNumber)
<div class="ModelErrorDisplay" style="color: red"></div>
</td>
<td>
#Html.TextBoxFor(m => m.listInstallProducts[i].InstallationDate, new { #class = "form-control input-sm input-Date", #maxlength = 50, #placeholder = "DD/MM/YYYY" })
#Html.ValidationMessageFor(m => m.listInstallProducts[i].InstallationDate)
<div class="DateErrorDisplay" style="color: red"></div>
</td>
<td>
<img src="~/Images/delete_icon.png" onclick="DeleteProduct(this);" />
</td>
</tr>
}
<input class="btn btn-primary top-padding" type="submit" value="Previous" id="back-step" name="direction"/>
}
In Controller:
public ActionResult InstalledProductInfo(InstalledProductInformationModel InstalledProducts, string direction)
{
if(ModelState.IsValid)
{
return RedirectToAction("EquipmentOwnerInfo");
}
return View(InstalledProducts);
}
Model is:
public class InstalledProductInformationModel : InstalledProductInformation
{
public InstalledProductInformation installedProductInformation { get; set; }
public List<InstalledProductInformation> listInstallProducts { get; set; }
}
please help me out.
By default, the DefaultModelBinder required collection indexers to start at zero and be consecutive. If you delete the first item (indexer = 0) then no items will be bound. If you delete the 3rd item (indexer = 2) then only the first 2 items will be bound. You can override this behavior by adding an input for a special property named Index where the value equals the indexer.
#for (int i = 0; i < ((Model != null && Model.listInstallProducts != null) ? Model.listInstallProducts.Count : 3); i++)
{
<tr>
<td>
#Html.TextBoxFor(m => m.listInstallProducts[i].SerialNumber, new { #class = "form-control input-sm input-Serial", #placeholder = "Enter serial number", #maxlength = 50, #OnKeypress = "return alphanumeric_only(event);" })
#Html.ValidationMessageFor(m => m.listInstallProducts[i].SerialNumber)
<div class="SerialErrorDisplay" style="color: red"></div>
</td>
....
<td>
// Add the following input
<input type="hidden" name="listInstallProducts.Index" value="#i" />
<img src="~/Images/delete_icon.png" onclick="DeleteProduct(this);" />
</td>
</tr>
}
<input class="btn btn-primary top-padding" type="submit" value="Previous" id="back-step" name="direction"/>
Side note: You should be adding the 3 items in the controller before you pass the model to the view so you get proper model binding and then simply use #for (int i = 0; i < Model.listInstallProducts.Count; i++)
List indexes are enumerated continuously, so there can be no gap. If You remove one row, the list index is no more continuous. The new row should be there if You will deal with indexes.

Send single value through RouteValueDictionary

One of the properties of my ViewModel is an array which, unfortunately, is null every time I post back to the controller. I figured a simple hack where I place the values into a coma-delimited string.
This works great for our paging plugin, which posts back to our Index method, using a RouteValueDictionary. However, it is not working in the Html.BeginForm helper which posts back to a different controller action (the Update method).
View
#*Since we can't send arrays or complex objects break array down into string for RouteValueDictionary*#
var channelCodes = "";
for (int i = 0; i < Model.searchChannelCode.Length; i++)
{
channelCodes += Model.searchChannelCode[i];
if (i + 1 < Model.searchChannelCode.Length)
{
channelCodes += ",";
}
}
#*The 'searchChannelCodesPagin' variable from this RouteValueDictionary always posts back as null
using (Html.BeginForm("Update", "ZipCodeTerritory", new RouteValueDictionary()
{
{"searchChannelCodesPaging", channelCodes }
}, FormMethod.Post, new {id = "UpdateForm"}))
{
#Html.HiddenFor(model => model.searchZip)
#Html.HiddenFor(model => model.searchTerritory)
#Html.HiddenFor(model => model.searchState)
#Html.HiddenFor(model => model.searchActiveOnly)
#Html.HiddenFor(model => model.zipCodeTerritory)
<div id="cloneBox">
<div id="rw1">
#Html.LabelFor(model => model.newTerritory)
#Html.TextBoxFor(model => model.newTerritory, new { style = "width: 30px;padding-left:10px;", maxLength = 3 })
#Html.LabelFor(model => model.newDescription)
#Html.TextBoxFor(model => model.newDescription, new { style = "width: 250px;padding-left:10px;", maxLength = 30 })
#Html.LabelFor(model => model.newEffectiveDate)
#Html.TextBoxFor(model => model.newEffectiveDate, new { style = "width: 80px;padding-left:10px;" })
<div id="rw2" style="padding-top: 10px;">
#Html.LabelFor(model => model.newChannelCode)
#Html.DropDownListFor(model => model.newChannelCode, Model.ChannelCodes, " ")
#Html.LabelFor(model => model.newStateCode)
#Html.DropDownListFor(model => model.newStateCode, Model.StateCodes, " ")
#Html.LabelFor(model => model.newEndDate)
#Html.TextBoxFor(model => model.newEndDate, new { style = "width: 80px;" })
</div>
</div>
</div>
<br/>
<div id="buttonDiv">
<button type="submit" id="CloneButton" name="button" value="clone">Apply New Data</button>
<button type="submit" id="deleteButton" name="button" value="delete">Delete Selected Items</button>
<button type="submit" id="removeButton" name="button" value="removeErrors">Remove Selected Errors</button>
</div>
}
Controller
The forma above posts to this controller action. The searchChannelCodePaging variable is null each time.
[HttpPost]
public ActionResult Update(ZipCodeIndex updateZip, string button, string searchChannelCodesPaging)
{
Since you are doing a post, the simplest way to get it to the backend would be add a hidden field:
#Html.HiddenFor("searchChannelCodesPaging", searchChannelCodesPaging);
As a routing value, you may need to get it explicitly within the control via one of the two following approaches. These objects are directly accessible within the Controller class.
RouteData.Values("searchChannelCodesPaging")
Request.QueryString.Get("searchChannelCodesPaging");
You don't have to serialize a array type model parameter to a CSV string to get it to post to your controller. You can do this instead:
#for (var i = 0; i < Model.searchChannelCode.Length; i++)
{
#Html.HiddenFor(m => m.searchChannelCode[i]);
}

Reading collection of user input from the view?

I have created a page to display list of questions to user & read user input for each question.
Issue: When user submits the Form, Model Parameter is null.
Post Action of the View is defined as below -
[HttpPost]
public ActionResult Index(IEnumerable<Question> model)
{
return View(model);
}
Code to display options to user is as below -
#foreach (var quest in Model)
{
<li>
#Html.LabelFor(x => quest.Title, quest.Title)
<ol class="Opt">
#foreach (var opt in quest.Options)
{
<li>#Html.RadioButtonFor(o => opt, opt.Title)
#Html.LabelFor(o => opt.Title, opt.Title)
</li>
}
</ol>
</li>
}
Here model is #model List<LakshyaMvc.Models.Question> and of course I have wrapped the code inside #using (Html.BeginForm())
Change your View Model.
#using (Html.BeginForm("Index", "Queries", FormMethod.Post))
{
for (int i = 0; i < Model.Count; i++)
{
<li>
#Html.LabelFor(x => Model[i].Question, Model[i].Question)
<ol class="Opt">
#for (int j = 0; j < Model[i].Options.Count; j++)
{
<li>#Html.RadioButtonFor(o => Model[i].Options[j].Option, false)
#Html.LabelFor(o => Model[i].Options[j].Option,
Model[i].Options[j].Option)
</li>
}
</ol>
</li>
<input type="submit" name="Submit" value="Submit" />
}
}

Resources