What the controller should receive when I am sendi

2019-09-06 12:46发布

问题:

What should the controller should receive when I am sending a view that contains multiple partial view with different models.

I have a view that renders multiple/dynamic partial views (these views are selecting depending on the package that the user previously selected ), all of these partial views have different models and I need to submit them in one big "parent" form. My main concern is what should the controller receive? and how to handle all the information inside he form, since I have multiple and variable models (depending of what the user choose) that are being send from the form.

This is my my parent view, that renders all the partial views, depending of which package the user chose (i.e. The parent view can render from 1 to 7 forms. all of them have different models). IEnumerable string contains a list of strings what I will use to spit out a Dictionary with Views and Models to render them later in this view.

@using UI.Shared.Utils
@model IEnumerable<string>
@{
  var forms = CustomFormUtil.GetCustomMetaPartial(Model);       
}                    
@using (Html.BeginForm(MVC.Service.NewOrderRequest.Index(**I am confused what to pass here**), FormMethod.Post))
{
  foreach (var form in forms)
  {
    { Html.RenderPartial(form.View, form.Model); }
  }
  <p style="clear: both">
    <input id="submitNOR" type="submit" value="Submit" style="float: right" />
  </p>
}

So let's say the user fills 4 partial views, that means I have to send 4 different models to the controller so I can use the data filled by the user.

This is my controller:

[HttpPost]
[SaveModelState]
public virtual RedirectToRouteResult Index(**What should I receive**)
{
  if (!ModelState.IsValid)
  {
    ModelState.AddModelError("error", "Please fill out the required fields");
    return RedirectToAction(MVC.Service.NewOrderRequestForms.Index());
  }
  ...
}

My main concern is what should the controller receive? I have tried the following:

[HttpPost]
[SaveModelState]
public virtual RedirectToRouteResult Index(IEnumerable<ICustomModel> models)

but after some research this is not possible also I have tried to pass all the possible models to the controller

[HttpPost]
[SaveModelState]
public virtual RedirectToRouteResult Index(Model1 model1, ..., ModelN modeln)

This is also not working, and my latest attempt was to create a Master Model that contains all the possible models and pass it to the Controller, this is my Master Model

namespace UI.Next.Areas.Service.Models
{
  public class TdlMasterModel : ICustomMetaModel
  {
    public TdlMasterModel()
    {
    }
    public TdlMasterModel(Guid? accountId)
    {
      AccountId = accountId;
    }
    public Guid? AccountId { get; set; }
    public Model1 model1{ get; set; }
    ...
    public ModelN modeln{ get; set; }
  }
}

Controller:

[HttpPost]
[SaveModelState]
public virtual RedirectToRouteResult Index(MasterModel masterModel)

So far, nothing of the proposed solutions had worked. Is there any straight solution for this or I will have to create a ModelBinder to handle this.

回答1:

Use a view model containing the models for each form you want to render

View Model

public class MasterVM
{
  public Model1 Model1 { get; set; }
  public Model2 Model2 { get; set; }
  ....
}

Controller

public ActionResult Index()
{
  MasterVM model = new MasterVM();
  // Set the values of only those models you want to display forms for
  model.Model2 = new Model2();
  return View(model);
}

[HttpPost]
public ActionResult Index(MasterVM model)
{
  if(!ModelState.IsValid)
  {
    return View();
  }
  if (model.Model1 != null)
  {
    // save Model1
  }
  else if (model.Model2 != null)
  {
    // save Model2
  }
  ....
  // redirect somewhere
}

Partial _Model1.cshtml

@model Model1

@Html.LabelFor(m => m.SomeProperty)
@Html.TextBoxFor(m => m.SomeProperty)
@Html.ValidationMessageFor(m => m.SomeProperty)

@Html.LabelFor(m => m.AnotherProperty)
@Html.TextBoxFor(m => m.AnotherProperty)
@Html.ValidationMessageFor(m => m.AnotherProperty)

....

View

@model MasterVM
@using Html.BeginForm())
{
  if(Model.Model1 != null)
  {
    @Html.Partial("_Model1", Model.Model1, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "Model1" }})
  }
  if(Model.Model2 != null)
  {
    @Html.Partial("_Model2", Model.Model2, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "Model2" }})
  }
  ....
  <input type="submit" />
}

Note the 3rd parameter of @Html.Partial. This will add a prefix to the control names, so if Model1 contains property string Name, the control will be generated as <input name="Model1.Name" ...> allowing you to post back and bind to MasterVM