Search code examples
c#asp.net-mvcentity-frameworkcheckboxmany-to-many

C# - MVC 4 Many-To-Many Checkboxes values passed to another view


I have been working on this and have been searching for hours and still can not figure out a solution.

I am trying to display the ItemNames of the checked checkboxes from my AsoociateMenuItems view to my Index view. Would appreciate any help I can get.

MenuItemViewModel:

public class MenuItemViewModel
{
    public int MenuId { get; set; }
    public double ItemPrice { get; set; }
    public string ItemName { get; set; }
    public bool Selected { get; set; }
    public virtual ICollection<IngredientViewModel> Ingredients { get; set;}
}

OrderViewModel:

public class OrderViewModel
{
    public int OrderId { get; set; }
    public int TableNum { get; set; }
    public string Notes { get; set; }
    public double Discount { get; set; }
    public virtual ICollection<MenuItemViewModel> MenuItem { get; set; }
}

Index:

@model IEnumerable<Final_POS.Models.Order>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Employee.EmpName)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.TableNum)
        </th>
        <th>
+
            @Html.DisplayNameFor(model => model.Discount)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.MenuItems)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Employee.EmpName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.TableNum)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Discount)
        </td>
        <td>
            @Html.EditorFor(modelItem => item.MenuItems)
        </td>
        <td>
            @Html.ActionLink("Edit", "AsoociateMenuItems", new { id=item.OrderId }) |
            @Html.ActionLink("Details", "Details", new { id=item.OrderId }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.OrderId })
        </td>
    </tr>
}

</table>

AsoociateMenuItems: -this is a replacement for my edit view

@model Final_POS.Models.ViewModel.OrderViewModel


@{
    ViewBag.Title = "AsoociateMenuItems";
}

