Dropdownlistfor will not show the correct selection - html

I have three dropdownlistfor in a loop that do not show the correct value from the DB. They always default to the first entry. I have checked and double checked the DB and verified that it should be the second one in the list. The list is also created correctly. What am I missing?
#foreach (CustomerMeasurementProfile oProfile in Model.Customer.CustomerMeasurementProfiles.Where(m => m.DeletedDate == null))
{
<div class="valuesforoneprofile form-group form-group-tight col-md-2">
<div class="col-md-11" >
#Html.Hidden(string.Format("Customer.CustomerMeasurementProfiles[{0}].Id", i), oProfile.Id)
#Html.Hidden(string.Format("Customer.CustomerMeasurementProfiles[{0}].CustomerId", i), oProfile.CustomerId)
#Html.TextBox(string.Format("Customer.CustomerMeasurementProfiles[{0}].Name", i), oProfile.Name, new { #class = "form-control input-sm" })
</div>
<div class="col-md-11" style="text-align:center">
#Html.CheckBox(string.Format("DeleteProfiles[{0}]", i), Model.DeleteProfiles[i])
</div>
<div class="col-md-11" style="padding-top:4px;">
#Html.DropDownListFor(m => oProfile.BodyTypeShoulderId, new SelectList(Model.BodyTypeShoulders, "Id", "Name"), new { #class = "form-control input-sm-select" })
</div>
<div class="col-md-11" style="padding-top:4px;">
#Html.DropDownListFor(m => oProfile.BodyTypePostureId, new SelectList(Model.BodyTypePosture, "Id", "Name"), new { #class = "form-control input-sm-select" })
</div>
<div class="col-md-11" style="padding-top:4px;">
#Html.DropDownListFor(m => oProfile.BodyTypeChestId, new SelectList(Model.BodyTypeChest, "Id", "Name"), new { #class = "form-control input-sm-select" })
</div>

If you want to set the selected value that is coming in Model. You need to do it like this:
#Html.DropDownListFor(m => oProfile.BodyTypeShoulderId,
new SelectList(Model.BodyTypeShoulders,
"Id",
"Name",
oProfile.BodyTypeShoulderId),
new { #class = "form-control input-sm-select" })
The above code will set the dropdown selected value to whatever is in the current Model object BodyTypeShoulderId
The first argument of DropDownListFor tells that on form post drop down selected value will be mapped with the Model property which is set there (we are passing m => oProfile.BodyTypeShoulderId) but this not sets selected Value.
For setting selected value you have to pass SelectList fourth parameter using this overload of SelectList class which is object selectedValue

Unfortunately #Html.DropDownListFor() behaves a little differently than other helpers when rendering controls in a loop. For a single object
#Html.DropDownListFor(m => oProfile.BodyTypeShoulderId, new SelectList(Model.BodyTypeShoulders, "Id", "Name"))
would work fine (if the value of BodyTypeShoulderId matches the value of one of the options, then that option would be selected). Ehsan has shown a work around when using it in a loop, however you have a few other issues in you code, not the least is that many of your properties will not post back correctly to a collection because your using a foreach loop rather than a for loop (which is generating duplicate name and id attributes). Your also generating a new SelectList in each iteration of the loop which is not very efficient.
You can solve both these and the dropdown selection issue by using an EditorTemplate. Assuming property CustomerMeasurementProfiles is typeof CustomerMeasurementProfiles, then
CustomerMeasurementProfiles.cshtml (add this to Views/Shared/EditorTemplates or Views/YourController/EditorTemplates)
#model CustomerMeasurementProfiles
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.CustomerId)
....
#Html.DropDownListFor(m => m.BodyTypeShoulderId, (SelectList)ViewData["shoulders"])
....
In the view model, add properties for the SelectList's and filtered collection of the items to display
IEnumerable<CustomerMeasurementProfiles> ActiveCustomerMeasurementProfiles { get; set; }
public SelectList BodyTypeShoulderList { get; set; }
....
and assign those values in the controller
Then in the main view
#using (Html.BeginForm())
{
...
#Html.EditorFor(m => m.ActiveCustomerMeasurementProfiles, new { shoulders = Model.BodyTypeShoulderList })
...
Using #Html.EditorFor() with a collection will correctly name the controls (and correctly select the right options) and on post back, ActiveCustomerMeasurementProfiles will now be correctly populated with all its properties.

Related

How did my ViewBag become populated with data?

I'm working with a Kendo Grid that shows a modal when editing or adding a row. I'm seeking to modify the modal and add another dropdown list to it. The one thing I'm totally confused about at the moment is that the cshtml for the modal refers to the ViewBag to provide the source data for the dropdownlists, but I can't find anywhere in the entire solution where any code (anywhere) populates the ViewBag with the properties the modal uses.
Before I started modifying, the cshtml had:
#Html.Kendo().DropDownListFor(model => model.Status).BindTo(ViewBag.Statuses).DataTextField("Name").DataValueField("Value").OptionLabel("Please Select")
^^^^^^^^^^^^^^^^
The debugger says this is valid; the ViewBag does contain a .Statuses and it is loaded with data, but I've no idea how this thing came to be in the ViewBag. The only place the controller refers to the viewbag is in setting the .Title
Here's cshtml for the modal:
#model ModalModel
#Html.HiddenFor(model => model.Id)
<!-- this is the new one -->
<div class="editor-group">
<div class="editor-label">
#Html.LabelFor(model => model.ProjectId)
</div>
<div class="editor-field">
#Html.Kendo().DropDownListFor(model => model.ProjectId).BindTo(ViewBag.ProjectId_Data).OptionLabel("Please Select")
#Html.ValidationMessageFor(model => model.ProjectId)
</div>
</div>
<!-- existing one. Needs DataTextField and DataValueField because model.Statuses is not an IEnumerable<SelectListItem>, its a custom collection of c# enum name/value representation -->
<div class="editor-group">
<div class="editor-field">
#Html.Kendo().DropDownListFor(model => model.Status).BindTo(ViewBag.Statuses).DataTextField("Name").DataValueField("Value").OptionLabel("Please Select")
#Html.ValidationMessageFor(model => model.Status)
</div>
</div>
Here's a snip of the cshtml for the main grid and some periphery stuff:
#model GridModel
<h3>#ViewBag.Title</h3>
#{
var projectListItems = Model.Projects.Select(e => new SelectListItem { Value = e.Id.ToString(), Text = e.Name });
var activityListItems = Model.Activities.Select(e => new SelectListItem { Value = e.Id.ToString(), Text = e.PrivateName });
}
#(Html.Kendo().Grid<UsageModel>()
.Name("MainGrid")
.Columns(cfg =>
{
cfg.Bound(e => e.DateUsed).ClientTemplate("#= kendo.toString(DateUsed, \"d\") #");
cfg.ForeignKey(e => e.ProjectId, projectListItems, "Value", "Text").Title("Project name").Width(150);
cfg.ForeignKey(e => e.ActivityId, activityListItems, "Value", "Text").Title("Activity name").Width(150);
cfg.ForeignKey(e => e.Status, Model.Statuses, "Value", "Name");
cfg.Command(cmd => { cmd.Edit(); cmd.Destroy().HtmlAttributes(new { style = "visibility:hidden" }); }).Width(80);
})
.Pageable()
...
The 4 items in the ViewBag are:
ProjectId_Data (IEnumerable<SelectListItem>)
ActivityId_Data (IEnumerable<SelectListItem>)
Status_Data (IEnumerable<SelectListItem>)
Statuses (IEnumerable<a custom internal type used for expanding enums into name/value strings>)
Am I correct in assuming that Kendo added these things to the viewbag as part of the data binding process on the main grid? The rendering of the grid to page occurs before the processing of the modal..
Please give (IEnumerable) inside BindTo() for casting and try
#using Kendo.Mvc.UI
#using System.Collections
#Html.Kendo().DropDownListFor(model => model.Status).BindTo((IEnumerable)ViewBag.Statuses).DataTextField("Name").DataValueField("Value").OptionLabel("Please Select")

Modifying dropdown menu - Autogenerated MVC / CRUD code

I am new to Entity Framework/MVC and need to know how to modify a dropdown menu autogenerated by Visual Studio. I imagine it should be pretty simple, but I have not been able to figure it out. I have used the database-first approach and have the two following tables in my database:
public partial class RestaurantRating
{
public int RestaurantRatingId { get; set; }
public int RestaurantRatingScore { get; set; }
}
public partial class RestaurantType
{
public int RestaurantTypeId { get; set; }
public string RestaurantTypeDesc { get; set; }
}
I removed the extra details, but basically one of them will store restaurant ratings (the rating being an integer) and the other one will store restaurant types (what type of food they serve). The only really difference between the two is that the rating is an integer and they type description is a string.
Visual Studio autogenerated code for the CRUD operations for these and other tables. The HTML code in Create.cshtml for these two tables is as follows:
<div class="form-group">
#Html.LabelFor(model => model.RetaurantTypeId, "RetaurantTypeId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("RetaurantTypeId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.RestaurantTypeId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RestaurantRatingId, "RestaurantRatingId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("RestaurantRatingId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Id, "", new { #class = "text-danger" })
</div>
</div>
And the ViewBag information for these two tables in the Create Action result in the controller is the following:
ViewBag.RestaurantRatingId = new SelectList(db.RestaurantRating, "RestaurantRatingId", "RestaurantRatingId");
ViewBag.RestaurantTypeId = new SelectList(db.RestaurantType, "RestaurantTypeId", "RestaurantTypeDesc");
The problems and expected results are the following:
The dropdown menu for RestaurantType works as expected. It simply loads the different types into a dropdown menu and allows the user to select one of them. However, the RestaurantRating will load the RatingIds instead of the descriptions, which is what I need. I have tried changing the viewbag without success.
The HTML code automatically selects the first value for the dropdown menus, but it is possible to save NULL values to the database for these fields. How can I add an empty default value for the dropdown menus above, so that if the user selects the empty value (or does not touch the dropdown menu) a NULL value will be pushed to the database?
Any help is greatly appreciated. I will be happy to provide any additional code/information. Thank you so much!
You Just need to add an option label to your dropdown like this:-
#Html.DropDownList("RetaurantTypeId", null,"optionLable goes Here", htmlAttributes: new { #class = "form-control" })
and make sure on the other side that you are binding it to a nullable model property so the model binder will be able to set the model property value to Null.
hope this answer your question.

How to set programatically the Id, Name and Class atribute of a #Html.EditFor or #Html.TextBoxFor - MVC 4

I am trying to generate a unique label and and input text box for a partial view that is being used to render a list of user input rows.
By unique I mean that each input text box should have its unique html "id" and "name" so that when is submitted each input can be identified
In the View I have
#model UserDataModel
#{
var inpName = "benefName" + #Model.Row;
var inpAge = "benefAge" + #Model.Row;
}
#Html.LabelFor(x => x.Name, new { #class="labelhalf"})
#Html.EditorFor(x => x.Name, new { id = #inpName, htmlAttributes = new { #class = "form-control animated" } })
When the view is being render this is what I am seeing
<label class="labelhalf" for="Name">Nombre (Opcional)</label>
<input class="text-box single-line" id="Name" name="Name" type="text" value="">
As you can see the "name" and "id" attributes of the text input is "Name" and "Name" and is not using the value of the #inpName variable ("benefName1" for example)
Also I am trying to assign some CSS classes to that same input using "htmlAttributes"
I had previously tried this with this approach
<label form="FormStep_01" for=#inpName class="labelhalf">Nombre (Opcional)</label>
<input form="FormStep_01" id=#inpName class="form-control animated" pattern="^[_A-z0-9]{1,}$" type="text" placeholder="" required="">
...but the content of the input fields with this approach are not being submited and that is the reason I am trying to use the #Html.EditorFor
UPDATE
I am now using the TextBoxFor which takes the "id" and the "class" fine but not the "name" which is used in the submit
#Html.LabelFor(x => x.Name, new { #class = "labelhalf" })
#Html.TextBoxFor(x => x.Name, new { #id = #inpName, name = #inpName, #class = "form-control animated" })
Please let me know how to achieve this in MVC4
Issue 1 (Using EditorFor())
You cannot add html attributes using EditorFor() in MVC-4. This feature was not introduced until MVC-5.1, and then the correct usage is
#Html.EditorFor(m => m.SomeProperty, new { htmlAttributes = new { someAttribute = "someValue" }, })
Issue 2 (Using TextBoxFor())
You cannot change the value of the name attribute using new { name = "someValue" }. The MVC team built in a safe guard to prevent this because the whole purpose of using the HtmlHelper methods to generate form controls is to bind to your model properties, and doing this would cause binding to fail. While there is a workaround, if you do discover it, don't do it! As a side note - the following line of the private static MvcHtmlString InputHelper() method in the source code
tagBuilder.MergeAttribute("name", fullName, true);
is what prevents you overriding it.
Issue 3 (Manual html)
You not giving the inputs a name attribute. A form posts back a name/value pair based on the name and value attributes of successful controls, so if there is no name attribute, nothing will be sent to the controller.
Side note: If your manually generating html, there is no real need to add an id attribute unless your referring to that element in javascript or css.
Its unclear why your trying to create a input for something that does not appear to relate to your model, but if your trying to dynamically generate items for adding items to a collection property in your model, refer the answers here and here for some options which will allow you to bind to your model.

Generating SelectList with default selected item in MVC

I'm trying to generate a selectList with a default selected item in MVC. I assign a selectlist to the viewbag and try to render it in my view and keep getting the below error.
Does anyone know what I might be doing wrong?
Create the list in my controller:
ViewBag.MasterAccountSelect = new SelectList(masterAccountsList, "MasterAccount", "MasterAccount", userSettingsViewModel.MasterAccountName);
Render the SelectList in my view:
<div class="editor-label">
#Html.LabelFor(model => model.MasterAccountSelect, "MasterAccountSelect")
</div>
<div class="editor-field">
#Html.DropDownList("MasterAccountSelect")
#Html.ValidationMessageFor(model => model.MasterAccountSelect)
</div>
I get this error:
Compiler Error Message: CS1061: 'UserSettingsViewModel' does not
contain a definition for 'MasterAccountSelect' and no extension method
'MasterAccountSelect' accepting a first argument of type
'UserSettingsViewModel' could be found (are you missing a using
directive or an assembly reference?)
I also get this error when I
You view model does not have a property named MasterAccountSelect hence the error. From the code you are assigning to ViewBag, you appear to be wanting to bind to a property in your model named MasterAccountName in which case your model should be
public class UserSettingsViewModel
{
[Display(Name = "Master Account Name")]
[Required(ErrorMessage = "Please select a master account name")]
public string MasterAccountName { get; set; }
public SelectList MasterAccountNameList { get; set; }
....
}
Then in the GET method
UserSettingsViewModel model = new UserSettingsViewModel
{
MasterAccountName = ..... ,
MasterAccountNameList = new SelectList(masterAccountsList, "MasterAccount", "MasterAccount")
}
return View(model);
And in the view
<div class="editor-label">
#Html.LabelFor(m => m.MasterAccountName)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.MasterAccountName, Model.MasterAccountNameList, "-Please select-")
#Html.ValidationMessageFor(m => m.MasterAccountName)
</div>
Side note: Since you wanting the options value and display text to be the same (using property MasterAccount), you can also use
MasterAccountNameList = new SelectList(masterAccountsList.Select(x => x.MasterAccount))
try Label instead of LabelFor, You can not use LabelFor strictly type html helper if you don't have property in your model.
<div class="editor-label">
#Html.Label("MasterAccountSelect")
</div>
<div class="editor-field">
#Html.DropDownList("MasterAccountSelect")
#Html.ValidationMessageFor(model => model.MasterAccountSelect)
</div>
One good Suggestion
Whenever you are using Viewbag for filling dropdown, Keep your Viewbag and property name different because it will not auto select your selected value.

How to change value of #Html.EditorFor

I'm working on the simple webpage where user will be able to change some data.
I'm using #Html.EditorFor for changing that data. But I have some problems.
Here You can see my HTML Code:
<div class="form-group">
#Html.LabelFor(model => model.DeviceUser, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DeviceUser, new { #Value = ViewBag.id})
#Html.ValidationMessageFor(model => model.DeviceUser)
</div>
</div>
As You can see I'm trying to replace DeviceUser with new Id which is passed from Controler using ViewBag.
But for unknow reason for me this textbox always holds old value.
Can anyone suggest me how to fix it?
Try changing this
#Html.EditorFor(model => model.DeviceUser, new { #Value = ViewBag.id})
to
#Html.TextBoxFor(model => model.DeviceUser, new { #Value = ViewBag.id})