Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Post a form with multiple partial views

I'm currently trying to post a form composed of two strongly typed views. This question is similar but it doesn't have an answer:

MVC 3 Razor Form Post w/ Multiple Strongly Typed Partial Views Not Binding

When I submit form the model submitted to the controller is always null. I've spent a couple of hours trying to get this to work. This seems like it should be simple. Am I missing something here? I don't need to do ajax just need to be able to post to the controller and render a new page.

Thanks

Here's my view code:

<div>
    @using (Html.BeginForm("TransactionReport", "Reports", FormMethod.Post, new {id="report_request"}))
    {
        ViewContext.FormContext.ValidationSummaryId = "valSumId";
        @Html.ValidationSummary(false, "Please fix these error(s) and try again.", new Dictionary<string, object> { { "id", "valSumId" } });
        @Html.Partial("_ReportOptions", Model.ReportOptions);
        @Html.Partial("_TransactionSearchFields", new ViewDataDictionary(viewData) { Model = Model.SearchCriteria });
    }

Here's the code in the controller:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult TransactionReport(TransactionReportRequest reportRequest)
{
    var reportInfo = new List<TransactionReportItem>();

    if (ModelState.IsValid)
    {
        var reportData = _reportDataService.GetReportData(Search.MapToDomainSearchCriteria(reportRequest.SearchCriteria));
        if (reportData!=null)
        {
            reportInfo = reportData.ToList();
        }
        return View(reportInfo);
    }
    return View(reportInfo);
}

The partial views themselves are pretty irrelevant since all they are doing is biding and displaying their models.

like image 673
dalcantara Avatar asked May 15 '12 20:05

dalcantara


People also ask

How do I return multiple partial views from a controller?

You can only return one value from a function so you can't return multiple partials from one action method. If you are trying to return two models to one view, create a view model that contains both of the models that you want to send, and make your view's model the new ViewModel.

How do you implement partial view?

To create a partial view, right-click on view -> shared folder and select Add -> View option. In this way we can add a partial view. It is not mandatory to create a partial view in a shared folder but a partial view is mostly used as a reusable component, it is a good practice to put it in the "shared" folder.

When should partial views be used?

Partial views are an effective way to: Break up large markup files into smaller components. In a large, complex markup file composed of several logical pieces, there's an advantage to working with each piece isolated into a partial view.


4 Answers

Partials are not the way to go here. You are looking for EditorTemplates, these are made for what you want. This case, your properties will be nicely bound to your model (that you will submit).

Your main View will have this form (note that you only have to use EditorFor instead of Partial; in this case, you probably will need to put that viewData parameter in the ViewBag or so):

@using (Html.BeginForm("TransactionReport", "Reports", FormMethod.Post, new {id="report_request"}))
{
    ViewContext.FormContext.ValidationSummaryId = "valSumId";
    @Html.ValidationSummary(false, "Please fix these error(s) and try again.", new Dictionary<string, object> { { "id", "valSumId" } });
    @Html.EditorFor(model => model.ReportOptions);
    @Html.EditorFor(model = Model.SearchCriteria });
}

Now you only have to drag your partials to the folder ~/Shared/EditorTemplates/ and rename them to match the model name they are the editor templates for.

In the ~/Shared/EditorTemplates/ folder, make a new "view", example "SearchCriteria.cshtml". Inside, put as "model" the type of class you which to create an editor template for. Example (example class has properties Name and OtherCriteria):

@model MyNamespace.SearchCriteria
<ul>
    <!-- Note that I also use EditorFor for the properties; this way you can "nest" editor templates or create custom editor templates for system types (like DateTime or String or ...). -->
    <li>@Html.LabelFor(m => m.Name): @Html.EditorFor(m => m.Name)</li>
    <li>@Html.LabelFor(m => OtherCriteria): @Html.EditorFor(m => m.OtherCriteria</li>
</ul>

Some good reading about them:

  • https://www.exceptionnotfound.net/asp-net-mvc-demystified-display-and-editor-templates/
  • https://www.hanselman.com/blog/ASPNETMVCDisplayTemplateAndEditorTemplatesForEntityFrameworkDbGeographySpatialTypes.aspx
like image 113
Styxxy Avatar answered Oct 01 '22 10:10

Styxxy


You should add prefix to the PartialView's fields. That will let binding data correctly.

So instead:

@Html.Partial("_ReportOptions", Model.ReportOptions);

Use:

@Html.Partial("_ReportOptions", Model.ReportOptions, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "ReportOptions" }})
like image 36
Sebastian Xawery Wiśniowiecki Avatar answered Oct 01 '22 10:10

Sebastian Xawery Wiśniowiecki


I agree with @Styxxy and @Tony, Editor Templates are the better solution. However, your problem is that that you are feeding a sub-model to the partial views. Thus, when the partial view renders it doesn't know that it's part of a larger model and does not generate the correct name attributes.

If you insist on using Partials rather than Editor Templates, then I suggest only passing the Model to the partials, then having each partial do Model.Whatever.Foo and it will generate the correct name attributes for binding.

like image 34
Erik Funkenbusch Avatar answered Oct 01 '22 10:10

Erik Funkenbusch


Try using EditorTemplates instead of Partials http://coding-in.net/asp-net-mvc-3-how-to-use-editortemplates/.

like image 31
magritte Avatar answered Oct 01 '22 09:10

magritte