<h2>AsoociateMenuItems</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>OrderViewModel</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.OrderId, new { htmlAttributes = new { @class = "form-control" } })

        <div class="form-group">
            @Html.LabelFor(model => model.TableNum, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.TableNum, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.TableNum, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.HiddenFor(model => model.Notes, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.HiddenFor(model => model.Notes, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Notes, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Discount, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Discount, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Discount, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.EmployeeEmpId, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.EmployeeEmpId, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.EmployeeEmpId, "", new { @class = "text-danger" })
            </div>
        </div>

        @Html.EditorFor(model => model.MenuItem)
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

This next code snippet is being used by my AsoociateMenuItems in this line @Html.EditorFor(model => model.MenuItem)

MenuItemViewModel: (View)

@model Final_POS.Models.ViewModel.MenuItemViewModel

<fieldset>
    @Html.HiddenFor(model => model.MenuId)

    @Html.CheckBoxFor(model => model.Selected)
    @Html.DisplayFor(model => model.ItemName)
    @Html.DisplayFor(model => model.ItemPrice)

</fieldset>

Controller:

    public class OrdersController : Controller
    {
        private POSContext db = new POSContext();

        // GET: Orders
        public ActionResult Index()
        {
            var orders = db.Orders.Include(o => o.Employee);
            return View(orders.ToList());
        }

        // GET: Orders/Details/5
        public ActionResult Details(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Order order = db.Orders.Find(id);
            if (order == null)
            {
                return HttpNotFound();
            }
            return View(order);
        }

        // GET: Orders/Create
        public ActionResult Create()
        {
            ViewBag.EmployeeEmpId = new SelectList(db.Employees, "EmpId", "EmpName");
            return View();
        }

        // POST: Orders/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "OrderId,TableNum,Discount,EmployeeEmpId")] Order order)
        {
            if (ModelState.IsValid)
            {
                db.Orders.Add(order);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            ViewBag.EmployeeEmpId = new SelectList(db.Employees, "EmpId", "EmpName", order.EmployeeEmpId);
            return View(order);
        }

        // GET: Orders/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Order order = db.Orders.Find(id);
            if (order == null)
            {
                return HttpNotFound();
            }
            ViewBag.EmployeeEmpId = new SelectList(db.Employees, "EmpId", "EmpName", order.EmployeeEmpId);
            return View(order);
        }

        // POST: Orders/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "OrderId,TableNum,Discount,EmployeeEmpId")] Order order)
        {
            if (ModelState.IsValid)
            {
                db.Entry(order).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            ViewBag.EmployeeEmpId = new SelectList(db.Employees, "EmpId", "EmpName", order.EmployeeEmpId);
            return View(order);
        }

        // GET: Orders/Delete/5
        public ActionResult Delete(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Order order = db.Orders.Find(id);
            if (order == null)
            {
                return HttpNotFound();
            }
            return View(order);
        }

        // POST: Orders/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Order order = db.Orders.Find(id);
            db.Orders.Remove(order);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        public ActionResult AsoociateMenuItems(int? id)
        {
            Order _order = db.Orders.Find(id);

            if (_order == null)
            {
                return HttpNotFound();
            }

            OrderViewModel _orderViewModel = new OrderViewModel()
            {
                OrderId = _order.OrderId,
                Discount = _order.Discount,
                TableNum = _order.TableNum,
                EmployeeEmpId = _order.EmployeeEmpId
            };

            List<MenuItemViewModel> _menuItemViewModel = new List<MenuItemViewModel>();
            foreach (MenuItem menuItem in db.MenuItems)
            {
                _menuItemViewModel.Add(new MenuItemViewModel()
                {
                    MenuId = menuItem.MenuId,
                    ItemName = menuItem.ItemName,
                    ItemPrice = menuItem.ItemPrice,
                    Selected = _order.MenuItems.Contains(menuItem)
                });
            }

            _orderViewModel.MenuItem = _menuItemViewModel;

            return View(_orderViewModel);
        }



        [HttpPost]
        public ActionResult AsoociateMenuItems(OrderViewModel _orderViewModel)
        {
            Order _order = db.Orders.Find(_orderViewModel.OrderId);
            _order.MenuItems.Clear();

            foreach (MenuItemViewModel _menuItemViewModel in _orderViewModel.MenuItem)
            {
                if (_menuItemViewModel.Selected)
                {
                    MenuItem _menuItem = db.MenuItems.Find(_menuItemViewModel.MenuId);
                    _order.MenuItems.Add(_menuItem);
                }
            }
            db.SaveChanges();

            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}

Solution

  • Let's start. Your question is really hard for understanding. BUT I hope that I understood. At first, you should use model in all views. It is really important. You MUST do it. The easiest way - just extend you OrderViewModel with EmpName

    public class OrderViewModel
    {
        public int OrderId { get; set; }
        public int TableNum { get; set; }
        public string Notes { get; set; }
        public double Discount { get; set; }
        public string EmpName { get; set; }
        public virtual ICollection<MenuItemViewModel> MenuItems { get; set; }  //renamed to plural  
    }
    

    Than change your Index View

    @model IEnumerable<OrderViewModel>
    
    @{
        ViewBag.Title = "Index";
    }
    
    <h2>Index</h2>
    
    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
    <table class="table">
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.EmpName) 
            </th>
            <th>
                @Html.DisplayNameFor(model => model.TableNum)
            </th>
            <th>
    
                @Html.DisplayNameFor(model => model.Discount)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.MenuItems)
            </th>
            <th></th>
        </tr>
    
    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.EmpName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.TableNum)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Discount)
            </td>
            <td>
                @Html.EditorFor(modelItem => item.MenuItems)
            </td>
            <td>
                @Html.ActionLink("Edit", "AsoociateMenuItems", new { id=item.OrderId }) |
                @Html.ActionLink("Details", "Details", new { id=item.OrderId }) |
                @Html.ActionLink("Delete", "Delete", new { id=item.OrderId })
            </td>
        </tr>
    }
    
    </table>
    

    Than change the controller method Index (Just get menuitems from db also)

        // GET: Orders
        public ActionResult Index()
        {
            var orders = db.Orders.Include(o => o.Employee).Include(o => o.MenuItems);
            var orderModels = new List<OrderViewModel>();
            foreach(var _order in orders)
            {
                OrderViewModel _orderViewModel = new OrderViewModel()
                {
                    OrderId = _order.OrderId,
                    Discount = _order.Discount,
                    TableNum = _order.TableNum,
                    EmpName = _order.Employee.EmpName
                };
    
                List<MenuItemViewModel> _menuItemViewModels = new List<MenuItemViewModel>();
                foreach (MenuItem menuItem in order.MenuItems)
                {
                    if(_order.MenuItems.Contains(menuItem)) //where selected is true
                    {
                        _menuItemViewModel.Add(new MenuItemViewModel()
                        {
                            MenuId = menuItem.MenuId,
                            ItemName = menuItem.ItemName,
                            ItemPrice = menuItem.ItemPrice,
                        });
                    }
                }
                _orderViewModel.MenuItems = _menuItemViewModels;
                orderModels.Add(_orderViewModel);
            }
            return View(orderModels);
        } 
    

    I hope you will understand what I meant. And sure, my code need code refactoring, but you can do it by yourself.