views:

154

answers:

1

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; }
            }
        }
+1  A: 

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.

Alexander Prokofyev
you really did your work here! i'll try it out :)
Stefanvds
awesome, it works perfect. brilliant idea on making the selectlist right in the spot where it is needed, and thus having the 'to be selected' item at hand. my personal workaround was making an array of selectlists with each its selected item set. but it was a lot of job, compared to this. bounty earned! :) still i hope they will fix this in MVC3
Stefanvds