Unable to bind selectlistitem to model property - c#

I have a couple of classes like below -
public class P
{
public int Id { get; set; }
public virtual ICollection<PDetail> PDetails { get; set; }
}
public class PDetail
{
public int Id { get; set; }
public int Type { get; set; }
public double Price { get; set; }
}
Now, in my View, I am displaying it as -
#foreach (var detail in Model.PDetails)
{
<div class="row">
<div class="col-sm-6">
#Html.DropDownListFor(m => detail.Type, (IEnumerable<SelectListItem>)ViewData["Types"], "--Type--", htmlAttributes: new { #class = "form-control" })
</div>
<div class="col-sm-6">
#Html.TextBoxFor(m => detail.Price, "", htmlAttributes: new { #class = "form-control", placeholder = "Price" })
</div>
</div>
}
Here, I am able to display detail.Price for each detail object, but detail.Type is not getting selected from ViewData["Types"] dropdownlist.
PS: ViewData["Types"] is just a dictionary of typeIds = {1,2,3...}
Info1
I also tried changing the View to -
#for (int i = 0; i < Model.PDetails.Count(); i++)
{
<div class="row">
<div class="col-sm-6">
#Html.DropDownListFor(m => m.PDetails.ElementAt(i).Type, (IEnumerable<SelectListItem>)ViewData["Types"], "--Type--", htmlAttributes: new { #class = "form-control" })
</div>
</div>
}
But it is still not working. How can I go about fixing it?

