Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DropDownListFor not binding on Edit View with repeating items (List<T>)

Here is the thing. I have an Edit view, which doesnt bind the dropdowns' value when I open it.

    [NonAction]
    public List<SelectListItem> VraagType() {
        List<SelectListItem> l = new List<SelectListItem>();
        SelectListItem a = new SelectListItem();
        SelectListItem b = new SelectListItem();

        a.Text = "Meerkeuze";
        a.Value = "M";

        b.Text = "Open";
        b.Value = "O";

        l.Add(a);
        l.Add(b);

        return l;
    }

    [NonAction]
    public List<SelectListItem> getSchalen() {
        return _db.EvalSchaals.ToList().ToSelectList(q => q.Sch_Naam, q => q.Sch_ID.ToString(), q => q.Sch_ID == -1).ToList();
    }

    public ActionResult Edit(int id) {
        ViewData["vraagtype"] = VraagType();
        ViewData["schaal"] = getSchalen();

        EvalVragenBlok evb = _db.EvalVragenBloks.First(q => q.Vrbl_ID == id);
        List<EvalVragen> ev = _db.EvalVragens.Where(q => q.Vrbl_ID == id).ToList();

        FlatEvalVragenBlok fevb = Mapper.Map<EvalVragenBlok, FlatEvalVragenBlok>(evb);
        fevb.Vragen = new List<FlatEvalVragen>();

        return View(fevb);
    }

this is the code from the controller.

here is the code from the Edit.aspx view

        <h2>
            Edit</h2>
        <% using (Html.BeginForm()) {%>
        <%: Html.ValidationSummary(true) %>
        <fieldset>
            <legend>Fields</legend>
    <legend>Fields</legend>
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Vrbl_Titel) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Vrbl_Titel) %>
                <%: Html.ValidationMessageFor(model => model.Vrbl_Titel) %>
            </div>
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Sch_ID) %>
            </div>
            <div class="editor-field">
                <%: Html.DropDownListFor(model => model.Sch_ID, ViewData["schaal"] as List<SelectListItem>, "Selecteer een schaal...") %>
                <%: Html.ValidationMessageFor(model => model.Sch_ID) %>
            </div>
            <%= Html.ValidationMessageFor(model => model.Vragen) %>
            <table id="vragentbl">
                <tr>
                    <th>
                    </th>
                    <th>
                        Vraag
                    </th>
                    <th>
                        Soort
                    </th>
                </tr>
                <% if (Model.Vragen != null) { %>
                <% for (int i = 0; i < Model.Vragen.Count; i++) {  %>
                <tr>
                    <td>
                        <%=i + 1%>
                    </td>
                    <td>
                        <%= Html.TextBoxFor(model => model.Vragen[i].Evvr_Vraag, new { style = "width:400px" })%><br />
                        <%= Html.ValidationMessageFor(model => model.Vragen[i].Evvr_Vraag)%>
                    </td>
                    <td>
                        <%= Html.DropDownListFor(model => model.Vragen[i].Evvr_Type, ViewData["vraagtype"] as List<SelectListItem>, new { style = "width:95px" })%><br />
                        <%= Html.ValidationMessageFor(model => model.Vragen[i].Evvr_Type)%>
                    </td>
                </tr>
                <% }
                   } %>
                <tr>
                    <td>
                    </td>
                    <td>
                        <a id="addnew" href="#">Voeg extra keuze toe</a>
                    </td>
                    <td>
                    </td>
                </tr>
            </table>
            <p>
                <input type="submit" value="Save" />
            </p>
        </fieldset>
        <% } %>

I have 2 List 's. 1 of them is in the non-repeating part of the form (Schalen), the other one (VraagType) is Inside the repeating part.

for Schalen, everything works fine. i open the edit view, and all fields are filled in like it should be. the Vrbl_Titel has its value, and the dropdown of Sch_ID has the value it received from the object which i sent with the view, which came from the DB.

The problem lies in the repeating part. the textbox for model.Vragen[i].Evvr_Vraag get's its value, and the dropdown for model.Vragen[i].Evvr_Type is shown, however, this dropdown does not get the value which was sent in the object. it keeps it's default standard value, which is the first item in the 'selectlist'

how do i get my value from my 'Vragen' object, into the dropdown. if i put the value in a simple textbox

<%= Html.TextBoxFor(model => model.Vragen[i].Evvr_Type)%>

then the textbox does get the value. so the problem is that the dropdownvalue doesnt change form it's initial value... bug in MVC?

just for info, this is how the object(s) look sent to the view:

        namespace MVC2_NASTEST.Models {

            public partial class FlatEvalVragenBlok {
                public int Vrbl_ID { get; set; }
                public int Sch_ID { get; set; }
                public string Vrbl_Titel { get; set; }
                public List<FlatEvalVragen> Vragen { get; set; }
            }
        }

        namespace MVC2_NASTEST.Models {

            public partial class FlatEvalVragen {
                public int Evvr_ID { get; set; }
                public int Vrbl_ID { get; set; }
                public int Evvr_rang { get; set; }
                public string Evvr_Vraag { get; set; }
                public char Evvr_Type { get; set; }
            }
        }
like image 623
Stefanvds Avatar asked Aug 17 '10 10:08

Stefanvds


1 Answers

It seems this is really a bug or at least inconsistency in ASP.NET MVC 2. I have examined its source and found what InputHelper() method called from TextBoxFor() helper receives default value calculated with

ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model

But SelectInternal() method called from DropDownListFor() helper receives only a name of a control found with ExpressionHelper.GetExpressionText() method.

So SelectInternal() tries to find default value using ViewData.Eval() method from MVC 1. It's known what this method isn't able to extract values from arrays by numeric index.

So in your case are applicable

<%: Html.DropDownListFor(model => model.Sch_ID) %>
<%= Html.TextBoxFor(model => model.Vragen[i].Evvr_Type)%>

but not

<%: Html.DropDownListFor(model => model.Vragen[i].Evvr_Type) %>

because it's equivalent to

<%: Html.DropDownList("Vragen[" + i + "].Evvr_Type") %>

At the same time I want to emphasize again what

<%= Html.TextBoxFor(model => model.Vragen[i].Evvr_Type)%>

isn't equivalent to

<%= Html.TextBox("model.Vragen[" + i + "].Evvr_Type")%>

because latter even in MVC 2 can't bind default value.

Possible workarounds

First. Since SelectInternal() also checks ModelState dictionary you can fill this dictionary before returning the view.

for (int i=0; i < fevb.Vragen.Count(); i++)
    ModelState.Add("Vragen[" + i + "].Evvr_Type", new ModelState
    {
        Value = new ValueProviderResult(fevb.Vragen[i].Evvr_Type, null, 
            CultureInfo.CurrentCulture) 
    });

This will be done by MVC itself after from post, so you should do it manually only first time.

Second. Instead of

<%= Html.DropDownListFor(model => model.Vragen[i].Evvr_Type, 
    ViewData["vraagtype"] as List<SelectListItem>)%>

use

<%= Html.DropDownListFor(model => model.Vragen[i].Evvr_Type, 
    new SelectList(ViewData["vraagtype"] as IEnumerable, "Value", "Text",
        Model.Vragen[i].Evvr_Type))%>

ViewData["vraagtype"] in this case doesn't have to contain objects of SelectListItem, any IEnumerable is enough. You may check SelectList() method description in case of need.

like image 95
Alexander Prokofyev Avatar answered Oct 18 '22 16:10

Alexander Prokofyev