Wjdavis5
Wjdavis5

Reputation: 4151

MVC5 Add Item to List

Given a Model of :

public class Receipt: BaseEntity
{
    [Required(AllowEmptyStrings = false,ErrorMessage = "Please Select A Store")]
    public Store  Store { get; set; }
    public List<Item> Items { get; set; }
    public Item NewItem { get; private set; }
    [Required(AllowEmptyStrings = false,ErrorMessage = "ReceiptModel.EntryDate is required")]
    public DateTime EntryDate { get; set; }
    public Guid EntryOwner { get; set; }
    public string Name { get; set; }

    public double ReceiptTotal()
    {
        return Items.Sum(item => item.ItemPrice);
    }
    public Receipt()
    {
        Items = new List<Item>();
        EntryOwner = Guid.Empty;
        EntryDate = DateTime.Now;
        Store = new Store();
        NewItem = new Item();
     }       
}

And a Controller :

public class ReceiptController : Controller
{
    //
    // GET: /Receipt/

    public ActionResult Index()
    {
        Receipt receipt = new Receipt();
        return View(receipt);
    }

    [HttpPost]
    public ActionResult AddItem(Receipt receipt)
    {
        receipt.Items.Add(receipt.NewItem);
        if (ModelState.IsValid)
        {
            return View("Index", receipt);
        }
        return View("Index", receipt);
    }

And a View: Updated View - Removed all partial pages and combined into a single page

<form class="form-horizontal" method="POST" action="@Url.Action("AddItem","Receipt")">
<div class="jumbotron">
    <div id="main" class="panel panel-primary">

        <div class="panel-heading"> <h1 class="panel-title">Receipt Builder</h1></div>
        <div class="panel-body">

            <fieldset>
                @Html.LabelFor(model => model.Name)
                @Html.EditorFor(model => model.Name)

                @Html.LabelFor(model => model.Store.Name)
                @Html.EditorFor(model => model.Store.Name)

                @Html.LabelFor(model => model.EntryDate)
                @Html.EditorFor(model => model.EntryDate)
            </fieldset>

        </div>


    </div>
</div>

<table class="table table-bordered table-condensed table-hover table-responsive table-striped">
    <thead>
        <tr>
            <td>
                <label class="control-label">#</label>
            </td>
            <td>
                <label class="control-label">Item</label>
            </td>
            <td>
                <label class="control-label">Unit</label>
            </td>
            <td>
                <label class="control-label">Quantity</label>
            </td>
            <td>
                <label class="control-label">UnitPrice</label>
            </td>
            <td>
                <label class="control-label">Category</label>
            </td>
            <td>
                <label class="control-label">Total</label>
            </td>
        </tr>
        <tr>

                <td>
                    <button class="btn-sm btn-danger" style="border-radius: 50%; background-color: red;" name="Submit" id="Submit" type="submit">+</button>
                </td>

                <td>
                    @Html.EditorFor(model => model.NewItem.Name)
                </td>

                <td>
                    @Html.EditorFor(model => model.NewItem.UnitType)
                </td>

                <td>
                    @Html.EditorFor(model => model.NewItem.Quantity)
                </td>

                <td>
                    @Html.EditorFor(model => model.NewItem.UnitPrice)
                </td>

                <td>
                    @Html.EditorFor(model => model.NewItem.Category.Name)<br/>
                    @Html.EditorFor(model => model.NewItem.Category.IsTaxable)
                </td>

                <td>
                    @Html.EditorFor(model => model.NewItem.ItemPrice)
                </td>


        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Items)
        {
            <tr>
                <td>
                    @Model.Items.ToList().IndexOf(item) + 1
                </td>
                <td>
                    @Html.LabelFor(titem => item.Name)
                </td>
                <td>
                    @Html.LabelFor(titem => item.UnitType)
                </td>
                <td>
                    @Html.LabelFor(titem => item.Quantity)
                </td>
                <td>
                    @Html.LabelFor(titem => item.UnitPrice)
                </td>
                <td>
                    @Html.LabelFor(titem => item.Category.Name)
                </td>
                <td>
                    @Html.LabelFor(titem => item.ItemPrice)
                </td>
            </tr>
        }
    </tbody>

    <tfoot></tfoot>
</table>

Upvotes: 4

Views: 29807

Answers (2)

Wjdavis5
Wjdavis5

Reputation: 4151

I was finally able to resolve this thanks to This Link

It was indeed a binding issue as was suggested. It seems that with complex collections such as Lists we cannot actually utilize the benefits of IEnumerable like I had hoped. This is due to how the data is actually passed in the post having names that are ambiguous. Here is the final code that is working (only have to update the view)

 @for (int i = 0; i < Model.Items.Count(); i++)
        {
            <tr>
                <td>
                @(i+1)
                </td>
                <td>
                    @Html.TextBoxFor(m => m.Items[i].Name)
                </td>
                <td>
                    @Html.TextBoxFor(m => m.Items[i].UnitType)
                </td>
                <td>
                    @Html.TextBoxFor(m => m.Items[i].Quantity)
                </td>
                <td>
                    @Html.TextBoxFor(m => m.Items[i].UnitPrice)
                </td>
                <td>
                    @Html.TextBoxFor(m => m.Items[i].Category.Name)
                </td>
                <td>
                    @Model.Items[i].ItemPrice
                </td>
            </tr>
        }

The resulting HTML names each element with its index ie Item[N].Name Now the post data can be bound correctly.

Upvotes: 6

Dmytro Rudenko
Dmytro Rudenko

Reputation: 2524

Items from your Receipt are shows just as text, without controls. So when you post data back to server ASP.NET MVC can't find this information from request body and lefts Receipt.Items collection empty.

You may add information about entered receipts as hidden fields in partial view with help of Html.HiddenFor() method. Or just save entered items in DB or somewhere else and read theirs into Receipt.Items collection before returning AddItem view.

Upvotes: 1

Related Questions