Original Question: This might be a simple thing to do but I have been looking for it for more than 30 hrs by now and couldn't find any suitable answer that works for me (tried a tons of ways as well).
So, what I am trying to achieve is that I have to display some data on a view and also take in some values within the same view. For this I have created a main view, this displays some data and takes in some values. Also a partial view within the main view, which also displays some data and takes in some values.
The Models for main view and Partial View are different and the related DB tables as well.
Main View Model:
public class VMBooking : VMBase
{
public int BookingID { get; set; }
public Guid BookingGUID { get; set; }
public int NoOfChildrenArrived { get; set; }
...
public List<VMBookingGuest> VMBookingGuests { get; set; }
...
}
Partial View Model
public class VMBookingGuest
{
public int BookingGuestID { get; set; }
public int BookingID { get; set; }
public int GuestID { get; set; }
public string GuestName { get; set; }
public string GuestCNIC { get; set; }
public bool IsCNIC { get; set; }
public bool IsGuestArrived { get; set; }
public bool IsGuestDeparted { get; set; }
}
Now I have successfully passed the partial view model in the partial view and my data is being displayed as well..
My Main View CSHTML:
@using VMBooking
<form class="form-horizontal" id="validation-form" method="post" defaultbutton="btnSubmit">
@Html.HiddenFor(c => c.BookingID)
@Html.HiddenFor(c => c.BookingGUID)
@Html.Partial("_Arrival", Model.VMBookingGuests)
<div class="form-group">
<input asp-for="@Model.NoOfChildrenArrived" type="text" id="NoOfChildrenArrived" />
</div>
<button type="submit" id="btnSubmit">Submit</button>
</form>
Partial View CSHTML:
@foreach (var item in Model)
{
<tr class="center">
<td>@item.GuestName</td>
<td>@item.GuestCNIC</td>
<td>
<div>
<input id="IsGuestArrived" name="IsGuestArrived" asp-for="@item.IsGuestArrived" type="checkbox"/>
</div>
</td>
</tr>
}
The corresponding controller action is as follows
[HttpGet]
public IActionResult Arrival(int id)
{
VMBooking model = _BookingRepo.GetBookingByID(id);
model.VMBookingGuests = _BookingRepo.GetGuestinfo(id);
PopulateDropdowns(model);
return View(model);
}
[HttpPost]
public IActionResult Arrival(VMBooking vmBooking)
{
VMBooking model = _BookingRepo.GetBookingByID(vmBooking.BookingID);
model.VMBookingGuests = _BookingRepo.GetGuestinfo(vmBooking.BookingID);
if (model != null)
{
_BookingRepo.ArrivalUpdate(vmBooking);
foreach (var item in model.VMBookingGuests)
{
_BookingRepo.GuestArrivalUpdate(item);
}
return RedirectToAction("Index");
}
PopulateDropdowns(model);
return View();
}
Things are working fine, but the problem arises where I have to submit this combined input data (from main and partial view) on a Single submit button, which is on the main view. When i press the submit button only the values from my main view are passed to the controller and not of the partial view.
Note that I have to pass a list of Check Box values (id="IsGuestArrived") to the controller for every guest entry.
And as said before I have tried a number of different ways but none of them is working for me. So I am asking, What would be the suitable way to achieve this?
Edit: I have found the answer to my query and now i would like to display the changes that i made to my code on the suggestion of @KingKing...
What i did was I inserted partial tag instead of @html.partial in my main view, so the code for my main view goes as
@using VMBooking
<form class="form-horizontal" id="validation-form" method="post" defaultbutton="btnSubmit">
@Html.HiddenFor(c => c.BookingID)
@Html.HiddenFor(c => c.BookingGUID)
<partial name="_Arrival" for="VMBookingGuests" />
<div class="form-group">
<input asp-for="@Model.NoOfChildrenArrived" type="text" id="NoOfChildrenArrived" />
</div>
<button type="submit" id="btnSubmit">Submit</button>
</form>
And as for my partial view i went with
@foreach (var item in Model)
{
<input type="hidden" asp-for="@Model[i].GuestID" />
<input type="hidden" asp-for="@Model[i].BookingID" />
<tr class="center">
<td>@item.GuestName</td>
<td>@item.GuestCNIC</td>
<td>
<div>
<input id="IsGuestArrived" name="IsGuestArrived" asp-for="@item.IsGuestArrived" type="checkbox"/>
</div>
</td>
</tr>
}
notice the Hidden values that i have used inside my partial view.
And within my controller instead of using
model.VMBookingGuests = _BookingRepo.GetGuestinfo(vmBooking.BookingID);
I used
model.VMBookingGuests = vmBooking.VMBookingGuests;
Because without the hidden values the guest IDs were not being recognized.
Elements rendered in partial views should be able to be rendered with their names prefixed with the correct path. It's a bit tricky with Html.Partial
(will have a solution at the end). But if you use the tag helper <partial>
, it would be easier by using the for
attribute (note it's different from model
attribute which will not pass along the current name path), like this:
<partial name="_Arrival" for="VMBookingGuests"/>
Your partial view need to be updated as well, because you're mapping an array, you need to use indices to access each array member so that the name for each item can be rendered correctly (with the index), like this:
<!-- NOTE: this requires the view model of your partial view must support accessing item by index, like an IList -->
@for(var i = 0; i < Model.Count; i++) {
<tr class="center">
<td>@Model[i].GuestName</td>
<td>@Model[i].GuestCNIC</td>
<td>
<div>
<input id="IsGuestArrived-@i" asp-for="@Model[i].IsGuestArrived" type="checkbox"/>
</div>
</td>
</tr>
}
Now it should work expectedly because the element names are rendered correctly like VMBookingGuests[0].IsGuestArrived
, ...
Now for Html.Partial
, you can still use it but you need to pass in the prefix info via a ViewDataDictionary
, you need to build that prefix by your own, but it's easy because we use just a standard method to get that, like this:
@{
var vd = new ViewDataDictionary(ViewData);
vd.TemplateInfo.Prefix = Html.NameFor(e => e.VMBookingGuests);
}
<!-- for Html.Partial -->
@Html.Partial("_Arrival", Model.VMBookingGuests, vd)
Now with that prefix info passed along, the element names are also rendered correctly as when using <partial>
. Of course you still need the same corrected code for your partial view above.