Thanks for sticking through my barrage of questions. I was able to reproduce your issue, and after a whole lot of attempts to get it to work, it seems that this may just be a bug in the MVC framework that hasn't been fixed in over 4 years.
This answer provides two workarounds:
https://stackoverflow.com/a/3529347/1945651
One involves using a somewhat verbose bit of code to manually add the value to the ModelState.
The other requires your list of items to be indexable with square brackets (e.g. an array or IList/IList<T>), and then involves adding the current value as a default value in the SelectList passed to the HTML helper:
#for (int i = 0; i < Model.PDetails.Count(); i++)
{
<div class="row">
<div class="col-sm-6">
#Html.DropDownListFor(m => m.PDetails[i].Type,
new SelectList(ViewData["Types"] as IEnumerable,
"Value", "Text", Model.PDetails[i].Text),
"--Type--",
htmlAttributes: new { #class = "form-control" })
</div>
</div>
}
Could you give that a try?

Related

.NET MVC 5 Check Box list from database using View Model [duplicate]

This question already has answers here:
Pass List of Checkboxes into View and Pull out IEnumerable [duplicate]
(2 answers)
Closed 6 years ago.
I need to populate a checkbox list of equipment on a form for users to request equipment. The data for the list is stored in a table named 'Equipment'. I am working with EF 6 database first. The view is strongly typed and will write to an 'Orders' table. I am stuck on how to use a View Model and not ViewBag to populate the check box list for the form. I have looked at MikesDotNetting, the Rachel Lappel post about view models and several others and it's not making sense to me.
Code below:
public class Equipment
{
public int Id { get; set; }
public string Description { get; set; }
public string Method { get; set; }
public bool Checked { get; set; }
}
public class Order
{
public int id{ get; set; }
public string Contact_Name { get; set; }
public List<Equipment>Equipments { get; set; }
public string Notes { get; set; }
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Contact_Name,Equipment,Notes")] Order order)
{
if (ModelState.IsValid)
{
db.Orders.Add(order);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(order);
}
View
#model CheckBoxList.Models.Order
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Order</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Contact_Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Contact_Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Contact_Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
//checkbox list populated here
</div>
<div class="form-group">
#Html.LabelFor(model => model.Notes, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Notes, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Notes, "", new { #class = "text-danger" })
</div>
</div>
See this answer for how to do it How to bind checkbox values to a list of ints?
That example uses Guid's as PK's but that can be easily replaced with int's.
I'm going to assume your Equipment class is your EF entity.
So you are creating your order page so let's start with the CreateOrderViewModel
public class CreateOrderViewModel
{
public string Contact_Name { get; set; }
public Dictionary<int, string> AllEquipment{ get; set; }
public int[] SelectedEquipment { get;set; }
public string Notes { get; set; }
}
Populate AllEquipment with just the id and the name of the piece of equipment. This is the complete list of equipment that will be needed to show all the equipment checkboxes with the value of the id of the equipment.
Something like
var viewModel = new CreateOrderViewModel {
AllEquipment = context.Equipment.ToDictionary(e => e.Id, e.Description);
}
SelectedEquipment is the list of equipment with checkboxes checked. So when you post this information back, the SelectedEquipment property will have a list of all the id's that need to be attached to the order.
When you create the order just iterate through the list and add them to the Equipment list in your Order entity.
Make a for loop in your list and generate a checkbox for every item in it.
<div class="form-group">
#for (int i = 0; i < Model.Equipments.Count(); i++)
{
#Html.CheckBoxFor(x => x.Equipments[i].Checked)
#Model.Equipments[i].Description
//If you need to hide any values and get them in your post
#Html.HiddenFor(x => x.Equipments[i].Id)
#Html.HiddenFor(x => x.Equipments[i].Method)
}
</div>

Showing and hiding controls from both controller and view

I am trying to create a best possible solution for this but as this is the first time I am encountering this scenario so I am not sure how to best implement this.
I have a very simple model,
public class Feedback
{
[Required]
[Display(Name = "Current ID")]
public int? PreviousID { get; set; }
[Required]
[Display(Name = "Next ID")]
public int? NextID { get; set; }
[Required]
public int ScenarioID { get; set; }
[Display(Name = "Select you scenario")]
public IEnumerable<SelectListItem> YourScenario { get; set; }
}
When User first loads the view then only dropdownlist for YourScenario and TextBox for PreviousID is displayed. When User select dropdownlist then based on its value the TextBox for NextID is displayed to user. Here is my view,
<div class="form-group">
#Html.LabelFor(m => m.YourScenario, new { #class = "col-sm-3 control-label" })
<div class="col-sm-9">
#Html.DropDownListFor(m => m.ScenarioID, m.YourScenario, "Choose Scenario", new { #class = "form-control chosen-select" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.PreviousID, new { #class = "col-sm-3 control-label" })
<div class="col-sm-9">
#Html.TextBoxFor(m => m.PreviousID)
</div>
</div>
<div class="form-group" style="display:none">
#Html.LabelFor(m => m.NextID, new { #class = "col-sm-3 control-label" })
<div class="col-sm-9">
#Html.TextBoxFor(m => m.NextID)
</div>
</div>
To show/hide the NextID I use the Jquery on the view,
$('#YourScenario').change(function () {
var selectedValue = $(this).val();
var nextID = $('#NextID');
if (selectedValue = "3") {
nextID .show();
}
else {
nextID .hide();
}
});
All this works great. Now to use the same View as Edit Mode I pass the model to the view from controller. What I want is that the TextBox for NextID should be displayed or hidden automatically based on the model values. If I use if condition on the View then the control is not available through Javascript so how can I achieve this?
Here is a simple example: https://jsfiddle.net/jma71jf7/
When you run the code, it will hide the input, because there is no selected value, and this line will trigger the change event function:
$('#YourScenario').trigger('change');
But when editing, the select will have some value, and it will hide/show the input according to the value.

Helper for DropDown List in ASP.NET MVC

I'm creating ASP.NET MVC application and I have problem with my dropdowns in "Register" page.
First, my model (Created by EF in "database first" mode):
public partial class User()
{
...
public int CountryId { get; set; }
}
public partial class Country()
{
...
public int Id { get; set; }
public String Name { get; set; }
}
NOTE: User and Country are related by CountryId (is not a collection)
In my controller:
ViewBag.countries = context.Countries.ToList();
return View();
In my view:
<select name="CountryId" id="CountryId">
#foreach (var item in ViewBag.countries)
{
<option value="#item.Id" id="#item.Id">#item.Name</option>
}
</select>
So, I think that I need use this helpers:
#Html.DropDownList
#Html.DropDownListFor
But it's imposible doing work...
The default code of ASP.NET for views is:
<div class="form-group">
#Html.LabelFor(model => model.CountryId, "CountryId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CountryId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CountryId, "", new { #class = "text-danger" })
</div>
</div>
And this code throws this error:
Additional information: There is no ViewData item of type 'IEnumerable' that has the key 'CountryId'.
Any suggestions? Thanks in advance!!

dependency validation based on dropdown selecion in MVC4

I have a dropdropdown list have values Yes, No. If you select yes I have validate next two textboxes. How can I do dependency validation based on dropdown selection?
public class MeetingAbstract
{
public string HasMaterialPublishedElseWhereLabel { get; set; }
public string HasMaterialPublishedElseWhereOptions { get; set; }
[Required]
public string HasMaterialPublishedElseWhereText { get; set; }
public string DtPublishedTimeId { get; set; }
public string DtPublishedTimeLabel { get; set; }
//validate this based on HasMaterialPublishedElseWhereText =Yes value
public string DtPublishedTimeText { get; set; }
public string PublishedPlaceId { get; set; }
public string PublishedPlaceLabel { get; set; }
//validate this based on HasMaterialPublishedElseWhereText =Yes value
public string PublishedPlaceText { get; set; }
}
view
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-6">#Html.Label(Model.HasMaterialPublishedElseWhereLabel,
new {#class = "control-label mandatory"})</div>
<div class="col-md-4">
#{
options = Model.HasMaterialPublishedElseWhereOptions;
optionsList = options.Split(',').ToList();
optionSelect = optionsList.Select(option => new SelectListItem()
{Text = option, Value = option}).ToList();
}
#Html.DropDownListFor(model => model.HasMaterialPublishedElseWhereText,
optionSelect, i18n_Models_Abstract.SelectOption, new { #class = "input-
validation-error form-control" })
#Html.ValidationMessageFor(model => model.HasMaterialPublishedElseWhereText,
i18n_Models_Abstract.RequiredField,
new { style = "padding-left: 5px" })
</div>
</div>
</div>
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-6">#Html.Label(Model.DtPublishedTimeLabel, new {#class =
"control-label mandatory"})</div>
<div class="col-md-4">#Html.TextBoxFor(model => model.DtPublishedTimeText, new \
{#class = "form-control", #placeholder = Model.DtPublishedTimeLabel,
required = "required", maxlength = 40})
#Html.ValidationMessageFor(model => model.DtPublishedTimeText,
i18n_Models_Abstract.RequiredField, new { style = "padding-left: 5px" })</div>
</div>
</div>
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-6">#Html.Label(Model.PublishedPlaceLabel, new {#class =
"control-label mandatory"})</div>
<div class="col-md-4">#Html.TextBoxFor(model => model.PublishedPlaceText, new
{#class = "form-control", #placeholder = Model.PublishedPlaceLabel,
required = "required", maxlength = 40})
#Html.ValidationMessageFor(model => model.PublishedPlaceText,
i18n_Models_Abstract.RequiredField, new { style = "padding-left: 5px" })
</div>
</div>
</div>
I would add a Validate method on your model which will be called and exposed via ModelState.IsValid:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
if (this.HasMaterialPublishedElseWhereText == "Yes") {
yield return new ValidationResult("This isn't valid! Let me tell you why...");
}
}
Then in your controller simply call:
if (ModelState.IsValid) {
//You can only get this far if your Validate method
//doesn't return any ValidationResult objects.
//Do your magic!
}
Obviously this won't be executed client-side so if validation on the client is a requirement then you might need to roll that in.
One last note is that if you only have two values (e.g. Yes and No) a radio button may make more sense than a drop down.

