I have a Controller and View which allow a user to ‘checkout’ the contents of a shopping cart.
All was working fine when my CheckoutController was returning an Order model to the Checkout view. However, I now want to also display the contents of the shopping cart on the Checkout view so have modified the Controller and View to use a viewmodel containing the Order and CartItems.
I have created a CheckoutViewModel and modified the GET: and POST: ActionResults to use this viewmodel.
Problem is the CheckOutViewModel is not being properly populated during the POST:
I am getting an ‘Object reference not set to an instance of an object’ error on this line ‘checkoutViewModel.Order.Username = User.Identity.Name;’ in the POST: ActionResult but I am not sure exactly what is not being instansiated.
If need be, I can post the original working coded that used the Oreder model instead of the CheckoutViewModel.
I’m sure I have some simple sturctural or syntax problem, I just can’t tell what it is because I’m still trying to get my head around the best way to do things in MVC.
GET: ActionResult
//GET: /Checkout/AddressAndPayment
public ActionResult AddressAndPayment()
{
//To pre-populate the form, create a new Order object and get the ShoppingCart, populate the ViewModel and pass it to the view
var order = new Order();
order.Username = User.Identity.Name;
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
storeDB.SaveChanges();
var cart = ShoppingCart.GetCart(this.HttpContext);
// Set up our ViewModel
var viewModel = new CheckoutViewModel
{
CartItems = cart.GetCartItems(),
CartTotal = cart.GetTotal(),
Order = order
};
// Return the view
return View(viewModel);
}
POST: ActionResult
//POST: /Checkout/AddressAndPayment
[HttpPost]
public ActionResult AddressAndPayment(FormCollection values)
{
var checkoutViewModel = new CheckoutViewModel();
TryValidateModel(checkoutViewModel);
try
{
checkoutViewModel.Order.Username = User.Identity.Name;
checkoutViewModel.Order.OrderDate = DateTime.Now;
//Save Order
storeDB.Orders.Add(checkoutViewModel.Order);
storeDB.SaveChanges();
//Process the order
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.CreateOrder(checkoutViewModel.Order);
storeDB.SaveChanges();
//Send 'Order Confirmation' email
ViewData["order"] = checkoutViewModel.Order;
UserMailer.OrderConfirmation(checkoutViewModel.Order).SendAsync();
return RedirectToAction("Complete", new { id = checkoutViewModel.Order.OrderID });
}
catch
{
//Invalid - redisplay with errors
return View(checkoutViewModel);
}
}
View
@model OrderUp.ViewModels.CheckoutViewModel
@{
ViewBag.Title = "AddressAndPayment";
}
<script src="../../Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<h2>Pick Up Details</h2>
<fieldset>
<legend>When are you gonna be hungry?</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Order.Phone)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Order.Phone)
@Html.ValidationMessageFor(model => model.Order.Phone)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Order.PickUpDateTime)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Order.PickUpDateTime)
@Html.ValidationMessageFor(model => model.Order.PickUpDateTime)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Order.Notes)
</div>
<div class="editor-multiline-field">
@Html.EditorFor(model => model.Order.Notes)
@Html.ValidationMessageFor(model => model.Order.Notes)
</div>
</fieldset>
<input type="submit" value="Submit Order" />
}
<div style="height:30px;"></div>
<h3>
<em>Review</em> your cart:
</h3>
<table>
<tr>
<th>
Menu Item
</th>
<th>
Price (each)
</th>
<th>
Notes
</th>
<th>
Quantity
</th>
<th></th>
</tr>
@foreach (var item in Model.CartItems)
{
<tr id="row-@item.RecordID">
<td>
@Html.ActionLink(item.MenuItem.Name, "Details", "Store", new { id = item.MenuItemID }, null)
</td>
<td>
@Html.DisplayFor(modelItem => item.MenuItem.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.Notes )
</td>
<td id="item-count-@item.RecordID">
@item.Count
</td>
<td>
</td>
</tr>
}
<tr>
<td >
Total
</td>
<td id="cart-total">
@String.Format("${0:F2}", Model.CartTotal)
</td>
<td>
</td>
<td>
</td>
<td>
</td>
</tr>
</table>
You should use CheckoutViewModel as an input parameter to your HttpPost method and then try debug and see if you are still not getting that model populated after the form post.