45

I have 2 models in my sample MVC 3 application, SimpleModel and ComplexModel, shown below:

public class SimpleModel
{
    public string Status { get; set; }
}

public class ComplexModel
{
    public ComplexModel()
    {
        Simple = new SimpleModel();
    }

    public SimpleModel Simple{ get; set; }
}

I have defined views for this models:

_SimplePartial.cshtml:

@model SimpleModel

@Html.LabelFor(model => model.Status)
@Html.EditorFor(model => model.Status)

and Complex.cshtml:

@model ComplexModel

@using (Html.BeginForm()) {

    @Html.Partial("_SimplePartial", Model.Simple)
    <input type="submit" value="Save" />
}

After submitting form, with random value entered in Status field, the value is not binded to my model. The Status field is NULL when I'm checking the model in my controller action:

[HttpPost]
public ActionResult Complex(ComplexModel model)
{
    // model.Simple.Status is NULL, why ?
}

Why is it not binded ? I don't want to inherit models. Do I have to write my custom model binders for such simple case ?

Regards.

jwaliszko
  • 16,942
  • 22
  • 92
  • 158

2 Answers2

62

Instead of:

@Html.Partial("_SimplePartial", Model.Simple)

I would recommend you using Editor templates:

@model ComplexModel
@using (Html.BeginForm()) 
{
    @Html.EditorFor(x => x.Simple)
    <input type="submit" value="Save" />
}

and then put the simple partial inside ~/Views/Shared/EditorTemplates/SimpleModel.cshtml or inside ~/Views/Home/EditorTemplates/SimpleModel.cshtml where Home is the name of your controller:

@model SimpleModel
@Html.LabelFor(model => model.Status)
@Html.EditorFor(model => model.Status)

Of course if you prefer to have the partial in some special location and not follow the conventions (why would you?) you could specify the location:

@Html.EditorFor(x => x.Simple, "~/Views/SomeUnexpectedLocation/_SimplePartial.cshtml")

then everything will come into place as expected.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    Seems ok but there is one drawback. The id attribute of Status field has changed from 'Status' to 'Simple_Status'. Because of this my javascript stops working. Is there any way to tell MVC not to change the default id's of elements ? Since I'm not going to include multiple views using the same model, the id's will be always unique. Still, mabye more correct way is to just fix js ? – jwaliszko Mar 04 '11 at 20:27
  • 1
    The answer is helpful. but @Html.EditorFor(x => x.Simple) will display all the properties of SimpleModel Class. But if I want to display only few properties, how could I achieve it? – Ovini Feb 15 '12 at 16:33
  • this is very old but someone else might need it... to have an EditorFor view how you want it, follow these instructions: http://lostechies.com/jimmybogard/2011/09/07/building-forms-for-deep-view-model-graphs-in-asp-net-mvc/ – Verena Haunschmid Jan 14 '14 at 11:32
  • 1
    ok, but if I want to add a new item to collection?? I need a javascript when I click the button to add new item. Which one is the best approach? – Leandro De Mello Fagundes Jan 20 '14 at 13:22
  • 1
    @LeandroDeMelloFagundes if you want a dynamic list on the client to edit. Then check Knockout or Angular. Those are client MVC frameworks. You can then post the whole object with dynamic list to the server. – LockTar Jul 16 '14 at 13:49
  • Brilliant answer, was about to get really frustrated with this!! Many thanks! – Graham Whitehouse Jul 22 '14 at 12:49
  • 1
    It seems the path to "SomeUnexpectedLocation" of this questions ain't supported by EditorFor control. – jpsimard-nyx Mar 09 '16 at 23:37
  • @lucian.jp MVC 5 and ASP.NET Core are hardcoded to search in the `EditorTemplates` directory, so it's no longer possible to place your partials elsewhere. If that's an issue, @toralux's answer below is the only option. – Ian Kemp Dec 29 '16 at 10:15
26

As Daniel Hall suggests in his blog, pass a ViewDataDictionary with a TemplateInfo where HtmlFieldPrefix is set to the name of the SimpleModel-property:

 @Html.Partial("_SimplePartial", Model.Simple, new ViewDataDictionary(ViewData)
    {
        TemplateInfo = new System.Web.Mvc.TemplateInfo
        {
            HtmlFieldPrefix = "Simple"
        }
    })
Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
toralux
  • 518
  • 4
  • 6
  • 1
    public static ViewDataDictionary WithFieldPrefix(this ViewDataDictionary viewData, string fieldPrefix) { return new ViewDataDictionary(viewData) { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = fieldPrefix } }; } – James White Feb 05 '15 at 20:25
  • 1
    I like this answer. because most of the time I need to use Partial in complex models. – kartal Sep 14 '15 at 17:12
  • 1
    This is the one to use! – EthR Oct 06 '15 at 14:44