3

In an mvc 3 .Net FW 4.0 project I have a data that relates in a parent child relationship, I've built up my model to contain a list of "children" with each parent record, and displaying it as in the following example:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/ViewMasterPage.Master" Inherits="System.Web.Mvc.ViewPage<MvcTest.Models.ModelList>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    ShowPropReturn
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<h2>ShowPropReturn</h2>

<script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>

<% using (Html.BeginForm()) { %>
    <%: Html.ValidationSummary(true) %>
    <fieldset>
        <legend>ModelList</legend>

        <div class="editor-label">
            <%: Html.LabelFor(model => model.MyProperty1) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(model => model.MyProperty1) %>
            <%: Html.ValidationMessageFor(model => model.MyProperty1) %>
        </div>

        <div class="editor-label">
            <%: Html.LabelFor(model => model.MyProperty2) %>
        </div>
        <div class="editor-field">
            <%: Html.EditorFor(model => model.MyProperty2) %>
            <%: Html.ValidationMessageFor(model => model.MyProperty2) %>
        </div>

        <table>
        <% foreach (var item in Model.MyProperty3)
           { %>
                <tr>
                    <td>
                        <%: Html.TextBoxFor(i => item.string1) %>
                    </td>
                    <td>
                        <%: Html.TextBoxFor(i => item.string2) %>
                    </td>
                </tr>

        <% } %>
        </table>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
<% } %>

<div>
    <%: Html.ActionLink("Back to List", "Index") %>
</div>

</asp:Content>

I figured out that if a displayfor is used to only display a field value, it does not get posted back which makes sense. But I'm getting a null for the child list object in the model on httppost and I need to have these items edited on the same view, it is also null in the formcollection object, can anyone help me on this please?

1
  • See Darin's answer for the solution. But why isn't your approach working? The lambda passed to EditorFor needs to select a property or field of the model type of the page/control. It will not be passed some other thing in scope: item is never passed, so how can it be used inside the implementation of EditorFor? Commented May 5, 2011 at 10:05

3 Answers 3

3

Instead of the foreach loop try using an editor template:

<table>
    <%= Html.EditorFor(x => x.MyProperty3)
</table>

and inside the corresponding editor template (~/Views/Shared/EditorTemplates/SomeModelType.ascx):

<%@ Control Language="C#" 
    Inherits="System.Web.Mvc.ViewUserControl<AppName.SomeModelType>" 
%>
<tr>
    <td>
        <%= Html.TextBoxFor(x => x.string1) %>
    </td>
    <td>
        <%= Html.TextBoxFor(x => x.string2) %>
    </td>
</tr>

Now this will generate proper input field names so that when you submit the form values will be correctly bound.

Remark: In this case I assume that MyProperty3 on your main view model is defined like this (a collection of SomeModelType):

public IEnumerable<SomeModelType> MyProperty3 { get; set; }
Sign up to request clarification or add additional context in comments.

Comments

1

Erika - I was able to make that work by using for loop

for (int i = 0; i < Model.MyList.Count(); i++) {
    @Html.TextboxFor(m => m.MyList[i].someproperty)
}

However, I like Darin's solution better.

Also I discovered that binding will not work if you set the Name property of a helper

Comments

1

Darin's answer is correct, but there is a caveat: you need to have a field in your form for each "set" field in the model, otherwise it isn't a match and the resulting List of model is null.

Consider this case for a List of AccountModel:

public class AccountModel
{
    public AccountType Type { get; set; }
    public bool Selected { get; set; }
    public string Name { get { return Util.GetAccountTypeName(Type); } }
}

The parent view need only contain ...

    @Html.EditorFor(model => model.Accounts)

... where ...

    List<AccountModel> Accounts { get; set; }

... is part of your overall ViewModel.

Now, if you create your editor template like this ...

@model WebLinx.Models.AccountModel

    <div class="informationRow">        
        @Html.CheckBoxFor(x => x.Selected) 
        <div class="formLabel">
            @Html.Label(Model.Name)
        </div>
    </div>

... then you will get a null object back in your parent model when you submit, even though the list of accounts with checkboxes view appears complete when displayed.

But if you include one extra line:

@model WebLinx.Models.AccountModel

    <div class="informationRow">        
        @Html.CheckBoxFor(x => x.Selected) 
        @Html.HiddenFor( x=> x.Type)  @* extra line with hidden field *@
        <div class="formLabel">
        @Html.Label(Model.Name)
        </div>
    </div>

then you have a complete match between the form and the AccountModel object, and the List of AccountModel will contain all the members (and updated values for the checkboxes).

1 Comment

Great explanation. Adding all the fields this way helped me solve the problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.