3

In RoR, you can change the form context so that it properly builds the form element ids and names (e.g. simple_fields_for myModel do |innerModel|, or similar). I'm wondering what's the correct way to do this w/MVC5. For example...

Models

class Model1 {
    public Model2 M2 { get; set; }
}

class Model2 {
    public List<Model3> M3s { get; set; }
}

class Model3 {
    public string Name { get; set; }
}

Controller

class MyController {
    public ActionResult Test(Model1 model) {
        View(model);
    }
}

View

@model Model1

@using (MvcForm form = Html.BeginForm()) {
    Html.RenderPartial("_Model2", this.Model.M2);
}

If 3 Model3 instances are posted back, the model state should be similar to:

M2.M3s[0].Name=Name1
M2.M3s[1].Name=Name2
M2.M3s[2].Name=Name3

In this case, the view is using a partial to render Model2, but the partial doesn't know that it's in the context of this.Model.M2 and therefore does not prefix the ids and names generated with Model2. -- it simply starts them with Model3. The solution would be to use a new form context (not literally meaning FormContext), something like this:

@using (MvcForm form = Html.BeginForm()) {
    using (form.For(m => m.M2)) {
        Html.RenderPartial("_Model2", this.Model.M2);
    }
}

Note that form.For does not exist, but the intention is that it changes the current ModelMetadata to have the appropriate context and prefix of M2. Thus, anything rendered within that block would inherit that new context and the ids and names would be correctly generated.

Mathew Thompson
  • 55,877
  • 15
  • 127
  • 148
Josh M.
  • 26,437
  • 24
  • 119
  • 200
  • Far easier is you use `EditorTemplate`'s rather than partials which will correctly add the prefixes (rename you partials to `/Views/Shared/EditorTemplates/Model2.cshtml` (to match the type) and then in the view use `@Html.EditorFor(m => m.M2)` –  Aug 21 '15 at 23:46

1 Answers1

2

You can actually specify the prefix when you call RenderPartial, then the form values of your partial view fields will automatically receive that prefix:

Html.RenderPartial("_Model2", this.Model.M2, new ViewDataDictionary
{
    TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "M2" }
})
Mathew Thompson
  • 55,877
  • 15
  • 127
  • 148
  • Thanks, I did find this answer (of course after I typed all of this, even though I tried to find the answer before) which suggests a similar method: http://stackoverflow.com/questions/1488890/asp-net-mvc-partial-views-input-name-prefixes I'll write up a helper to make this more straightforward and seamless! – Josh M. Aug 21 '15 at 14:52
  • @JoshM. No problems, yeah that's pretty handy to wrap it in a HTML helper method actually. – Mathew Thompson Aug 21 '15 at 14:54
  • 1
    This works well, but is definitely cumbersome. Gets me moving forward for now, thanks! I'm surprised there's no built-in helper to handle this situation, seems it is pretty common as I run into it a lot. Another, more annoying, way to get around this is to have an editor template/partial for each "step" in the model chain. But that is such overkill. – Josh M. Aug 21 '15 at 15:07
  • @JoshM. Yeah it's still a bit of a shortcoming in the framework. Another alternative is to just pass the parent model into each of the partials, which isn't ideal either. – Mathew Thompson Aug 22 '15 at 14:38