In this blog post, we'll delve into the process of creating a shopping cart using Alpine.js. We'll explore how to structure components, manage state with Alpine's store, and implement essential features such as fetching new products, adding and removing items, calculating total price, and handling UI interactions.
Content
Alpine.js stands out as a lightweight yet powerful tool, perfect for developers who want to add interactivity to their web pages without the overhead of a full-fledged framework, also it is great for SEO.
Allow me to show you!
Create the main Component
Let's define an Alpine.js component called shoppingCartProductsList
, it will manage the state and behavior of a shopping cart product list, including loading products from an API, handling pagination, and toggling the visibility of the shopping cart:
Component Initialization
Alpine.data('shoppingCartProductsList', (): ShoppingCartProductsList => ({}))
Alpine.data()
defines a new Alpine.js component named shoppingCartProductsList
. It returns an object that contains the state and methods for this component.
State Variables
toggleCart: false,
loading: false,
products: [],
limit: 10,
page: 1,
-
toggleCart
: Controls the visibility of the shopping cart (initially hidden). -
loading
: Indicates whether the component is currently loading data from the API. -
products
: An array to store the list of products fetched from the API. -
limit
: Specifies the number of products to fetch per page. -
page
: Tracks the current page number for pagination.
Initialization (init method)
async init() {
this.loading = true
const res = await axios.get<ProductsGetRes>('https://dummyjson.com/products?limit=10&skip=0')
this.products = res.data.products
this.loading = false
},
It is an asynchronous method that runs when the component is initialized. It fetches the first batch of products from the API using Axios and stores them in the products
array. The loading state is set to true while the data is being fetched, and then reset to false once the data is loaded.
Pagination (paginate method)
async paginate() {
if (this.loading) return
this.loading = true
this.page += 1
const skip = (this.page - 1) * this.limit;
const res = await axios.get<ProductsGetRes>(`https://dummyjson.com/products?limit=${this.limit}&skip=${skip}`)
this.products = [...this.products, ...res.data.products]
this.loading = false
},
Another asynchronous method for loading more products as the user navigates through pages by clicking the load more
button. It first checks if the component is already loading to prevent multiple requests.
The page number is incremented, and the skip value is calculated to determine which products to fetch.
The newly fetched products are appended to the existing products
array.
Toggle Cart (toggleCartHandler method)
toggleCartHandler() {
this.toggleCart = !this.toggleCart
if (this.toggleCart) {
document.body.style.overflowY = 'hidden';
(Alpine.store('shoppingCartTheCart') as ShoppingCartTheCart).currentView = 'cart';
} else {
document.body.style.overflowY = 'scroll'
}
}
A method for toggling the visibility of the shopping cart.
When the cart is toggled on, the page's vertical scroll is disabled by setting overflowY
to hidden
.
It also updates the currentView
property of another Alpine.js store shoppingCartTheCart
to show the cart view. When toggled off, the vertical scroll is restored.
Create the Store
Let's define a store that will manage the state and actions related to the shopping cart, such as adding and removing products, calculating the total price, and handling UI interactions.
Store Initialization
const shoppingCartTheCart: ShoppingCartTheCart = {}
Alpine.store('shoppingCartTheCart', shoppingCartTheCart)
shoppingCartTheCart
is a constant with the type ShoppingCartTheCart
. The object holds the properties and methods necessary to manage the shopping cart. I will use it to register shoppingCartTheCart
as a global store in Alpine.js.
This makes the shopping cart's state and methods accessible from any Alpine.js component within the application.
State Variables
totalPrice: 0,
totalProducts: 0,
products: [],
cartBtnToggler: document.querySelector('#cartBtnToggler')!,
currentView: 'cart',
-
totalPrice
: Tracks the total price of all products in the cart. -
totalProducts
: Tracks the total number of products in the cart. -
products
: An array that stores the products added to the cart. -
cartBtnToggler
: A reference to the HTML element with the ID - -
cartBtnToggler
, used for handling UI interactions (e.g., animations). -
currentView
: A string that indicates the current view of the cart, defaulted tocart
.
Initialization (init method)
init() {
this.cartBtnToggler.addEventListener('animationend', () => {
this.cartBtnToggler.classList.remove('animate__rubberBand')
})
},
This method runs when the shopping cart store is initialized.
It adds an event listener to cartBtnToggler
that listens for the end of an animation.
Once the animation ends, the method removes the animate__rubberBand
class from the button, ensuring that the animation can be re-triggered later.
Adding a Product (addProduct method)
addProduct(newProduct: Product) {
this.totalPrice += newProduct.price
this.totalProducts += 1
const productIndex = this.products.findIndex(product => product.id === newProduct.id)
if (productIndex > -1) {
this.products[productIndex].amount += 1
} else {
this.products.push({ ...newProduct, amount: 1 })
}
this.cartBtnToggler.classList.add('animate__rubberBand')
},
This method adds a new product to the shopping cart. It first increments the totalPrice
and totalProducts
to reflect the new addition.
The method checks if the product is already in the cart using findIndex:
- If the product exists (i.e., productIndex > -1), it increments the amount of that product.
- If the product is new, it is added to the
products
array with an initial amount of 1. - Finally, the method triggers the
animate__rubberBand
animation on the cart button to provide visual feedback.
Removing a Product (removeProduct method)
removeProduct(productToRemove: CartProduct) {
this.totalPrice = Math.round((this.totalPrice - productToRemove.price) * 100) / 100;
this.totalProducts -= 1
const productToRemoveIndex = this.products.findIndex(product => product.id === productToRemove.id)
if (productToRemove.amount === 1) {
this.products.splice(productToRemoveIndex, 1)
} else {
this.products[productToRemoveIndex].amount -= 1
}
}
This method removes a product from the shopping cart.
It first decrements the totalPrice
by the price of the product being removed, rounding it to two decimal places. The totalProducts
count is also decremented. The method finds the product in the products
array using findIndex:
- If the product's amount is 1, it is completely removed from the cart using splice.
- If the product's amount is greater than 1, the amount is simply decremented by 1.
Summary and the source code
I like to use Alpine.js to build cool websites. It is very easy to create components and stores using Alpine.js, which helps a lot in quickly adding interactivity to any web page without hurting SEO.
π Your feedback is much appreciated
Top comments (0)