I have shared the view and the viewmodel below. I populated an indented select list (with optgroup
) by using foreach
loop instead of using some @Html
helper. However, I do not know how to assign the selected value from the list to a specific property of the CreateMainEntryViewModel
. More specifically, I need to set DiscussionWallId
, and TimeFrameId
property values based on the selection made in the dropdownlist when the form is submitted with. In this task I am using Ajax.BeginForm()
:
@model EcholuMvc.Models.CreateMainEntryViewModel
@using (Ajax.BeginForm("CreateMainEntry", "MainEntry", new AjaxOptions
{
UpdateTargetId = "div_MainEntries",
InsertionMode = InsertionMode.Replace
}))
{
@Html.TextBoxFor(m => m.Title, new { placeholder = "Write a brief title...", @class = "form-control input-lg" })
@Html.TextAreaFor(m => m.Content, new { placeholder = "Type your entry here...", @class = "form-control mainEntryField" })
<label for="drp_DiscussionWallListForNewMainEntry" style="text-align:right" class="col-sm-3 control-label">Choose the Discussion Wall:</label>
<select id="drp_DWListForNewMainEntry" class="form-control">
@{
foreach (DiscussionWall dw in Model.DiscussionWalls)
{
<optgroup label="@dw.WallTitle">
foreach (TimeFrame tf in dw.TimeFrames)
{
<option data-dw="@dw.WallId" data-tf="@tf.TimeFrameId">@tf.TimeFrameName</option>
}
</optgroup>
}
}
</select>
<button type="submit" id="btn_SubmitNewPost">Post</button>
}
This is the viewmodel:
public class CreateMainEntryViewModel
{
public string Title { get; set; }
public string Content { get; set; }
public Boolean IsAnonymous { get; set; }
public int DiscussionWallId { get; set; }
public int TimeFrameId { get; set; }
public CreateMainEntryViewModel(List<DiscussionWall> walls)
{
DiscussionWalls = walls;
}
public List< DiscussionWall> DiscussionWalls { get; set; }
}
I appreciate any help!
To directly answer your question, you need to include a hidden input for the DiscussionWallId
property, add a name
attribute for the <select>
element and value
attributes for its <option>
elements, and use javascript/jquery to update the value of the hidden input based on the selected option.
@Html.HiddenFor(m => m.DiscussionWallId) // add
<select name="TimeFrameId" id="TimeFrameId" class="form-control"> // add name attribute
@{
foreach (DiscussionWall dw in Model.DiscussionWalls)
{
<optgroup label="@dw.WallTitle">
foreach (TimeFrame tf in dw.TimeFrames)
{
<option data-dw="@dw.WallId" value="@tf.TimeFrameId">@tf.TimeFrameName</option> // modify
}
</optgroup>
}
</select>
and to update the value of the hidden input
$('#TimeFrameId').change(function() {
var dw = $(this).children('option:selected).data('dw');
$('#DiscussionWallId').val(dw);
});
Both the DiscussionWallId
and TimeFrameId
properties will now be bound when you submit the form.
However adding the hidden input (and your data-dw
attributes) is unnecessary (and a malicious user could easily change the value of the hidden input, potentially causing your app to fail). If for any reason you need the DiscussionWallId
property associated with the selected TimeFrame
, then you should be getting that value from the repository based on the value of TimeFrameId
.
There are also many other reasons why this is a poor design including you are not getting strongly typed 2-way model binding and you cannot use the built in client side validation features.
If you using MVC-5.2, then you can use the Group
property of SelectListItem, or the overloads of SelectList that accepts a dataGroupField
value to generate an IEnumerable<SelectListItem>
that supports grouped options in the view and then simply use the strongly typed @Html.DropDownListFor()
helper. However this is really only suitable if you have a few DiscussionWalls
objects, and each one contains only a few TimeFrame
objects. If not, then you should be implementing cascading dropdownlists. For an example, refer this answer and this DotNetFiddle.