Posting multiple forms on MVC Razor View with a Si

2019-03-28 09:38发布

问题:

I have a razor view that will contain 5 forms. (A, B, C, D, E). Each of these forms has their own ViewModel (AViewModel, BViewModel, CViewModel, DViewModel, EViewModel).

I created a parent ProductViewModel that the page will use as its only ViewModel. Within this ViewModel will be all of the other ViewModels for each form on the page (listed above).

Here is what the ProductViewModel's properties look like:

public AViewModel { get; set; }
public BViewModel { get; set; }
public CViewModel { get; set; }
public DViewModel { get; set; }
public EViewModel { get; set; }

My razor view looks like the following:

@model MVCApp.ViewModels.ProductViewModel

@{
    ViewData["Title"] = "Product Detail";
}

<h2>Product Detail</h2>

<form asp-controller="A" asp-action="Save" data-ajax="true" data-ajax-method="POST">
    <div class="form-horizontal">
        <h4>Form A</h4>
        <hr />
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        <div class="form-group">
            <label asp-for="AViewModel.Name" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="AViewModel.Name" class="form-control"/>
                <span asp-validation-for="AViewModel.Name" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="AViewModel.StartDate" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="AViewModel.StartDate" class="form-control"/>
                <span asp-validation-for="AViewModel.StartDate" class="text-danger" />
            </div>
        </div>
        <div class="form-group">
            <label asp-for="AViewModel.EndDate" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="AViewModel.EndDate" class="form-control"/>
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
</form>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

When the Save button is clicked I end up in the Save (POST) Action Method of the A Controller just as I would expect:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Save(AViewModel viewModel)
    {
        return Ok();
    }

However, I noticed all of the properties of the viewModel param are empty.

Does this mean I really have to pass the ProductViewModel object as my ViewModel to the Save Action Method for all 5 of these forms where only the ViewModel in scope will be populated and everything else will be null? Or is there a better way?

回答1:

I suggest make each form a partial view and pass in only the sub model needed from the parent model

 @{await Html.RenderPartialAsync("AViewPartial", Model.AViewModel); }

then your inputs in the partial can be changed from

<input asp-for="AViewModel.Name" class="form-control"/>

to

<input asp-for="Name" class="form-control"/>

which should make it bind correctly by the modelbinder so it gets passed into your action



回答2:

Your parameter to the POST method needs to be ProductViewModel.

The reason for this is because of the way the properties are named in your HTML:

<input asp-for="AViewModel.Name" class="form-control"/>

The model binder takes your serialized form, and sees that you're sending the POST method a value for a property named AViewModel.Name. When your parameter is of type AViewModel, it doesn't find anything, because it's looking for the child property of your parameter type with a name of AViewModel, and then assigning that object's Name property to the value of the input.

If you don't want to make the parameter of each of your POST methods ProductViewModel, then you have one leading alternative: Create Partial Views for your five different forms, and pass in the individual properties from your ProductViewModel as your model for the partial view. This will then eliminate the AViewModel property prefix from your asp-for attribute.