-2

I know I've asked a lot of questions lately and for that I apologize, I'm in the process of learning the ins and outs of MVC. Anyways, when I select a quantity and hit add to cart it doesn't add the quantity I select, almost like it just grabs a random number and adds it. Here's the code for the view:

 @model IEnumerable<AccessorizeForLess.ViewModels.DisplayProductsViewModel>

@{
    ViewBag.Title = "Products > Necklaces";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<link href="~/Content/Site.css" rel="stylesheet" />
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
<link href="~/Content/jquery.fancybox.css?v=2.1.5" rel="stylesheet" />
<link href="~/Content/jquery.fancybox-buttons.css?v=1.0.5" rel="stylesheet" />
<link href="~/Content/jquery.fancybox-thumbs.css?v=1.0.7" rel="stylesheet" />
<h2>Products > Necklaces</h2>
<div id="update-message"></div>
<p class="button">
    @Html.ActionLink("Create New", "Create")
</p>
@using (Html.BeginForm("AddToCart", "Orders", FormMethod.Post))
{
    <div id="container">
        <div class="scroll">

            @foreach (var item in Model)
            {
                <div class="scroll">
                    <div class="itemcontainer">
                        <table>
                            <tr>
                                <td id="@item.Id">
                                    <div class="DetailsLink"> &nbsp;&nbsp;&nbsp;@Html.ActionLink(@item.Name, "Details", new { id = item.Id })</div>
                                    <br />
                                    <div id="@item.Id"></div>
                                    <div class="divPrice" id="@item.Price">@Html.DisplayFor(modelItem => item.Price)</div>
                                    <div class="divImg"><a class="fancybox-thumbs" href="@item.Image.ImagePath" title="@item.Image.AltText" data-fancybox-group="thumb"><img src="@item.Image.ImagePath" alt="@item.Image.AltText" title="@item.Image.AltText" /></a></div>
                                    <div>&nbsp;</div>
                                    <div class="divQuantity">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Quantity: @Html.TextBoxFor(modelItem => item.Quantity, new { @id = "quantity", @style = "width:50px;", @class = "formTextBox" })</div>
                                    <div class="divAddToCart">
                                        <p class="button">
                                            @Html.ActionLink("Add to cart", "AddToCart", "Orders", new { id = item.Id }, new { @class = "AddToCart" })
                                        </p>
                                    </div>
                                    <div style="height:15px;"></div>
                                </td>
                            </tr>
                        </table>
                    </div>
                </div>
                    }
                    <div class="button">@Html.ActionLink("Back To Categories","Categories")</div>
                    <br />
       </div>        
    </div>
}
@section scripts {
    <script src="~/Scripts/jQuery-jScroll.js"></script>
    <script src="~/Scripts/jquery.fancybox.js?v=2.1.5"></script>
    <script src="~/Scripts/jquery.fancybox-thumbs.js?v=1.0.7"></script>
    <script src="~/Scripts/jquery.fancybox-buttons.js?v=1.0.5"></script>
    <script type="text/javascript">
            //$(function () {
            //    $('.scroll').jscroll({
            //        autoTrigger: true
            //    });
                $('.fancybox-thumbs').fancybox({
                    prevEffect: 'none',
                    nextEffect: 'none',

                    closeBtn: true,
                    arrows: false,
                    nextClick: false
                });
            });
    </script>
}

Here's the code for AddToCart in my controller:

// GET: /Orders/AddToCart/5
public ActionResult AddToCart(int id)
{
    // Retrieve the product from the database
    var productAdded = db.Products
        .Single(p => p.ProductId == id);

    // Add it to the shopping cart
    var cart = ShoppingCart.GetCart(this.HttpContext);

    cart.AddToCart(productAdded);

    // Go back to the main store page for more shopping
    return RedirectToAction("Index");
}

And finally the code for AddToCart in my ShoppingCart model:

 public void AddToCart(Product item)
    {
        // Get the matching cart and product instances
        var order = entities.Orders.FirstOrDefault(
            c => c.OrderGUID == ShoppingCartId
            && c.OrderItems.Where(p=>p.ProductId == item.ProductId).FirstOrDefault().ProductId == item.ProductId);

        if (order == null)
        {
            // Create a new order since one doesn't already exist
            order = new Order
            {
                InvoiceNumber = Guid.NewGuid().ToString(),
                OrderDate=DateTime.Now,
                OrderGUID = ShoppingCartId,
                IsShipped = false
            };
            entities.Orders.Add(order);

            // Save changes
            entities.SaveChanges();

            //add the OrderItem for the new order
            OrderItem oi = new OrderItem()
            {
                OrderId = order.OrderId,
                OrderGUID = ShoppingCartId,
                ProductId = item.ProductId,
                ProductQuantity = item.Quantity,
                ProductPrice = item.ProductPrice
            };

            entities.OrderItems.Add(oi);
            entities.SaveChanges();
        }
        else
        {
            // If the item does exist in the cart, 
            // then add one to the quantity
            order.OrderItems.Where(p => p.ProductId == item.ProductId).FirstOrDefault().ProductQuantity++;
            entities.SaveChanges();
        }            
    }

