4

Given a Model

public class Task
{
public int TaskId { get; set; }
public string Title { get; set; }
public ICollection<SomeData> Information { get; set; }
}

where

public class SomeData
{
    public int SomeDataId { get; set; }
    public string Description { get; set; }
}

I have a view

@model myProject.Models.Task

<div>
    @Html.LabelFor(model => model.Title)
</div>

<table>
@Html.Partial("_InformationEdit", Model.Information.ToList(), new ViewDataDictionary(Html.ViewDataContainer.ViewData) {
                TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "Information" }})
</table>                    

and my partial is

@model IList<myProject.Models.SomeData>

@for (int i = 0; i < Model.Count(); i++)
{
    <tr>
        <td>
            @Html.EditorFor(modelItem => Model[i].Description)
        </td>
    </tr>
 }

However

My Html fields are being rendered like

<input class="text-box single-line" id="Information__0__Description" name="Information.[0].Description" type="text">

Where the names should be Information[0].Description. It's got an additional dot in there, so is not being bound back to the model correctly when posted. How can I fix this?

As per Model binding to a list I can see what my Id's are supposed to be, but I just can't figure out the correct syntax.

Also, is there a more elegant way to achieve this with an IEnumerable using a @foreach ?

Related:

ASP.Net MVC4 bind a "create view" to a model that contains List

ASP.NET MVC model binding an IList<> parameter

Community
  • 1
  • 1
RYFN
  • 2,939
  • 1
  • 29
  • 40

3 Answers3

1

You could use the <input... directly like this:

Page:

<table>
    @Html.Partial("_InformationEdit", Model.Information)
</table>

Partial Page:

@for (int i = 0; i < Model.Count(); i++)
{
    <tr>
        <td>
            <input class="text-box single-line" id="Information[@i]Description" name="Information[@i].Description" type="text" value="@Model[i].Description" />
        </td>
    </tr>
}

Or, to be able to pass the prefix as in your example you could keep the Page code the same and change your partial like:

Page:

<table>        
    @Html.Partial("_InformationEdit", Model.Information, 
        new ViewDataDictionary(Html.ViewDataContainer.ViewData) 
        {
            TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "Information" }
        })
</table>

Partial Page:

@for (int i = 0; i < Model.Count(); i++)
{
    <tr>
        <td>
            @{
                string fieldName = string.Format("{0}[{1}].Description", ViewData.TemplateInfo.HtmlFieldPrefix, i);
                <input class="text-box single-line" id="@fieldName" name="@fieldName" type="text" value="@Model[i].Description" />
            }
        </td>
    </tr>
}
Belogix
  • 8,129
  • 1
  • 27
  • 32
  • Thanks for this, but how about an extended case where there might be validation on the model (which would get automatically created by EditorFor) ? – RYFN Feb 07 '13 at 15:00
1

Changing my partial to

@model IList<myProject.Models.SomeData>

@{
    var Information = Model;
}

@for (int i = 0; i < Information.Count(); i++)
{
    <tr>
        <td>
            @Html.EditorFor(modelItem => Information[i].Description)
        </td>
    </tr>
 }

Works, but this seems a bit odd!

I guess ensuring that the object being bound is of the same name as the property it needs to be bound to does some wizardry... Other suggestions or explanations are welcome!

RYFN
  • 2,939
  • 1
  • 29
  • 40
1

your partial should be:

@model IList<myProject.Models.SomeData>

@for (int i = 0; i < Model.Count(); i++)
{
  <tr>
    <td>
      @Html.EditorFor(model => model[i].Description)
    </td>
  </tr>
}

also it's easier to read if you replace i by the item name

Neo Roy
  • 11
  • 3
  • It is not the same as the question (though I don't know if it is a solution) as in the problem the Model is accessed directly, however here it is only accessed via the lambda parameter – yoel halb Jun 10 '14 at 17:33