I'm using Steve Sanderson's BeginCollectionItem approach to add dynamic content. Everything works fine when I'm doing it on the first level. However, when try to implement a nested collection meaning a BeginCollectionItem in another BeginCollectionItem, it doesn't seem to work.
My models are as follows:
public class Order
{
[Key]
[HiddenInput]
public int id { get; set; }
[Display(Name = "Order number")]
public string number { get; set; }
...
[Display(Name = "Payment method")]
public List<PaymentMethod> payment_methods { get; set; }
...
}
public class PaymentMethod
{
public MethodOfPayment method { get; set; }
public CC cc { get; set; }
public CASH cash { get; set; }
public TT tt { get; set; }
}
public class TT
{
[Key]
public int id { get; set; }
[Required(ErrorMessage = "{0} is required.")]
[Display(Name = "Total amount")]
public double? total_value { get; set; }
...
[Display(Name = "Transfers")]
public List<Transfer> transfers { get; set; }
}
public class Transfer
{
[Key]
public int id { get; set; }
[Display(Name = "Payment")]
public int payment_id { get; set; }
[Required(ErrorMessage = "{0} is required.")]
[Display(Name = "SWIFT")]
public string swift { get; set; }
[Required(ErrorMessage = "{0} is required.")]
[Display(Name = "Amount transferred")]
public double? transfer_amount { get; set; }
[Required(ErrorMessage = "{0} is required.")]
[Display(Name = "Date transferred")]
public DateTime transfer_date { get; set; }
...
}
Now what i have is an Order which could have several payment methods, and if one of the payment methods is a TT (telex transfer) it could have several transfers involved. Implementing several payment methods in one Order as a collection works, but when I try to implement several transfers within a TT, nothing of these transfers gets passed to the controller.
Here's what my view looks like:
@model prj.Models.Model.Order
@using (Html.BeginForm("Create")){
@Html.ValidationSummary(true, "Creation was unsuccessful. Please correct the errors and try again.")
...
@Html.TextBoxFor(m => m.number, new { id = "txtnumber" })
...
<div id="editorPaymentRows">
@foreach (var payment in Model.payment_methods)
{
@Html.Partial("_NewPayment", payment)
}
</div>
}
In the _NewPayment partial:
@using prj.Helpers
@model prj.Models.Model.PaymentMethod
<div class="editPaymentRow">
@using (Html.BeginCollectionItem("payment_methods"))
{
...
<div class="editor-label">
@Html.LabelFor(m => m.tt.total_value)<req>*</req>
</div>
<div class="editor-field">
@Html.TextBoxFor(m => m.tt.total_value)
</div>
...
<div id="editorTransferRows">
@if (Model.tt != null)
{
foreach (var transfer in Model.tt.transfers)
{
@Html.Partial("_NewTransfer", transfer)
}
}
...
</div>
}
</div>
and finally in the _NewTransfer partial:
@using prj.Helpers
@model prj.Models.Model.Transfer
...
<div class="editTransferRow">
//using (Html.BeginCollectionItem("transfers"))
@using (Html.BeginCollectionItem("tt.transfers"))
{
...
<div class="editor-label">
@Html.LabelFor(m => m.swift)<req>*</req>
</div>
<div class="editor-field">
@Html.TextBoxFor(m => m.swift, new { @class = "t_swift" })
</div>
...
<div class="editor-label">
@Html.LabelFor(m => m.transfer_amount)<req>*</req>
</div>
<div class="editor-field">
@Html.TextBoxFor(m => m.transfer_amount, new { @class = "t_transfer_amount" })
</div>
...
}
</div>
So everything works, except in the controller the List transfers which is in TT property of the PaymentMethods is always null. It's not being passed to the controller properly. Is there something i'm missing?
Does nested BeginCollectionItem not work? is there an extra step I must do? Please shed some light. Thanks
I figured it out using Joe Stevens' method shown at the following link:
http://www.joe-stevens.com/2011/06/06/editing-and-binding-nested-lists-with-asp-net-mvc-2/
Cheers
To get the prefix with an Html.BeginCollectionItem
, you can access ViewData.TemplateInfo.HtmlFieldPrefix
(I'm using the nuget package). You're on the right track with tt.transfers
, but you need the specific prefix instead.
Instead of just
Html.BeginCollectionItem("tt.transfers")
you'll need the prefix of the current payment_method as well.
@{
var paymentMethodPrefix = ViewData.TemplateInfo.HtmlFieldPrefix;
}
@using (Html.BeginCollectionItem(paymentMethodPrefix + ".tt.transfers"))
and a quick test looks like you can also just:
@using (Html.BeginCollectionItem(ViewData.TemplateInfo.HtmlFieldPrefix + ".tt.transfers"))