Code for DisplayProductsViewModel:

using AccessorizeForLess.Data;

using System.ComponentModel.DataAnnotations;

namespace AccessorizeForLess.ViewModels
{
    public class DisplayProductsViewModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        [DisplayFormat(DataFormatString = "{0:C}")]
        public decimal? Price { get; set; }
        public ProductImage Image { get; set; }

        public int ProductQuantity { get; set; }
    }
}

Can anyone possibly see what I'm doing wrong

EDIT

This is what I see when I use the debugging tools

enter image description here

EDIT 'Here is the new view with the submit button added

@model IEnumerable<AccessorizeForLess.ViewModels.DisplayProductsViewModel>

@{
    ViewBag.Title = "Products > Necklaces";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<link href="~/Content/Site.css" rel="stylesheet" />
<link href="~/Content/jquery.fancybox.css?v=2.1.5" rel="stylesheet" />
<link href="~/Content/jquery.fancybox-buttons.css?v=1.0.5" rel="stylesheet" />
<link href="~/Content/jquery.fancybox-thumbs.css?v=1.0.7" rel="stylesheet" />
<h2>Products > Necklaces</h2>
<div id="update-message"></div>
<p class="button">
    @Html.ActionLink("Create New", "Create")
</p>
@using (Html.BeginForm("AddToCart", "Orders", FormMethod.Post))
{
    <div id="container">
        <div class="scroll">

            @foreach (var item in Model)
            {
                <div class="scroll2">
                    <div class="itemcontainer">
                        <table>
                            <tr>
                                <td id="@item.Id">
                                    <div class="DetailsLink"> &nbsp;&nbsp;&nbsp;@Html.ActionLink(@item.Name, "Details", new { id = item.Id })</div>
                                    <br />
                                    <div id="@item.Id"></div>
                                    <div class="divPrice" id="@item.Price">@Html.DisplayFor(modelItem => item.Price)</div>
                                    <div class="divImg"><a class="fancybox-thumbs" href="@item.Image.ImagePath" title="@item.Image.AltText" data-fancybox-group="thumb"><img src="@item.Image.ImagePath" alt="@item.Image.AltText" title="@item.Image.AltText" /></a></div>
                                    <div>&nbsp;</div>
                                    <div class="divQuantity">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Quantity: @Html.TextBoxFor(modelItem => item.Quantity, new { @id = "quantity", @style = "width:50px;", @class = "formTextBox" })</div>
                                    <div class="divAddToCart">
                                        <input type="submit" value="Add To Cart" class="btn btn-default" /> @*@Html.ActionLink("Add to cart", "AddToCart", "Orders", new { id = item.Id }, new { @class = "AddToCart" })*@

                                    </div>
                                    <div style="height:15px;"></div>
                                </td>
                            </tr>
                        </table>
                    </div>
                </div>
                    }
                    <div class="button">@Html.ActionLink("Back To Categories","Categories")</div>
                    <br />
       </div>        
    </div>
}
@section scripts {
    <script src="~/Scripts/jQuery-jScroll.js"></script>
    <script src="~/Scripts/jquery.fancybox.js?v=2.1.5"></script>
    <script src="~/Scripts/jquery.fancybox-thumbs.js?v=1.0.7"></script>
    <script src="~/Scripts/jquery.fancybox-buttons.js?v=1.0.5"></script>
    <script type="text/javascript">
            //$(function () {
            //    $('.scroll').jscroll({
            //        autoTrigger: true
            //    });
                $('.fancybox-thumbs').fancybox({
                    prevEffect: 'none',
                    nextEffect: 'none',

                    closeBtn: true,
                    arrows: false,
                    nextClick: false
                });

                // Document.ready -> link up remove event handler
                //$(".AddToCart").click(function () {
                //    alert('Clicked!');
                //});
            //s});
    </script>
}

And AddToCart

[HttpPost]
public ActionResult AddToCart(DisplayProductDetailsViewModel model)
{
    // Retrieve the product from the database
    var productAdded = db.Products
        .Single(p => p.ProductId == model.Id);

    // Add it to the shopping cart
    var cart = ShoppingCart.GetCart(this.HttpContext);

    cart.AddToCart(productAdded);

    // Go back to the main store page for more shopping
    return RedirectToAction("Index");
}

When I set a breakpoint in AddToCart model is always null, what am I missing here?

PsychoCoder
  • 10,570
  • 12
  • 44
  • 60
  • When you run the debugger on this is `item.Quantity` correct? What does the network request show you for the `POST AddToCart`? – Jasen Jul 25 '15 at 20:28
  • 1
    Also, `GET AddToCart` is cachable by the browser. You should be doing a POST. – Jasen Jul 25 '15 at 20:30
  • @Jasen **item.Quantity** is always 1 (I stepped through), it's like it's not picking up the value from the textbox in the view – PsychoCoder Jul 25 '15 at 22:31
  • Ok, now is the network request reflecting what was entered into the form? – Jasen Jul 25 '15 at 22:48
  • @Jasen no, it doesnt matter what I enter for a quantity it's always 1 – PsychoCoder Aug 01 '15 at 17:35
  • What does the network request -- headers and body-- look like? Open the browser's debugger, use its network monitor, then make your request. What are the details for that request (before it gets to the server)? – Jasen Aug 01 '15 at 18:55
  • @Jasen I guess I'm semi-stupid because I dont understand what it is you're asking kind sir. – PsychoCoder Aug 01 '15 at 18:59
  • Open the browser's debugging tools (usually F12). Click network tab. Click preserve log. Click start recording. Make your AddToCart request. Watch debugger for AddToCart and click on it for details. [Here's a nice](http://stackoverflow.com/a/4423097/2030565) picture of what you want -- it's for Chrome but FF and IE are very similar. – Jasen Aug 01 '15 at 19:09
  • @Jasen I did what you suggested and didn't see AddtoCart anywhere in the list – PsychoCoder Aug 01 '15 at 19:37
  • @Jasen check my edit, I added a picture of what I see when I followed your instructions – PsychoCoder Aug 02 '15 at 18:57

1 Answers1

0

You are always sending the same quantity because you always trigger the ActionLink's GET request that only passes the query string parameter id. The quantity field in your form is never sent as that's part of the POST form body submission.

// you are not triggering this submission: POST AddToCart
@using(Html.BeginForm("AddToCart", "Orders", FormMethod.Post))
{
    // quantity is passed on the request's body
    @Html.TextBoxFor(modelItem => item.Quantity)

    // but GET AddToCart/id is send instead
    @Html.ActionLink("Add to cart", "AddToCart", "Orders", new { id = item.Id }, new { @class = "AddToCart" })
}

As indicated by your screenshot

Request URL: http://localhost:24177/Orders/AddToCart/21
Request Method: GET
Status Code: 302 Found

Notice "Form Data" or "Request Body" is missing from your request.

Each time you call GET AddToCart you query the same product from the data store and set the quantity property to the same value.

You ought to send this GET request as POST instead because:

  • It can be cached by the browser and it won't make subsequent requests to the server
  • Web crawlers can trigger the ActionLink request

So add a submit button to your form and make a POST action. Then be sure the assign a new quantity from the form.

[HttpPost]
public ActionResult AddToCart(FormModel model)
{
    var product = db.Products.Where(... && p => p.Id == model.ProductId);
    var quantity = model.ProductQuantity;

    cart.AddToCart(product, quantity);

    return RedirectToAction("Index");
}
Community
  • 1
  • 1
Jasen
  • 14,030
  • 3
  • 51
  • 68
  • @Jansen I'm trying to follow where you're going here I really am. I changed the view and made some changes to AddToCart, can you check the latest edit and tell me where I'm going wrong here? – PsychoCoder Aug 03 '15 at 20:05
  • Try a submission without a foreach loop. Then make sure the model property names match your form's input names (verify with the rendered html source). – Jasen Aug 03 '15 at 20:23
  • All the properties of **model** in **AddToCart** are null, and there isnt even a property for quantity. I checked the rendered HTML and changed all my property naes to match the ids in the view and still nothing. – PsychoCoder Aug 03 '15 at 21:41
  • Again, does the network request match what you expect at the server? Match up the input `name` attribute rather than the `id` attribute to the model property. – Jasen Aug 04 '15 at 01:09
  • @Jansen I wanted to come back to this question and thank you for all the help you have offered me in this issue. I wanted you to know how much I appreciate the time you took to teach me things I didn't know but now know. The client has decided to go the AJAX route, but dont think I dont appreciate you and what you taught me. – PsychoCoder Aug 04 '15 at 06:57