1

I have this ViewModel:

public class DepositViewModel
{

    public DepositViewModel()
    {
        DepositDate = DateTime.Now;
        CollectedReceipts = new List<DepositQuantityAmountViewModel>();
        CancelledReceipts = new List<DepositQuantityAmountViewModel>();
    }

    [Display(Name = "Deposit #")]
    public long DepositId { get; set; }

    [Display(Name = "Deposit Type")]
    public string DepositType { get; set; }

    [Display(Name = "Cashier #")]
    public int CashierId { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "Deposit Date")]
    [DisplayFormat(DataFormatString = "{0:dd-MMM-yyyy}", ApplyFormatInEditMode = true)]
    public DateTime DepositDate { get; set; }

    [Display(Name = "Collected Receipts")]
    public IList<DepositQuantityAmountViewModel> CollectedReceipts { get; set; }

    [Display(Name= "Cancelled Receipts")]
    public IList<DepositQuantityAmountViewModel> CancelledReceipts { get; set; }

    [Display(Name = "Finance Reference")]
    [MaxLength(2000)]
    public string FinanceReference { get; set; }

    [Display(Name = "Received Amount")]
    [DisplayFormat(DataFormatString = "{0:N2}", ApplyFormatInEditMode = true)]
    public decimal ReceivedAmount { get; set; }

    [Display(Name = "Payment Modes")]
    public IList<BDOs.PayMode> PaymentModes { get; set; }
}

public class DepositQuantityAmountViewModel
{
    [Display(Name = "Description")]
    public string Description { get; set; }

    [Display(Name = "Category")]
    public string Category { get; set; }

    [Display(Name = "Count")]
    public int Count { get; set; }

    [Display(Name = "Total")]
    [DisplayFormat(DataFormatString = "{0:N2}", ApplyFormatInEditMode = true)]
    public decimal Amount { get; set; }
}

In my view, I have this setup:

<div class="row">
    <div class="col-sm-2">
        <div class="form-group">
            <fieldset>
                <legend>@Html.DisplayNameFor(m => m.PaymentModes)</legend>
                <div class="col-sm-12">
                    <div class="row">
                        @foreach (var modelPaymentMode in Model.PaymentModes)
                        {
                            @Html.DisplayFor(m => modelPaymentMode, "_DepositPartPaymentModes")
                        }
                    </div>
                </div>
            </fieldset>
        </div>
    </div>

    <div class="col-sm-5">
        <div class="form-group">
            <fieldset>
                <legend>@Html.DisplayNameFor(m => m.CollectedReceipts)</legend>
                @Html.DisplayFor(m => m.CollectedReceipts, "_DepositPartDetail")
            </fieldset>
        </div>
    </div>

    <div class="col-sm-5">
        <div class="form-group">
            <fieldset>
                <legend>@Html.DisplayNameFor(m => m.CancelledReceipts)</legend>
                @Html.DisplayFor(m => m.CancelledReceipts, "_DepositPartDetail")
            </fieldset>
        </div>
    </div>
</div>

And here are the display templates:

_DepositPartPaymentModes.cshtml

@model DA.Services.IBS.Business.BDO.PayMode

<div class="form-group">
    @Html.TextBoxFor(m => m.Name, new { @class = "form-control", @readonly = "readonly" })
</div>

_DepositPartDetail.cshtml

@model IEnumerable<DA.Services.IBS.Web.FinancePortalFull.Models.DepositQuantityAmountViewModel>

@foreach (var countAmountPair in Model)
{
    if (countAmountPair.Category == "TotalCollected" || countAmountPair.Category == "TotalCancelled")
    {
        <div class="col-sm-12">
            <div class="row">
                <div class="col-sm-6">
                    <div class="form-group">
                        @Html.TextBoxFor(m => countAmountPair.Description, new { @class = "form-control", @readonly = "readonly" })
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="form-group">
                        @Html.TextBoxFor(m => countAmountPair.Amount, "{0:N2}", new { @class = "form-control", type = "numeric", dir = "rtl", @readonly = "readonly" })
                    </div>
                </div>
            </div>
        </div>
    }
    else
    {
        <div class="col-sm-12">
            <div class="row">
                <div class="col-sm-6">
                    <div class="form-group">
                        @Html.TextBoxFor(m => countAmountPair.Count, "{0:N0}", new { @class = "form-control", type = "numeric", dir = "rtl", @readonly = "readonly" })
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="form-group">
                        @Html.TextBoxFor(m => countAmountPair.Amount, "{0:N2}", new { @class = "form-control", type = "numeric", dir = "rtl", @readonly = "readonly" })
                    </div>
                </div>
            </div>
        </div>
    }
}

I have tried to bind each template separately looking at different guides. I cannot get the collections to be returned to postback as they are null. They display properly all three collections are null on submission.

What am I doing wrong?

Solution Do not use ForEach Loops in views to iterate collections. Use For Loops.

DoomerDGR8
  • 4,840
  • 6
  • 43
  • 91
  • 1
    check this solution "https://stackoverflow.com/questions/37238179/asp-net-mvc-5-model-binding-list-is-empty" – Anadi Mar 12 '18 at 14:38
  • @Anadi I'm checking that right now. – DoomerDGR8 Mar 12 '18 at 14:40
  • Maybe you can deploy each template as View Components ? with independency from the core. And recall load it when you need it https://codewala.net/2017/03/09/exploring-asp-net-core-view-component/ – Jordi Jordi Mar 12 '18 at 15:16

2 Answers2

2

The issue is in using foreach loops. When this is rendered in HTML, the input elements are not given names that will mean anything to the model binder when you post. You need to change these to for loops and the inputs will be rendered correctly.

You may also need to change your model so that the list is not null when the binder is trying to add to it.

Eg:

@for (int i = 0; i < Model.PaymentModes; i++)
{
    @Html.DisplayFor(m => Model.PaymentModes[i], "_DepositPartPaymentModes")
}
Steve Harris
  • 5,014
  • 1
  • 10
  • 25
0

Use for loop instead of foreach.

Element should be like this.

<input type="text" name="List[0].PropertyName" value="XYZ" />

change @model IEnumerable<DA.Services.IBS.Web.FinancePortalFull.Models.DepositQuantityAmountViewModel> to @model List<DA.Services.IBS.Web.FinancePortalFull.Models.DepositQuantityAmountViewModel>

_DepositPartDetail.cshtml

 for (int i=0;i<Model.Count(); i++)
  {
   {
    if (countAmountPair.Category == "TotalCollected" || countAmountPair.Category == "TotalCancelled")
    {
        <div class="col-sm-12">
            <div class="row">
                <div class="col-sm-6">
                    <div class="form-group">
                        @Html.TextBoxFor(m => m[i].Description, new { @class = "form-control", @readonly = "readonly" })
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="form-group">
                        @Html.TextBoxFor(m =>  m[i].Amount, "{0:N2}", new { @class = "form-control", type = "numeric", dir = "rtl", @readonly = "readonly" })
                    </div>
                </div>
            </div>
        </div>
    }
    else
    {
        <div class="col-sm-12">
            <div class="row">
                <div class="col-sm-6">
                    <div class="form-group">
                        @Html.TextBoxFor(m =>  m[i].Count, "{0:N0}", new { @class = "form-control", type = "numeric", dir = "rtl", @readonly = "readonly" })
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="form-group">
                        @Html.TextBoxFor(m =>  m[i].Amount, "{0:N2}", new { @class = "form-control", type = "numeric", dir = "rtl", @readonly = "readonly" })
                    </div>
                </div>
            </div>
        </div>
    }
}
Anadi
  • 744
  • 9
  • 24