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?
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:
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 typeAViewModel
, it doesn't find anything, because it's looking for the child property of your parameter type with a name ofAViewModel
, and then assigning that object'sName
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 yourProductViewModel
as your model for the partial view. This will then eliminate theAViewModel
property prefix from yourasp-for
attribute.I suggest make each form a partial view and pass in only the sub model needed from the parent model
then your inputs in the partial can be changed from
to
which should make it bind correctly by the modelbinder so it gets passed into your action