2

Is there any way to bind the value of a DropDownListFor in the context of a list of child models ?

Currently if I have a Parent model that has a list of child models that are looped over, the DropDownListFor HtmlHelper won't bind the value.

I created a case on dotnetfiddle to explain this : https://dotnetfiddle.net/RB4UVc

This is the code in the fiddle copied here:

Model.cs

using System;
using System.Web.Mvc;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace HelloWorldMvcApp
{
    public class Model
    {
        public int? SelectedValue { get; set; }

        public List<Value> Values { get; set; }

        public IEnumerable<SelectListItem> Fields
        {
            get
            {
                return new[]
                {
                    new SelectListItem { Text = "1", Value = "1" }, 
                    new SelectListItem { Text = "2", Value = "2" }, 
                    new SelectListItem { Text = "3", Value = "3" }
                };
            }
        }

        public Model()
        {
            Values = new List<Value>();
        }
    }

    public class Value
    {
        public int SelectedValue { get; set; }

        public string Name { get; set; }
    }
}

HomeController.cs

using System;
using System.Web.Mvc;
using System.Collections.Generic;

namespace HelloWorldMvcApp
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            var model = new Model();

            model.SelectedValue = 2;
            model.Values.Add(new Value { SelectedValue = 3, Name = "John Doe" });

            return View(model);
        }
    }
}

Index.cshtml

@model HelloWorldMvcApp.Model

<h1>Single</h1>
<p>Value: @Model.SelectedValue</p>

@Html.DropDownListFor(model => model.SelectedValue, Model.Fields)

<hr>

<h1>Collection</h1>

@foreach(var value in Model.Values)
{
    <p>Value: @value.SelectedValue</p>

    <p>Name: @Html.TextBoxFor(_ => value.Name)</p>

    @Html.DropDownListFor(_ => value.SelectedValue, Model.Fields)
}

<hr>

<h1>Indexed Collection</h1> 

@for(var i = 0; i < Model.Values.Count(); i++)
{
    <p>Value: @Model.Values[i].SelectedValue</p>

    <p>Name: @Html.TextBoxFor(model => model.Values[i].Name)</p>

    @Html.DropDownListFor(model => model.Values[i].SelectedValue, Model.Fields)
}

The first is using a normal value that binds correctly, the second would be a collection of child models. As we can see, the TextBoxFor is able to bind correctly the value.

Am I doing something wrong here or is there a workaround for this issue ? (beside building manually a <select> element)

Side note: I look specifically for this in a context of a loop considering I need to save an array of child models in a batch.

Erick
  • 5,969
  • 10
  • 42
  • 61
  • 1
    You need to include the code in your question (the link may go dead making the question useless and it was not even working when I visited it). The issue is explained in [this answer](http://stackoverflow.com/questions/37407811/mvc5-razor-html-dropdownlistfor-set-selected-when-value-is-in-array/37411482#37411482) –  Feb 02 '17 at 21:35
  • @StephenMuecke I edited with the code copied here. dotnetfiddle had the advantage of running it from the get-go but I see your point. Also, great answer you found there! It's hack-ish but could work. – Erick Feb 03 '17 at 14:10
  • 1
    Could work? It does, and its my answer :). And as a side note, the `foreach`loop option (in the question, not the fiddle) does not work when you submit - refer [this answer](http://stackoverflow.com/questions/30094047/html-table-to-ado-net-datatable/30094943#30094943) for an explanation –  Feb 03 '17 at 20:32

1 Answers1

0

Firstly, you do not need to manually increment a value in order to obtain the current index within your collection loop. You can write it as a 'for' loop instead of a 'foreach' like so:

@for(int i = 0; i < Model.Values.Count(); i++)
{
    <p>Value: @Model.Values[i].SelectedValue</p>

    <p>Name: @Html.TextBoxFor(model => model.Values[i].Name)</p>

    @Html.DropDownListFor(model => model.Values[i].SelectedValue, Model.Fields)
}

(Please note, I've not tested that code.)

Secondly, there is a 'Selected' boolean value in the SelectListItem object. Try setting that to true for the object you'd like to be selected upon page load.

jdurc
  • 76
  • 3
  • 11
  • Considering the `for` loop, inded I was aware of this indeed. As for the `DropDownListFor` whether it is inside of a for or a foreach there is no change in the binding. Also, the problem with the SelectedValue is if you have sub-models with 30 categories you have a total of 900 list element in memory. this increase significantly your memory footprint for such a trivial use. Although, for small usages this is very useful indeed. – Erick Feb 02 '17 at 17:10
  • @Erick Sorry, I'm not sure if I understand the issue; are you saying that you want to model bind the actual collection of items? – jdurc Feb 03 '17 at 11:21
  • you suggest to use the 'Selected' value of the SelectListItem. When you have multiple dropdowns with the same list you will bind all of the on the same list. Hence you can't use the 'Selected' or you need to reset that value before each dropdown. If you don't reset it the other way is to have a different copy for each dropdown. Hence, if you have 30 dropdown with a list of 30 choices, you have a total of 900 SelectListItem object in memory. It's big for nothing. – Erick Feb 03 '17 at 14:20
  • I get `'HtmlHelper' does not contain a definition for 'DropDownListFor' and the best extension method overload 'SelectExtensions.DropDownListFor(HtmlHelper, Expression>, IEnumerable)' requires a receiver of type 'HtmlHelper'` trying to use this. – vapcguy Oct 10 '18 at 21:39