UpdatePanel in Shopping Cart

Lorenz Lammersdorf asked on August 24, 2018 15:11

Hi guys,

I assume this is not necessarily a problem of Kentico, but maybe someone can kick me in the right direction.

My cart should look like this:

Cart

The transformation code looks like below. In principle this works, I can increasse / decrease the number of units, I can add more items and I can remove items, but if the item to remove is not the last one in the list the item is removed from the database but not from the view.

Any hints?

<div class="cart-item">
    <div class="title-left"><%# GetZiProductName(SKU.SKUID) %></div>
    <div class="product-thumb<% if ((SKU.SKUName.StartsWith("MF")) || (SKU.SKUName.StartsWith("HD"))) { %> halfsize <% } %>">
        <%# IfEmpty(SKU.SKUImagePath, OptionIcon(Eval<int>("SKU.SKUID")), "<a href='" + GetSKUUrl() + "'>" + ResponsiveImage(SKU.SKUImagePath, SKU.SKUName, "480") + "</a>") %>

        <asp:UpdatePanel ID="updPanel0" runat="server" UpdateMode="Always">
            <ContentTemplate>
            <div class='buttons'>
                <asp:Button ID="Button0" runat="server" oncommand="Button1_Click" Text="" CssClass="remove" CommandName='<%# Eval("SKU.SKUID") %>' CommandArgument='1' />
            </div>
            </ContentTemplate>
        </asp:UpdatePanel>
    </div>
    <asp:UpdatePanel ID="updPanel" runat="server" UpdateMode="Always">
    <ContentTemplate>
    <div class="info">
        <div class="title"><%# GetZiProductName(SKU.SKUID) %></div>
        <div class="cnt">
            <asp:Button ID="Button1" runat="server" oncommand="Button1_Click" Text="" CssClass="dec" CommandName='<%# Eval("SKU.SKUID") %>' CommandArgument='<%# GetCartUnits(Eval<int>("SKU.SKUID")) %>' />
                <div class="number"><div class="n"><%# GetCartUnits(Eval<int>("SKU.SKUID")) %></div></div>
            <asp:Button ID="Button2" runat="server" oncommand="Button2_Click" Text="" CssClass="inc" CommandName='<%# Eval("SKU.SKUID") %>' CommandArgument='<%# GetCartUnits(Eval<int>("SKU.SKUID")) %>' />
        </div>
        <div class="price">
            <div class="price-part">
                <% if(EvalDouble("UnitTotalPriceIncludingOptions") > 0) { %>
                    <div class="op op-x">x</div><div class="price-single"><%# FormatPrice(EvalDouble("UnitTotalPriceIncludingOptions"))%></div><%=cartitemunitprice%>
                <% } else { %>
                    <div class="price-none">Price on request</div>
                <% } %>
            </div>  
        </div>
    </div>
    </ContentTemplate>
    </asp:UpdatePanel>                      
</div>

<script runat="server">
protected string cartitemunitprice {
    get {
        if (GetCartUnits(Eval<int>("SKU.SKUID")) > 1) {
            return "<div class='price-part'><div class='op op-eq'>=</div><div class='price-total'>" + FormatPrice(EvalDouble("UnitTotalPriceIncludingOptions")*GetCartUnits(Eval<int>("SKU.SKUID"))) + "</div></div>";
        } else {
            return String.Empty;
        }
    }
    set {
        ViewState["cartitemunitprice"] = value;
    }
}

protected void Button1_Click(object sender, CommandEventArgs e)       
{      
    ZiShoppingCartUpdate.RemoveItem(sender, e, e.CommandName.ToString(), e.CommandArgument.ToString());
}

protected void Button2_Click(object sender, CommandEventArgs e)       
{      
    ZiShoppingCartUpdate.AddItem(sender, e, e.CommandName.ToString(), e.CommandArgument.ToString());
}
</script>

Recent Answers


Chris Bass answered on August 24, 2018 16:01 (last edited on August 24, 2018 16:11)

A few things I'd check based on some stuff I recently saw on my own project:

1) I see that your buttons have updatepanels around them, but those updatepanels don't include the rest of the cart-item row. So when the page updates, I believe the buttons will update (thus the numbers will update), but the rest of the row will not.

2) Given the above, one reason it could work on 'removing the last item' is, are you using the code-behind logic from the ShoppingCartContent web part? That web part specifically has logic that says 'refresh the entire page if the shopping cart becomes empty'.

As a potential consequence of this, while the cart page is open, in a separate window, add a new item, then update the quantity of one of the items in the cart (or delete them). I bet the new item doesn't show up.

I'd recommend putting the updatepanel around the entirety of the shopping cart repeater, or at the very least the whole row. The advantage of putting it around the whole repeater is that when the cart is empty, you can either get rid of the cart's container, or at least show a 'your cart is empty' message.

0 votesVote for this answer Mark as a Correct answer

Lorenz Lammersdorf answered on August 24, 2018 16:05

Hi Chris,

I've rewritten the code behind (but I've used most of the default shopping cart code). Deleting the last item in cart works, if the cart becomes empty it does a refresh.

I tried to put the whole item transformation in an UpdatePanel, but this works even worse (the system needs much much longer process the event, the images are not displayed after the update [this is related to the way we load the images] and the result is the same).

0 votesVote for this answer Mark as a Correct answer

Chris Bass answered on August 24, 2018 16:21

Ok. So, you said yes you're using the 'refresh the page on empty cart' logic from the basic shopping cart, which disregards update panels. So that means the 'removing last item' case isn't likely relevant to the 'why is the row not refreshing the rest of the time' question.

Basically, without being able to see the full web part ascx and ascx.cs, there isn't much more to go on here. You clearly have UpdatePanels that are working as designed (they constrain the postback so only the things inside them refresh). You want the rest of the web part to update, so those need to not be there. If that causes the event to be slow, that's a separate concern to look at.

