In ASP.NET MVC3, how should one render multiple Pa

2019-04-11 10:04发布

问题:

In MVC3 Razor, how do you create a page with multiple forms, so that each form is a partial view rendered with its own model?

We have been trying various forms of calling Html.RenderPartial(), passing in the partialview name as well as an instance of our models that we're accessing through the ViewBag, but every method we've tried seems to have serious issues, and so we must have a fundamental misunderstanding of how this is supposed to operate in an ideal world.

We have found some SO responses on similar topics (like this one) recommending a "super model" that contains references to each of the sub models our partial views might need to use, but we cannot find an example of how you would actually use those submodels' data, e.g. when trying to write EditorTemplates.

Edit: Let me clarify my question a bit. We ARE able to render a partialview or even an action (using RenderAction as Mark S. recommended). However, our primary trouble is that when those forms submit back to, e.g. save the state of the Model in that partialview area, the Controller doesn't seem to know what model we're referencing. How should we be calling into the Controller?

We've tried a few different ways of rendering these partial views. Our first try was like this:

@using (Html.BeginForm("SaveAccountInfo", null, FormMethod.Post, new { id = "section1-form" }))
{
    @Html.EditorForModel("AccountInfo", (OurModels.AccountInfo) ViewBag.account_info)
}

The ViewBag.account_info contains the Model data for this one partial view.

We've also tried:

@{Html.RenderPartial("FormAccountInfo", (OurModels.AccountInfo) ViewBag.account_info);}

And now:

@{Html.RenderAction("FormAccountInfo", "Main", new { account_info = (OurModels.AccountInfo) ViewBag.account_info }); }

But no matter which method we use for rendering this one form on the page, when that form gets submitted for processing, we can't seem to access any of the submitted data via the Model? I feel like we're doing something really dumb, but we're new to MVC.

回答1:

Don't get bogged down in abstractions, from the time you render the page to the time the controller method is called you're working in HTML and HTTP only.

If you want to post back to a different method there's numerous ways to do so. You can have multiple HTML forms on your page, or you can use something like jQuery to perform a post. Which controller action picks up your post and what data is sent back is determined by the form on the HTML page and its contents. The MVC handlers will pick up the request, and attempt to bind the request URL and contents to one of the available actions that match it.

To "post back a model" you need to send the data back to a suitable action in such a way that the model-binder can read it. This could be JSON data (if you're using MVC 3) or a form with name-value pairs. The controller though knows nothing about the page that is requesting the data, or how it was created. It doesn't even know whether the request is being made by a page.

You can try this for yourself by making a normal HTML page with a form on it that is set up to post to the URL of one of your MVC controllers. So long as the names of the form fields match the names of the properties on the argument of your controller action, MVC will bind it and call the action.

If you have complex views, you may well find that jQuery post is the way to go, with some client-side methods that gather data from the page and create JSON objects to send back. Using that method gives you very precise control over what data is sent back, whereas with forms you have limitations (such as overlapping data etc).

Edit - response to comment

Here's an example that is using raw HTML only to show a very simple case:

Public Class HomeController

    'Other action methods...

    <HttpPost()>
    Function SayHello(myName As String) As ActionResult
        Return Content("Hello " & myName)
    End Function

End Class

And HTML form (I plonked this on the Index view but it could go absolutely anywhere)

<form action="/Home/SayHello" method="post">
    <input type="text" name="myName" />
    <input type="submit" />
</form>

I've set the name on the <input> to match the parameter name, which gives MVC a helping hand. While there is only 1 value being posted back though, I could name it something else. However if I did this:

<form action="/Home/SayHello" method="post">
    <input type="text" name="aName" />
    <input type="text" name="anotherName" />
    <input type="submit" />
</form>

...then the model binder wouldn't know which value it should use, and you'd get values not being set properly.

Change to this however:

<form action="/Home/SayHello" method="post">
    <input type="text" name="myName" />
    <input type="text" name="anotherName" />
    <input type="submit" />
</form>

... and the model binder is now able to use the name to correctly identify which value to use and which to ignore.

In the case of strongly typed actions:

Public Class HomeController

    <HttpPost()>
    Function SayHello(person As SimplePerson) As ActionResult
        Return Content("Hello " & person.FirstName)
    End Function

End Class

Public Class SimplePerson
    Public Property FirstName As String
    Public Property Surname As String
End Class

You need to have form names that correspond with property names. My aName/anotherName form would send back the right shape, but the model binder wouldn't set any properties because it doesn't know what is what. This however would work:

<form action="/Home/SayHello" method="post">
    <input type="text" name="FirstName" />
    <input type="text" name="Surname" />
    <input type="submit" />
</form>


回答2:

You can use the Html.RenderAction() method. It provides a bunch of functionality that allows you to you to render and action and have it call get it own information from your data store. Here is a link to the documentation: http://msdn.microsoft.com/en-us/library/system.web.mvc.html.childactionextensions.renderaction.aspx.