How to get dynamic dropdownlist selected value

I am new to MVC and using MVC4 for project.
Here is the piece of code in view -
#model SomeModel
#{
for (var i = 0; i < Model.NumberOfQuestions; i++)
{
<div class="ques">
<div class="leftCol">
<label>#Resources.Enroll.Question</label>
</div>
<div class="rightCol">
#Html.DropDownListFor(m => m.Question, Model.Questions, new { id = "ddlQuestion" + i, #data_native_menu = "false", onchange = "SelectedIndexChanged(id)" })
#Html.ValidationMessageFor(m => m.Question)
</div>
<div class="clear" />
<div id="#("customQuestion" + i)" class="hide">
<div class="leftCol">
<label>#Resources.Enroll.CustomQuestion</label>
</div>
<div class="rightCol">
#Html.TextBoxFor(m => m.CustomQuestion, new { id = "txtCustomQues" + i })
#Html.ValidationMessageFor(m => m.CustomQuestion)
</div>
</div>
<div class="clear" />
<div class="leftCol">
<label>#Resources.Enroll.Answer</label>
</div>
<div class="rightCol">
#Html.TextBoxFor(m => m.Answer, new { id = "txtAnswer" + i })
#Html.ValidationMessageFor(m => m.Answer)
</div>
<div class="clear" />
</div>
}
}
The problem here is I'm not able to get some way of managing selected value for all dropdownlist and passing them to controller.
I am able to get single value retained in Question which is a public property in Model. But I am not getting how to do for all dynamically.
Can you please guide me how to do same?
Create a view model that represents the questions you are going to select i.e.
public class QuestionViewModel
{
public string QuestionId { get; set; }
}
Then add them to your existing model, without seeing your current one it should resemble this:
public class QuestionsViewModel
{
public QuestionsViewModel()
{
SelectedQuestions = new List<QuestionViewModel>();
}
public List<SelectListItem> QuestionsSelectList { get; set; }
public List<QuestionViewModel> SelectedQuestions { get; set; }
}
Use indexing in your for loop with the count of the Questions. The important bit is how the QuestionId is used by the model binder to send it back in the post:
#for(var i = 0; i < Model.SelectedQuestions.Count; i++)
{
#Html.DropDownListFor(m => m.SelectedQuestions[i].QuestionId, Model.QuestionsSelectList as List<SelectListItem>, "Select..", new { #data_native_menu = "false", onchange = "SelectedIndexChanged(id)" })
}
When the form is submitted you will have a collection of selected questions i.e.
var selectedQuestion1 = model.SelectedQuestions[0].QuestionId;

Categories