问题
I'm building a restaurant reservations system. When the user creates a reservation they can also decided what furniture will be used with the reservation. For this I created a partial view, so every time the user click on "add furniture" the partial view is loaded with ajax where the user can then specify what furniture and how many. They can add as many types of furniture as they want, meaning they can call the partial view as many times as they want. My problem comes in with the model binder, the model binder then have bind all those partial view instances to a list object of type "BistroReservations_ReservationsFurniture". Please help how to make the model binder work.
MainView (Please have a look at the bottom for the ajax.actionlink call "Add Furniture"
@using (Html.BeginForm())
{ @Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>BistroReservations_Reservation</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.DateOfArrival, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.DateOfArrival, new { htmlAttributes = new { @class = "form-control datecontrol" } })
@Html.ValidationMessageFor(model => model.DateOfArrival, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.BistroReservations_ShiftID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("BistroReservations_ShiftID", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.BistroReservations_ShiftID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.BistroReservations_GuestID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-6">
@Html.DropDownList("BistroReservations_GuestID", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.BistroReservations_GuestID, "", new { @class = "text-danger " })
@Ajax.ActionLink("Create a New Guest", "NewGuest", new AjaxOptions
{
HttpMethod = "GET",
UpdateTargetId = "NewGuest",
InsertionMode = InsertionMode.ReplaceWith,
})
</div>
</div>
<div id="NewGuest" class="form-group">
</div>
<br />
<div class="form-group">
@Html.LabelFor(model => model.ArrivalTime, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("ArrivalTime", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.ArrivalTime, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.LocationID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("LocationID", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.LocationID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.BistroReservations_TypeOfSeatingID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("BistroReservations_TypeOfSeatingID", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.BistroReservations_TypeOfSeatingID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.TableNoID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("TableNoID", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.TableNoID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.BistroReservations_StatusID, "BistroReservations_StatusID", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("BistroReservations_StatusID",null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.BistroReservations_StatusID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Comment, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor( model => model.Comment, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Comment, "", new { @class = "text-danger" })
@Ajax.ActionLink("Add Furniture", "Furniture", new AjaxOptions
{
HttpMethod = "GET",
UpdateTargetId = "Furniture",
InsertionMode = InsertionMode.InsertAfter
})
</div>
</div>
<div id="Furniture" class="form-group">
</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>
}
ActionResult returning the furniture partial view
public PartialViewResult Furniture()
{
ViewBag.BistroReservations_FurnitureID = new SelectList(db.BistroReservations_Furnitures, "BistroReservations_FurnitureID", "Description");
return PartialView("_Furniture");
}
Furniture partial view
@model CdvPortal.Models.BistroReservations.BistroReservations_ReservationFurniture
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.BistroReservations_FurnitureID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("BistroReservations_FurnitureID", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.BistroReservations_FurnitureID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group ">
@Html.LabelFor(model => model.Quantity, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Quantity, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Quantity, "", new { @class = "text-danger" })
</div>
</div>
</div>
Furniture Model
public class BistroReservations_ReservationFurniture
{
public int BistroReservations_ReservationFurnitureID { get; set; }
public int BistroReservations_ReservationID { get; set; }
public virtual BistroReservations_Reservation BistroReservations_Reservation { get; set; }
[Display(Name="Furniture Type")]
public int BistroReservations_FurnitureID { get; set; }
public virtual BistroReservations_Furniture BistroReservations_Furniture { get; set; }
public int Quantity { get; set; }
}
回答1:
You @Ajax.ActionLink()
method is returning views with duplicate id
attributes (invalid html) and duplicate name
attributes which are not prefixed with the correct property name and do not include indexers for binding to a collection. For example if the collection property is named FurnitureItems
then the html needed for the Quantity
property of typeof BistroReservations_ReservationFurniture
would need to be
<input type="text" name="FurnitureItems[0].Quantity" ...>
<input type="text" name="FurnitureItems[1].Quantity" ...>
You can use the BeginCollectionItem helper to generate the controls, which might look like (assuming the property is named FurnitureItems
)
@model CdvPortal.Models.BistroReservations.BistroReservations_ReservationFurniture
@using (Html.BeginCollectionItem("FurnitureItems"))
{
....
@Html.LabelFor(m => m.Quantity, ..)
@Html.EditorFor(m => m.Quantity, ..)
@Html.ValidationMessageFor(m => m.Quantity, ..)
}
This will generate the correct prefix and add include an indexer based on a guid
which also allows you to delete items from the collection. This article explains the usage in more detail
A pure client side alternative is shown in this answer. This gives better performance, but is harder to maintain since changing anything in the BistroReservations_ReservationFurniture
model means updating the client side template.
来源:https://stackoverflow.com/questions/29875633/model-binding-doesnt-work-when-multiple-instances-of-the-same-partial-view-are