Reputation: 2309
I am using the solution suggested in Mel's space (https://mleeb.wordpress.com/2013/11/23/editing-nested-lists-in-asp-mvc-4/) for editing nested list
Basically I have my model as below
ProductEditModel
--> ProductAudioEditModel
--> ProductAssetResourceEditModel
I got this working for the below
@Html.EditorFor(c => resource.TrackTitle, null, Html.GetHtmlName("TrackTitle"))
which gives me the correct value when it's edited.
However, I couldnt get this working for DropDownList or I am not able to pick the edited value in the dropdownlist . It always give me the original value in my controller.
@using (Html.BeginCollectionItem("ProductAssetAudios", out parentIdentifier))
{
.....
@foreach (var resource in Model.ProductAssetResources.OrderBy(a => a.ResourceNumber))
{
string childIdentifier = null;
@using (Html.BeginChildCollectionItem("ProductAssetResources", parentIdentifier, out childIdentifier))
{
@Html.HiddenFor(model => resource.AssetResourceStatusId, new { Name = Html.GetHtmlName(childIdentifier, "AssetResourceStatusId") })
@Html.DropDownListFor(model => resource.AssetResourceStatusId, new SelectList(visibleResourceStatuses, "AssetResourceStatusId", "Name", resource.AssetResourceStatusId), new { @class = "inherit-title" }) @Html.ValidationMessageFor(model => resource.AssetResourceStatusId)
}
}
}
The AssetResourceStatusId always holding the original value even though the drop down list is selected for a different value.
I was hoping that the EditorFor and DropDownListFor should be work in the same manner when editing nested list.
Generated HTML
<select class="inherit-title valid" id="ProductAssetAudios_0df86a5c-0a32-4b0f-97ee-3b3254f743d9__ProductAssetResources_c58ba43c-6081-41d4-88fd-d59799c7374e__resource_AssetResourceStatusId" name="ProductAssetAudios[0df86a5c-0a32-4b0f-97ee-3b3254f743d9].ProductAssetResources[c58ba43c-6081-41d4-88fd-d59799c7374e].resource.AssetResourceStatusId" aria-invalid="false"><option value="3">Extra</option>
<option selected="selected" value="2">Found</option>
<option value="8">Ignore</option>
</select>
<input name="ProductAssetAudios[b5670a6a-7a1d-4c76-86bc-85a05cd144c1].ProductAssetResources[aa378d38-0fb7-4304-9f24-79d0efcb36b9].AssetResourceStatusId" data-val="true" data-val-number="The field AssetResourceStatusId must be a number." data-val-required="The AssetResourceStatusId field is required." id="ProductAssetAudios_b5670a6a-7a1d-4c76-86bc-85a05cd144c1__ProductAssetResources_aa378d38-0fb7-4304-9f24-79d0efcb36b9__resource_AssetResourceStatusId" type="hidden" value="2">
-Alan-
Upvotes: 2
Views: 4892
Reputation:
You model contains a collection property named ProductAssetAudios
(typeof ProductAudioEditModel
) and each object in that collection contains a collection property named ProductAssetResources
(typeof ProductAssetResourceEditModel
) and each of those objects contains a property named AssetResourceStatusId
.
In C# code, if you were to get the AssetResourceStatusId
value of the 1st ProductAssetResourceEditModel
in the 1st ProductAudioEditModel
, your code would be
var id = model.ProductAssetAudios[0].ProductAssetResources[0].AssetResourceStatusId;
Drop the model
prefix and that is exactly how the name
attribute of the control must be. What the BeginCollectionItem()
and BeginChildCollectionItem()
methods do is to modify the collection indexers to a Guid
and adds a hidden input for the indexer to allow you to dynamically add and remove items from the collection. By default, the DefaultModelBinder
will bind collections with zero-based consecutive indexers, unless a value for the indexers is also posted (i.e the reason why the hidden input is added).
In your case, the name
attribute for the hidden input is correct, i.e. using
@Html.HiddenFor(model => resource.AssetResourceStatusId, new { Name = Html.GetHtmlName(childIdentifier, "AssetResourceStatusId") })
because your overriding the default name
attribute generated by HiddenFor()
. You just need to do the same for the DropDownListFor()
method, i.e. set the name attribute using new { Name = Html.GetHtmlName(childIdentifier, "AssetResourceStatusId") }
. But then you also need to then delete the hidden input because the DefaultModelBinder
will only bind the first value that is posted for a property. Note also that you will need to change the ValidationMessageFor()
also.
Side note. From the comments it appears that you are not wanting to add and remove items in the view, in which case, do not use the BeginCollectionItem()
and BeginChildCollectionItem()
methods. Instead, just use nested for
loops or custom EditorTemplates
for typeof ProductAudioEditModel
and ProductAssetResourceEditModel
. An example of using for
loops would be
for(int i = 0; i < Model.ProductAssetAudios.Count; i++)
{
@Html.TextBoxFor(m => m.ProductAssetAudios[i].SomeProperty)
....
for (int j = 0; j < Model.ProductAssetAudios[i].ProductAssetResources.Count; j++)
{
@Html.DropDownListFor(m => m.ProductAssetAudios[i].ProductAssetResources[j].AssetResourceStatusId, new SelectList(.....)
Refer also this answer for an example of using a nested EditorTemplate
.
Upvotes: 1