Extending Swift JavaScript modules

You can extend the Swift JavaScript module by subscribing to events – either all events of a particular type or only events fired by a specific element:

  • All events: document.addEventListener("update.swift.cart", ... 
  • A specific element: document.querySelector(“#DetailsInfoAddToCart”).addEventListener("update.swift.cart", ... 

The following events are available:

  • Cart.js 
    • update.swift.cart (detail.formData, detail.parentEvent) 
    • updated.swift.cart (detail.formData, detail.html) 
  • Productlist.js
    • update.swift.productlist (detail.formData, detail.parentEvent) 
    • updated.swift.productlist (detail.formData, detail.html) 
    • resetfacets.swift.productlist (detail.formData, detail.parentEvent) 
  • Pageupdater.js 
    • update.swift.pageupdater (detail.formData, detail.parentEvent) 
    • updated.swift.pageupdater (detail.formData, detail.parentEvent) 
  • VariantSelector.js 
    • optionclick.swift.variantselector (detail.parentEvent) 
    • selectioncomplete.swift.variantselector (detail.selections) 

It is also possible to completely take over an event from Swift – for instance, if you want to replace the swift.Cart.Update method you can add event.preventDefault() to your update.swift.cart event listender.

In this example you want to make really sure the customer knowns what’s been added to cart, so you want to notify the customer whenever an add to cart button is clicked (Figure 2.1).

Figure 2.1 A toast

To make this happen:

  1. First take a look at Toasts from Bootstrap – the lightweight notifications we will use to solve this
  2. Open the Files/Templates/Designs/Swift/Paragraph/Swift_ProductDetailsInfo.cshtml template – this is the template which typically contains an add to cart button
  3. At the bottom of this template add a notification – note the IDs on the elements where we want to change the content:
HTML
<div aria-live="polite" aria-atomic="true"> <div class="toast-container position-fixed bottom-0 end-0 p-3"> <div id="cartNotificationToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true"> <div class="toast-header"> <strong id="cartNotificationToastHeader" class="me-auto">@Translate("Added to cart")</strong> <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button> </div> <div class="toast-body d-flex gap-3 theme theme-gray"> <img id="cartNotificationToast_Image" src="" /> <div id="cartNotificationToast_Text"></div> </div> </div> </div> </div>

Next, create an event listener hooking into the update.swift.cart event:

JS
<script type="text/javascript"> document.addEventListener("update.swift.cart", function (event) { var cartNotification = document.querySelector("#cartNotificationToast"); var data = Object.fromEntries(event.detail.formData.entries()); var toast = new bootstrap.Toast(cartNotification); if (data.Quantity == "1") { document.querySelector("#cartNotificationToastHeader").innerHTML = data.Quantity + " " + '@Translate("Product added to cart")'; } else { document.querySelector("#cartNotificationToastHeader").innerHTML = data.Quantity + " " + '@Translate("Products added to cart")'; } if (data.Thumbnail != "") { document.querySelector("#cartNotificationToast_Image").src = data.Thumbnail; } document.querySelector("#cartNotificationToast_Text").innerHTML = data.ProductName; toast.show(); }); </script>

In the notification we want to show the name and image of the product just added to cart, but we need to make it available for the notification. You can start out by looking at the data currently available:

  1. Open the Product Details page in frontend
  2. Open the DevTools (F12 in Chrome) and switch to the Network tab
  3. Check Fetch/XHR and add a product to cart
  4. Click on the request and scroll to the Form Data section ({figureref)
  5. You should see a number of properties – e.g. ProductId, cartcmd, and Quantity – matching the Add to cart form in the ProductDetailsInfo.cshtml template

To add the name/image data to the form you have two options.

The first option is to add hidden fields with the data to the form:

RAZOR
<input type="hidden" name="ProductName" value="@product.Name" /> @if (product.DefaultImage != null) { <input type="hidden" name="Thumbnail" value="/Admin/Public/GetImage.ashx?image=@(product.DefaultImage.Value)&width=80&format=webp" /> }

The second option is to extend formData directly in the eventlistener:

JS
event.detail.formData.set("ProductName", "@(product.Name)");

Whichever option you choose, the script will:

  1. Listen to the ”update.swift.cart” event
  2. Take the formData entries and convert them to an object
  3. Adds the data to the Toast-markup
  4. Call the toast.show() command

Short story long, this will cause a small notification to appear in the lower right corner when products are added to cart.

Another common scenario is a need for displaying a notification before adding a product to cart, e.g. if the quantity selected is greater than the quantity in stock.

  1. First take a look at the Bootstrap 5 Modals documentation as we will use a model in this example
  2. At the bottom of the ProductDetailsInfo.cshtml template add a modal:
HTML
<div id="cartNotificationModal" class="modal" tabindex="-1"> <div class="modal-dialog theme light"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">@Translate("We do not have the selected quantity in stock")</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div id="cartNotificationModalBody" class="modal-body"></div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">@Translate("Cancel")</button> <button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="ConfirmNewQuantity()">@Translate("Confirm")</button> </div> </div> </div> </div>

In this scenario we need to pass information about the current stock level to the modal – to do this add a hidden field to the add-to-cart form:

HTML
<input type="hidden" name="StockQuantity" value="@product.StockLevel" />

Finally create the event listener and a method for confirming that you want to add to cart despite the low stock. As should be apparent from the example, we’re actually using an event.preventDefault() method to cancel the add to cart call when the quantity is higher than the stock level. Then, if the add to cart action is confirmed, we add the stock actually available to cart instead.

JS
<script> document.addEventListener("update.swift.cart", function (event) { var cartNotification = document.querySelector("#cartNotificationModal"); var modal = new bootstrap.Modal(cartNotification); var data = Object.fromEntries(event.detail.formData.entries()); if (data.Quantity > data.StockQuantity) { event.preventDefault(); document.querySelector("#cartNotificationModalBody").innerHTML = data.StockQuantity + " " + "@Translate("is available in stock")" + ". " + "@Translate("Do you want to add them to the cart?")"; var quantitySelector = document.querySelector("#Quantity_@(product.Id)"); quantitySelector.value = data.StockQuantity; modal.show(); } }); ConfirmNewQuantity = function () { var cartNotification = document.querySelector("#cartNotificationModal"); var modal = new bootstrap.Modal(cartNotification); var addToCartButton = document.querySelector("#AddToCartButton@(product.Id)"); swift.Cart.Update(addToCartButton); } </script>