34

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.

Community
  • 1
  • 1
dalcantara
  • 1,613
  • 2
  • 21
  • 35
  • Where is your submit button? It needs to be within the Form tag. Not sure if you just didn't include it or if it's outside the tag – CD Smith May 15 '12 at 21:01

5 Answers5

56

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:

Styxxy
  • 7,462
  • 3
  • 40
  • 45
  • 1
    Yes this is a much better solution. Grails has something similar I should've googled (I mean binged) for it using the grails terms :) This works great! Thanks for the help! – dalcantara May 15 '12 at 21:30
  • 4
    Sometimes it is hard to find something when you don't know the correct terminology in a certain technology. – Styxxy May 15 '12 at 21:39
  • Since the link is broken, here is a good toutorial about [EditorTemplates](http://www.growingwiththeweb.com/2012/12/aspnet-mvc-display-and-editor-templates.html) – bnu Jul 31 '17 at 13:42
  • 2
    @bnu : thanks for notifying. I replaced the old link with 2 good alternatives and added some small example code. – Styxxy Jul 31 '17 at 20:25
13

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" }})
10

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.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
4

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

magritte
  • 7,396
  • 10
  • 59
  • 79
2
@Html.Partial("_ReportOptions", Model.Contact, new ViewDataDictionary()
           {
               TemplateInfo = new TemplateInfo()
                   {
                       HtmlFieldPrefix = "Contact"
                   }
           })

)


@Html.Partial("_TransactionSearchFields", Model.SearchCriteria, new  
 ViewDataDictionary()
           {
               TemplateInfo = new TemplateInfo()
                   {
                       HtmlFieldPrefix = "SearchCriteria"
                   }
           })
Kesikan K
  • 29
  • 3