Using one Partial View Multiple times on the same Parent View

有些话、适合烂在心里 提交于 2019-12-04 05:40:37

Personally I prefer using editor templates, as they take care of this. For example you could have the following view model:

public class MyViewModel
{
    public ChildViewModel Child1 { get; set; }
    public ChildViewModel Child2 { get; set; }
}

public class ChildViewModel
{
    public string Foo { get; set; }
}

and the following controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel
        {
            Child1 = new ChildViewModel(),
            Child2 = new ChildViewModel(),
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        return View(model);
    }
}

and inside the Index.cshtml view:

@model MyViewModel
@using (Html.BeginForm())
{
    <h3>Child1</h3>
    @Html.EditorFor(x => x.Child1)

    <h3>Child2</h3>
    @Html.EditorFor(x => x.Child2)
    <input type="submit" value="OK" />
}

and the last part is the editor template (~/Views/Home/EditorTemplates/ChildViewModel.cshtml):

@model ChildViewModel

@Html.LabelFor(x => x.Foo)
@Html.EditorFor(x => x.Foo)

Using the EditorFor you can include the template for different properties of your main view model and correct names/ids will be generated. In addition to this you will get your view model properly populated in the POST action.

JotaBe

There is an alternative:

  1. Add a prefix to the PartialView
  2. Bind the model, removing the prefix

For 1, set the prefix in your View:

ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = "prefix";

For 2, you can recover the data with UpdateModel, like this:

UpdateModel(producto, "prefix");

This is not very advisable because your action doesn't receive the data as a parameter, but updates the model later. This has several inconvenients: 1) it's not clear what your action needs by looking at its signature 2) it's not easy to provide the input to the action for unit testing it 3) the action is vulnerable to overflow parameters (parameters provided by the user that shouldn't be there and are mapped to the model).

However, for 2 there is an alternative: register a custom Model Binder that allows you to do remove the prefix. And the custom Model Binder must know about it.

A good solution is in this SO Q&A: How to handle MVC model binding prefix with same form repeated for each row of a collection? But it has a little flaw: if you add a hidden field with the name "__prefix" in a partial view, and you render it several times as a partial view, this ID will be repeated for several different elements in the page, which is not allowed, and can provoke some trouble. And one of the most important reasons to provide a prefix is precisely rendering the same "edit" view as partial views for several instances of an entity. I.e. this would happen in a page like gmail, where you can edit several emails at once.

There are several possible solutions for this problem.

One of them is providing the prefix as a query string or routedata value, and not as a form field, which avoid the Id conflicts, and can be found by the model binder. (It can always have the same name).

Another solution is to use a hidden field, with a fixed pattern, but which is different for every rendered view. The prefix could follow this pattern for uniqueness: "PP$ActionControllerId" like "PP$EditProduct23", which is unique for each rendered view, and can be easily found between the request parameters looking for one that starts with "PP$".

And a final solution would be to create the prefix only in the view, and not providing it in any kind of request parameter. The Model binder would have to look for the prefix examining the names of the request parameters, until it finds one whose prefix follow the pattern.

Of course, the custom ModelBinder must be adapted to work tieh the chosen convention.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!