One last thing to check would be if you're correctly firing/handling the SHOPPING_CART_CHANGED event, I suppose.

0 votesVote for this answer Mark as a Correct answer

Lorenz Lammersdorf answered on August 24, 2018 16:25

Here we go:

using CMS.Ecommerce;
using CMS.Ecommerce.Web.UI;
using CMS.Helpers;
using System;
using System.IO;

/// <summary>
/// Custom item increase / decrease buttons
/// </summary>
public partial class ZiShoppingCartUpdate : CMSCheckoutWebPart
{
    private static ShoppingCartItemInfo GetCartItem(string skuid, ShoppingCartInfo cart)
    {
        // Get the product
        SKUInfo product = SKUInfoProvider.GetSKUs().WhereEquals("SKUID", Int32.Parse(skuid)).FirstObject;

        // Prepares the shopping cart item
        ShoppingCartItemInfo item = null;

        // Loops through the items in the shopping cart
        foreach (ShoppingCartItemInfo cartItem in cart.CartItems)
        {
            // Gets the first shopping cart item matching the specified product
            if (cartItem.SKUID == product.SKUID)
            {
                item = cartItem;
                break;
            }
        }

        return item;
    }

    private static void InvalidateCart(object sender, EventArgs e, ShoppingCartInfo cart)
    {
        // Invalidates the shopping cart's calculated values (to trigger recalculation of discounts, shipping, price totals, etc).
        cart.InvalidateCalculations();
        ShoppingCartInfoProvider.EvaluateShoppingCart(cart);
        ComponentEvents.RequestEvents.RaiseEvent(sender, e, SHOPPING_CART_CHANGED);
    }

    public static void AddItem(object sender, EventArgs e, string skuid, string ciu)
    {

        int cartitemunits = Int32.Parse(ciu);
        int newciu = cartitemunits + 1;

        // Get the current shopping cart
        ShoppingCartInfo cart = ECommerceContext.CurrentShoppingCart;
        ShoppingCartItemInfo item = GetCartItem(skuid, cart);

        if (item != null)
        {
            // Increase the number of units by 1
            ShoppingCartItemInfoProvider.UpdateShoppingCartItemUnits(item, newciu);
            InvalidateCart(sender, e, cart);
        }
    }

    public static void RemoveItem(object sender, EventArgs e, string skuid, string ciu)
    {
        int cartitemunits = Int32.Parse(ciu);
        int newciu = cartitemunits - 1;

        // Get the current shopping cart
        ShoppingCartInfo cart = ECommerceContext.CurrentShoppingCart;
        ShoppingCartItemInfo item = GetCartItem(skuid, cart);

        if (item != null)
        {
            // units = 0 -> delete item from cart
            if (newciu == 0)
            {
                // Delete all the children from the database if available        
                foreach (ShoppingCartItemInfo scii in cart.CartItems)
                {
                    if ((scii.CartItemBundleGUID == item.CartItemGUID) || (scii.CartItemParentGUID == item.CartItemGUID))
                    {
                        ShoppingCartItemInfoProvider.DeleteShoppingCartItemInfo(scii);
                    }
                }

                // Removes the item from the shopping cart
                ShoppingCartInfoProvider.RemoveShoppingCartItem(cart, item.CartItemGUID);

                // Removes the item from the database
                ShoppingCartItemInfoProvider.DeleteShoppingCartItemInfo(item);

                if (cart.TotalUnits == 0)
                {
                    ShoppingCartInfoProvider.DeleteShoppingCartInfo(ECommerceContext.CurrentShoppingCart);
                    ECommerceContext.CurrentShoppingCart = null;
                }
            }
            else
            {
                // Decrease the number of units by 1
                ShoppingCartItemInfoProvider.UpdateShoppingCartItemUnits(item, newciu);
            }

            InvalidateCart(sender, e, cart);
        }
    }
}

nice dog, btw.

0 votesVote for this answer Mark as a Correct answer

Chris Bass answered on August 24, 2018 16:54 (last edited on August 24, 2018 16:55)

Mind if I ask what the reason you're writing all of this custom is, rather than using the existing shopping cart control?

Some speed things: It looks like you're reaching out to the database a lot instead of using the EcommerceContext.CurrentShoppingCart as is. Like in

 // Get the product
    SKUInfo product = SKUInfoProvider.GetSKUs().WhereEquals("SKUID", Int32.Parse(skuid)).FirstObject;

    // Prepares the shopping cart item
    ShoppingCartItemInfo item = null;

    // Loops through the items in the shopping cart
    foreach (ShoppingCartItemInfo cartItem in cart.CartItems)
    {
        // Gets the first shopping cart item matching the specified product
        if (cartItem.SKUID == product.SKUID)
        {
            item = cartItem;
            break;
        }
    }

    return item;

Consider instead adding a 'using System.Linq;' and then replacing all of that with return cart.CartItems.First(item => item.SKUID == Int32.Parse(skuid)). Which saves you the DB call.

Then for Add, you just replace the entire call with ShoppingCartItemInfoProvider.UpdateShoppingCartItemUnits(cartItemInfo, cartItemInfo.CartItemUnits + 1);

See the "CartItemUnits" control in CMSModules/Ecommerce/Controls/Checkout for examples. You'd similarly be able to update the "Remove" method. (along with some logic to delete the item if its quantity reaches 0)

(Also I assume you're not using Product Options or bundles, since this code isn't adding those into the cart when you say 'add')

If you resolve the speed issues here you may not actually need to have the updatepanels at all.

1 votesVote for this answer Mark as a Correct answer

   Please, sign in to be able to submit a new answer.