1

I am trying to do the following: I have two models, header and List(details), sent to a view by a view model. When loading the main view, a dropdown is displayed from a list in the ViewModel.header model previously loaded. When you click on that dropdown, a partial view is loaded with some values, filtered by the value of the ddl, of the ViewModel.List(details) for the user to complete the information. So far everything works fine, but when doing the Post, controller it receives the ViewModel.List(details) in null.

what am I doing wrong?

Header

    public class StockTransactionsHeader
{
    [Key]
    public int TransactionHeaderID { get; set; }

    public DateTime TransactionDate { get; set; }

    public string TransactionDocument { get; set; }

    public int CategoryID { get; set; }

    [NotMapped]
    public List<SelectList> CategoryCollection { get; set; }

    public virtual List<StockTransactionsDetails> StockTransactionsDetails { get; set; }

}

Details

    public class StockTransactionsDetails
{
    [Key]
    public int TransactionDetailID { get; set; }

    public int TransactionHeaderID { get; set; }

    public int ProductID { get; set; }

    public decimal Qty { get; set; }

    public decimal Amount { get; set; }

    public decimal TransactionAmount { get; set; }

    [NotMapped]
    public string ProductDescription { get; set; }

    public virtual StockTransactionsHeader StockTransactionsHeader { get; set; }
}

ViewModel

public class StockTransactionsViewModel
{
    public StockTransactionsHeader StockTransactionsHeader { get; set; }
    public List<StockTransactionsDetails> StockTransactionsDetails { get; set; }
}

Controller Create

 public ActionResult Create()
 {
     var stockTransactions = new StockTransactionsViewModel();
     stockTransactions.StockTransactionsHeader = GetHeaderCategories();

     return View(stockTransactions);
 }

GetHeaderCategories()

private StockTransactionsHeader GetHeaderCategories()
{
        var header = new StockTransactionsHeader();
        header.CategoryCollection = CommonServices.GetSelecList((int)DeliveryCommonHelper.ConfigurationType.Categoria);
        return header;
}

MainView

@model DeliverySolutionCommon.ViewModels.StockTransactionsViewModel
@using (Html.BeginForm())
{
    <div class="form-row">
        <div id="partialView" class="table-responsive">

        </div>
    </div>

    <div class="form-group">
        <div class="col-md-2">
            <input type="submit" value=" Procesar " class="btn btn-warning" />
        </div>
    </div>
}

Script to load partial view

    <script>
    $(document).ready(function () {
        $("#Category").on("change", function () {
            autoFiltro();
        })
    })

    function autoFiltro() {
        var url = "@Url.Action("GetProductsListByCategory", "StockTransactions")";
        var id = $("#Category").val();
        var data = { idCategory: id };

        $.post(url, data).done(function (data) {
            $("#partialView").html(data);
        })
    }
</script>

GetProductsListByCategory

        [HttpPost]
    public PartialViewResult GetProductsListByCategory(int idCategory)
    {
        var products = ProductsServices.GetProductsListByCategory(idCategory);
        var stockTransactions = new StockTransactionsViewModel();
        stockTransactions.StockTransactionsDetails = GetTransactionsDetails(products);

        return PartialView("_createStockTransactions", stockTransactions);
    }

GetTransactionsDetails

        private List<StockTransactionsDetails> GetTransactionsDetails (List<Products> products)
    {
        var details = new List<StockTransactionsDetails>();

        foreach (var item in products)
        {
            StockTransactionsDetails detail = new StockTransactionsDetails();
            detail.ProductID = item.ProductID;
            detail.ProductDescription = item.Description;

            details.Add(detail);
        }
        return details;
    }

PartialView

    @model DeliverySolutionCommon.ViewModels.StockTransactionsViewModel
<table class="table table-sm table-bordered table-striped">
    @foreach (var item in Model.StockTransactionsDetails)
        {
            <tr class="d-flex">
                <td class="col-7">
                    @Html.DisplayFor(modelItem => item.ProductDescription)
                </td>
                <td class="col-1">
                    @Html.EditorFor(modelItem => item.Qty, new { htmlAttributes 
                    = new { @class = "form-control" } })
                </td>
                <td class="col-2">
                    @Html.EditorFor(modelItem => item.Amount, new { 
                     htmlAttributes = new { @class = "form-control" } })
                </td>
                <td class="col-2">
                    @Html.EditorFor(modelItem => item.TransactionAmount, new { 
                     htmlAttributes = new { @class = "form-control" } })
                </td>
            </tr>
        }
</table>

Aaaaand finally Create Post

        [HttpPost]
    public ActionResult Create(StockTransactionsViewModel stockTransactionsView)
    {
       // StockStransactionsView.StockTransactionsDetails = null

    }
  • PD: Researching, many people suggest using a for loop instead of a foreach in the partial view, try that and it did not work either. – Octavio David Peñuela Muñoz Jan 12 '19 at 05:20
  • Using a `for` [should work](https://stackoverflow.com/q/25333332/11683) (provided the collection is in fact an `IList`). Please post an example of HTML returned by the ajax call. – GSerg Jan 12 '19 at 09:25

1 Answers1

0

The problem is you are posting back a list and there is no indexing information in your HTML... MVC model binder does not know how to put the items in a list without the index info...

you can try something like this:

@for (int i = 0; i < Model.StockTransactionsDetails.Count, i++)
{  

    <tr class="d-flex">
        <td class="col-7">
            @Html.EditorFor(modelItem => Model[i].Amount, new { 
                 htmlAttributes = new { @class = "form-control" } })
        </td>

       // more code...

This would add the indexing information to your HTML...

Alternatively you can use EditorTemplate... something like this:

// Note that EditorFor template would iterate the list item for you
@Html.EditorFor(m => m.Model.StockTransactionsDetails) 

This tutorial might help

Hooman Bahreini
  • 14,480
  • 11
  • 70
  • 137