1

First let me thank @Jasen, he spent 9 days helping me with an issue and it means a lot to me that he took him time to help me. It was this that he was helping me with, but at the last second they decided they wanted to go with AJAX since the contact page uses it and removing items from the cart utilizes it.

Let me get to my issue, I have view (this is MVC 5) that in loop loads all the products of a selected category. I want to use jQuery nd AJAX to add items to the cart. This works great for the first item in the list the first time it is added to the cart.

I imagine my problem is all the buttons have an id of AddToCart and jQuery, the way I have it written can't decide which button is being clicked.

Here is 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/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 id="sending" style="display:none;"><img src="~/Content/ajax-loader.gif" /></div> <div style="color:red" id="ItemAdded"></div> <div class="scroll"> @foreach (var item in Model) { <div class="scroll2"> <div class="itemcontainer"> <table> <tr> <td id="@item.Id" class="divId"> <div class="DetailsLink" id="@item.Id"> &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="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="button" value="AddToCart" class="btn btn-default" id="AddToCart" /> </div> </div> <div style="height:15px;"></div> </td> </tr> </table> </div> </div> } <div class="button">@Html.ActionLink("Back To Categories","Categories")</div> <br /> </div> </div> @*}*@ 

And here is my jQuery code:

@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 () { //first disable the button to prevent double clicks $("#AddToCart").attr("disbled", true); $("#AddToCart").prop("value", "Adding..."); $("#ItemAdded").text(""); //now show the loading gif $("#sending").css("display", "block"); // Get our values var price = parseFloat($(".divPrice").attr("id")); var quantity = parseInt($("#quantity").val()); var id = parseInt($(".divId").attr("id")); $.ajax({ url: "@Url.Action("AddToCartAJAX", "Orders")", type: "POST", data: { "id": id, "quantity": quantity, "price": price }, //if successful success: function (data) { successfulCall() }, error: function (data) { alert(data.Message); } }); function successfulCall() { //enable the send button $("#AddToCart").attr("disbled", false); //hide the sending gif $("#sending").css("display", "none"); //change the text on the button back to Send $("#AddToCart").prop("value", "Add to Cart"); //display the successful message $("#ItemAdded").text("Your item has been added to your order."); //clear out all the values $("input#quantity").val("0"); } function errorCall() { $("#AddToCart").attr("disbled", false); $("#sending").css("display", "none"); $("#AddtoCart").prop("value", "Add to Cart"); $("#ItemAdded").text(data.message); } //alert('Clicked!'); }); //s}); </script> } 

Can someone show me what I am doing wrong here so I can get this working?

EDIT #1

Here is the updated jQuery code:

$(".AddToCart").click(function () { //first disable the button to prevent double clicks $(this).prop("disbled", true).prop("value", "Adding..."); $("#sending").css("display", "block"); var td = $(this).closest('td') //traverse DOM and find relevant element var price = parseFloat(td.find(".divPrice").prop("id")), quantity = parseInt(td.find("#quantity").val()), id = parseInt(td.find(".divId").prop("id")); $.ajax({ url: "@Url.Action("AddToCartAJAX", "Orders")", type: "POST", data: { "id": id, "quantity": quantity, "price": price }, //if successful success: function (data) { successfulCall() }, error: function (data) { errorCall(data); } }); 

It worked before making the client side changes (granted only once and only for the first item in the list), since I havent changed the server side code what could have gone wrong? EDIT #2

Here is the whole thing in it's entirity

@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 id="sending" style="display:none;"><img src="~/Content/ajax-loader.gif" /></div> <div style="color:red" id="ItemAdded"></div> <div class="scroll"> @foreach (var item in Model) { <div class="scroll2"> <div class="itemcontainer"> <table> <tr> <td id="@item.Id" class="divId"> <div class="DetailsLink" id="@item.Id"> &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 { @style = "width:50px;", @class = "formTextBox quantity" })</div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="button" value="AddToCart" class="btn btn-default AddToCart" /> </div> </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 () { //first disable the button to prevent double clicks $(this).prop("disbled", true).prop("value", "Adding..."); $("#sending").css("display", "block"); var td = $(this).closest('td') //traverse DOM and find relevant element var price = parseFloat(td.find(".divPrice").prop("id")), quantity = parseInt(td.find(".quantity").val()), id = parseInt(td.find(".divId").prop("id")); $.ajax({ url: "@Url.Action("AddToCartAJAX", "Orders")", type: "POST", data: { "id": id, "quantity": quantity, "price": price }, //if successful success: function (data) { successfulCall() }, error: function (data) { errorCall(data); } }); function successfulCall() { //enable the send button $(this).prop("disbled", false).prop("value", "Add To Cart"); //hide the sending gif $("#sending").css("display", "none"); //display the successful message $("#ItemAdded").text("Your item has been added to your order."); //clear out all the values $("input#quantity").val("0"); } function errorCall(data) { $(this).prop("disbled", false).prop("value", "Add To Cart"); $("#sending").css("display", "none"); $("#ItemAdded").text(data.message); } //alert('Clicked!'); }); //s}); </script> } 

EDIT #2

Here is the code for AddToCartAJAX in OrdersController:

public ActionResult AddToCartAJAX(int id, int quantity, decimal price) { var cart = ShoppingCart.GetCart(this.HttpContext); cart.AddToCart(id, quantity, price); return RedirectToAction("Index"); } 

And AddToCart in ShoppingCrt.cs:

public void AddToCart(int id, int quantity, decimal price) { // Get the matching cart and product instances var order = entities.Orders.FirstOrDefault( c => c.OrderGUID == ShoppingCartId && c.OrderItems.Where(p => p.ProductId == id).FirstOrDefault().ProductId == id); 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 = id, ProductQuantity = quantity, ProductPrice = price }; 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 == id).FirstOrDefault().ProductQuantity += quantity; } } 

Hope that helps

5
  • He is someone who was helping me with another issue. Commented Aug 4, 2015 at 6:32
  • yes I added AddToCart to the class of the buttons lilke you suggested. Commented Aug 4, 2015 at 7:12
  • and quantity class to @Html.TextBoxFor(modelItem => item.Quantity? also td.find(".quantity").val() and do make change in sucess and error method for correct selectors Commented Aug 4, 2015 at 7:13
  • @Satpal check edit #2 Commented Aug 4, 2015 at 7:26
  • 1
    $(this) in successfulCall and errorCall doesn't refer to .AddToCart use $(".AddToCart") instead Commented Aug 4, 2015 at 7:28

1 Answer 1

3

As you are creating elements in a loop, duplicate identifiers are created. Which makes your HTML invalid.

Identifiers in HTML must be unique. and this is expected behaviour.

You can assign a common class then can use class selector. Here in example I have added AddToCart CSS class to the button.

HTML

@Html.TextBoxFor(modelItem => item.Quantity, new {@style = "width:50px;", @class = "formTextBox quantity" }) <input type="button" value="AddToCart" class="btn btn-default AddToCart" /> 

Script

$(".AddToCart").click(function() { $(this).prop("disabled", true) .prop("value", "Adding..."); var td = $(this).closest('td.divId') //traverse DOM and find relevant element var price = parseFloat(td.find(".divPrice").prop("id")), quantity = parseInt(td.find(".quantity").val()), id = parseInt(td.find(".divId").prop("id")); //Your ajax call }); 

I would also recommend you to use data-* prefixed custom attributes to store arbitrary data.

 <div class="divPrice" data-price="@item.Price"></div> 

Which can be fetched using .data(), it also converts value to appropriate type.

 var price = $('.divPrice').data("price"); 

Note: Change the rest of selectors accordingly.

Sign up to request clarification or add additional context in comments.

7 Comments

I implemented your changes into my existing code, but now when it hits my ajax call I get a 500 (Internal Server Error), I've checked my permissions and they seem to be correct.
@PsychoCoder, Its nothing to do with permission and as per suggestion you have to do lot of changes to achieve the result and 500 means something is not right at server side.
can you check my edit please. I know it's got to be something simple I'm missing.
@PsychoCoder, As Satpal noted, a 500 error is a result of an exception on the serer. You need to show your controller code.
@PsychoCoder, return RedirectToAction("Index") will not work with $.ajax() this will help you => stackoverflow.com/a/17745088
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.