MVC4: nested partial-view loses model data

2019-07-20 00:49发布

问题:

In an MVC4 project im using a partial View thats using a ViewModel and has a GET Form on it. In the Controller action i expect the ViewModel object with some data. When this partial is placed on a normal (.cshtml) View, i recieve the data via the expected ViewModel object in the Controller action. But, When this partial is placed on another partial view, for some reason the ViewModel object in the controller action is empty. When i step through the creation of the HttpGet-form, the passed-in model is not empty, but when the GET controller action is called, the model is.

Anyone know the reason for this? Is this some MVC behavior i don't know about maybe? Thanks!

The Partial:

@model ViewModel
    @if (Model != null)
    {
        using (Html.BeginForm("DoSomething", "Home", FormMethod.Get))
        { 
            @Html.HiddenFor(m => m.Object)            
            <input id="btnSend" type="submit"> 
        }
    }

The Other Partial:

@using OtherViewModel.ViewModel
@Html.Partial("The Partial", Model.ViewModel, ViewData)

The View:

@Html.Partial("_TheOtherPartial", Model.OtherViewModel, new ViewDataDictionary(ViewData) {
                TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "prefix" }
    })

The Controller

[HttpGet]
[AllowAnonymous]
public ActionResult DoSomething(ViewModel data)
{
}

回答1:

There ore two things you should take under consideration here. The first is try not to put forms inside partial views. If you do that, you might end up with nested forms, which are not supported by the browsers because it's not valid HTML:

<!ELEMENT FORM - - (%block;|SCRIPT)+ -(FORM) -- interactive form -->

-(FORM) construction disallows a nested form.

The second thing is instead of partial views I'd advice you to use editor templates. Take a look here how to use them. Next, as I said before, try to keep the forms outside of your editor templates - use the form in your main view and let editor templates to render distinct parts of your page. It will be less confusing and results in cleaner code.

In your specific example it can look like this:

The main view:

@model MainModel
@using (Html.BeginForm("YourAction", "YourController"))
{ 
    @Html.EditorFor(m => m.OtherViewModel)
    <input id="btnSend" type="submit"> 
}

OtherViewModel.cshtml editor template:

@model OtherViewModel
@Html.EditorFor(m => m.ViewModel)

ViewModel.cshtml editor template:

@model ViewModel
@Html.EditorFor(m => m.Object)

The main controller:

public ActionResult YourAction(MainModel data)
{
    ...

Models:

public class MainModel
{
    public MainModel() { OtherViewModel = new OtherViewModel(); }
    public OtherViewModel OtherViewModel { get; set; }        
}

public class OtherViewModel
{
    public OtherViewModel() { ViewModel = new ViewModel(); }
    public ViewModel ViewModel { get; set; }
}

public class ViewModel
{
    public string Object { get; set; }
}

Notice that the templates names reflect model types names. Next, put your templates under this ~/Views/Shared/EditorTemplates/ or this ~/Views/YourController/EditorTemplates/ directory.



回答2:

Or, you could pass same model as original obtained in "The View" to the "The Other Partial" and again to the "The Partial", while using required portion only corresponding view.