2

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.

Edited

Generated HTML

DropDownListFor

    <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>

HiddenFor

<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-

Alan B
  • 2,219
  • 4
  • 32
  • 62
  • Show the actual html generated by both the `HiddenFor()` and `DropDownListFor()` methods. –  May 29 '16 at 04:00
  • @StephenMuecke, I have edited my question to include the generated HTMLs – Alan B May 29 '16 at 09:49
  • Use a `for-loop` to generate the html and it should work – brroshan May 29 '16 at 09:50
  • The hidden input is creating the correct `name` attribute assuming `ProductEditModel` contains a property `IEnumerable ProductAssetAudios` and `ProductAudioEditModel` contains a property `IEnumerable ProductAssetAudios`. You need to generate the dropdownlist the same way as you currently generate the hidden input and then delete the hidden input –  May 29 '16 at 10:02
  • I also assume your are dynamically add new items to the nested collection in the view, otherwise you should not be using this. –  May 29 '16 at 10:04
  • @StephenMuecke, your suggestion works very well. Can you answer this question , so that I can mark it as an answer? – Alan B Jun 03 '16 at 02:35
  • @StephenMuecke, what would be your suggestion if I dont intend to dynamically add new items to the collection in view but just updating the existing items? – Alan B Jun 03 '16 at 02:37
  • @AlanB, If your not dynamically adding and deleting items in the view, then just use a `for` loop or `EditorTemplate` as per [this answer](http://stackoverflow.com/questions/30094047/html-table-to-ado-net-datatable/30094943#30094943). Will add an answer in an hour or so. –  Jun 03 '16 at 03:01

1 Answers1

1

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.

Community
  • 1
  • 1