简体   繁体   中英

ASP.NET MVC 4.5 Map Partial View to Main View on Form submit

Hello Mighty Stackoverflowers,

I'm currently working on an ASP.NET MVC 4.5 application. I need to map the input values from my partial view to my main View Model, when I submit the create form.

In my View "Create.cshtml" I call a partial view "_SwotPart.cshtml" . I pass a part of my ViewModel to the Partial View, like this:

Create.cshtml:

@model MyCoolApp.BLL.Models.MainVm

@foreach (var swot in Model.Swots)
{
        <tr>
            @foreach (var swotPart in swot.SwotParts)
            {
                @Html.Partial("~/Views/Shared/_SwotPart.cshtml", swotPart)
            }
        </tr>
}

My partial View looks as follows, _SwotPartial.cshtml :

<td class="form-group">
    @Html.TextAreaFor(model => model.Label, htmlAttributes: new { Name = nameField, ID = nameField, @class = "form-control", placeholder = Model.SwotTypeId.GetLabel() })
</td>

Anyways, when I submit my form, the values from the partial view never arrive in the controller.

Do you have any ideas how to map this properly?

Thanks!

The problem is in the input names that will be generated the way you're currently trying to achieve this. Razor needs the context of the entire list, or at least the item's position in it, in order to generate correct input names. In other words, the easiest way to solve your issue (with a caveat) is:

@for (var i = 0; i < Model.Swots.Count(); i++)
{
    ...

    @for (var j = 0; j < Model.Swots[i].SwotParts.Count(); j++)
    {
        if (Model.Swots[i].SwotParts[j].SwotTypeId == SwotType.InternalHelpful || Model.Swots[i].SwotParts[j].SwotTypeId == SwotType.InternalHarmful)
        {
            @Html.Partial("~/Views/Shared/_SwotPart.cshtml", Model.Swots[i].SwotParts[j])
        }
    }

    ...

Then, the partial has the correct context to work with and your inputs will be named like Swots[0].SwotParts[0].Label , which the modelbinder will be able to work with.

However , the caveat here is that you're splitting this list into two loops. That's still not going to work, as you're effectively messing with the overall context of the item(s) position within the model. To fix that, you should split your list in your model, which is better anyways, as you can remove this business logic from your view:

public class SwotVm
{
    ...

    public List<SwotPartVm> InternalSwotParts { get; set; }
    public List<SwotPartVm> ExternalSwotParts { get; set; }
}

Then, you can simply iterate over each list individually, and the values will naturally post back to the appropriate list.

Given that you're using a partial to render fields for a particular class type, though, you'd be better served by creating an editor template. If you simply move your partial code to the view: Views\\Shared\\EditorTemplates\\SwotPartVm.cshtml , then in your main view, you can just do:

@for (var i = 0; i < Model.Swots.Count(); i++)
{
    ...

    <tr>
        <th class="swot-heading">Internal</th>
        @Html.EditorFor(m => m.Swots[i].InternalSwotParts)
    </tr>
    <tr>
        <th class="swot-heading">External</th>
        @Html.EditorFor(m => m.Swots[i].ExternalSwotParts)
    </tr>
}

That's obvious much cleaner, and you can take this concept even further by adding a SwotVm.cshtml editor template, allowing you replace even this little bit of code with just:

@Html.EditorFor(m => m.Swots)

Note: In your SwotVm.cshtml editor template, you would only include the code for a single SwotVm . In other words, not including the for statement.

In order for your application to parse the posted values and properly and bind it to your view model. Names of posted form data needs to be like.

swots[x1].swotParts[x2].label

Where x1 is a number ranging from 0 and up for each swot. Where x2 is a number ranging from 0 and up for each swot part in swots.

Now when you are posting, the form data names is just label .

Instead of :

 @Html.TextAreaFor(model => model.Label, htmlAttributes: new { Name = nameField, ID = nameField, @class = "form-control", placeholder = Model.SwotTypeId.GetLabel() })

try :

<textarea name="swots[x1].swotParts[x2].label" class="form-control" placeholder="@Model.SwotTypeId.GetLabel()" >@Model.Label</textarea>

Don't forget to replace the x1 and x2 with a number.

You can read more about model bindings to collections here.

http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM