Cart
The main way of working with the cart in SALVO-TS is via the Cart singleton object.
You can access the cart instance by importing it from @/theme.
typescriptimport { cart } from '@/theme';await cart.addItems(..)
typescriptimport { cart } from '@/theme';await cart.addItems(..)
Accessing items
All items
typescriptfor (let item of cart.items) {log.debug('got item', item);}
typescriptfor (let item of cart.items) {log.debug('got item', item);}
By line
typescriptconst item = cart.items[4];
typescriptconst item = cart.items[4];
Items with SKU
typescriptconst bracelets = cart.queryItems({sku: 'bracelet'});
typescriptconst bracelets = cart.queryItems({sku: 'bracelet'});
Items of product or variant
typescriptconst selectionPacks = cart.queryItems({productId: 1243456789});const chocolateFlavours = cart.queryItems({variantId: 5688912344});
typescriptconst selectionPacks = cart.queryItems({productId: 1243456789});const chocolateFlavours = cart.queryItems({variantId: 5688912344});
Items with properties
typescriptconst giftItems = cart.queryItems({hasProperties: ['_gift_message']});
typescriptconst giftItems = cart.queryItems({hasProperties: ['_gift_message']});
Items with property values
typescriptconst fitmentProducts = cart.queryItems({properties: {_install_method: 'fitment'}});
typescriptconst fitmentProducts = cart.queryItems({properties: {_install_method: 'fitment'}});
Adding items
Individually, by variant ID:
typescriptawait cart.addItem({id: 124567890})
typescriptawait cart.addItem({id: 124567890})
Individually, with qty and attributes:
typescriptawait cart.addItem({id: 124567890,quantity: 4,attributes: {_gift_message: 'Enjoy!',}});
typescriptawait cart.addItem({id: 124567890,quantity: 4,attributes: {_gift_message: 'Enjoy!',}});
In bulk:
typescriptawait cart.addItems([{id: 1234567890},{id: 1234567890, quantity: 2},{id: 1234567890quantity: 4,attributes: {_gift_message: 'Enjoy!',}},]);
typescriptawait cart.addItems([{id: 1234567890},{id: 1234567890, quantity: 2},{id: 1234567890quantity: 4,attributes: {_gift_message: 'Enjoy!',}},]);
Cart note and attributes
Accessing and updating cart note:
typescriptconst note = cart.note;if (!note) {await cart.updateNote(`The customer didn't specify any note`);}
typescriptconst note = cart.note;if (!note) {await cart.updateNote(`The customer didn't specify any note`);}
Accessing and updating cart attributes:
typescriptconst attributes = cart.attributes;const newAttributes = {...attributes,_vat_number: 123456,};await cart.updateAttributes(newAttributes);
typescriptconst attributes = cart.attributes;const newAttributes = {...attributes,_vat_number: 123456,};await cart.updateAttributes(newAttributes);
Examples
Swapping products based on variant ID, maintaining their quantity and properties:
typescriptasync swapProducts(oldVariantId: number, newVariantId: number) {let foundItems = cart.queryItems({variantId: oldVariantId});let newItems = foundItems.map((oldItem: CartItem) => ({id: newVariantId,quantity: oldItem.quantity,properties: oldItem.properties}));for (let item of foundItems) {await item.remove();}await cart.addItems(newItems);}
typescriptasync swapProducts(oldVariantId: number, newVariantId: number) {let foundItems = cart.queryItems({variantId: oldVariantId});let newItems = foundItems.map((oldItem: CartItem) => ({id: newVariantId,quantity: oldItem.quantity,properties: oldItem.properties}));for (let item of foundItems) {await item.remove();}await cart.addItems(newItems);}
A cart item count component:
typescript@Component()class CartCounter extends BaseComponent {@Ref()countSpan: HTMLElement;created() {cart.onChange.subscribe(this.updateCount);this.updateCount();}beforeDestroy() {cart.onChange.unsubscribe(this.updateCount);}updateCount() {this.countSpan.textContent = cart.items.length;}}
typescript@Component()class CartCounter extends BaseComponent {@Ref()countSpan: HTMLElement;created() {cart.onChange.subscribe(this.updateCount);this.updateCount();}beforeDestroy() {cart.onChange.unsubscribe(this.updateCount);}updateCount() {this.countSpan.textContent = cart.items.length;}}
Method & property listing
typescriptclass Cart {items: CartItem[];attributes: CartAttributes;note: string;token: string;totalPrice: number;totalWeight: number;requiresShipping: boolean;currency: string;onChange: Event<void>;// Adds items to the cartaddItem(item: CartItemInput): Promise<void>;addItems(items: CartItemInput[]): Promise<void>;// Update the cartupdateNote(note: string): Promise<void>;updateAttributes(attributes: CartAttributes): Promise<void>;update(node: null|string, attributes: null|CartAttributes): Promise<void>;// Updating item quantities using this method (/cart/update.js)// is unsafe when attributes are used -- consider using the// change() methods on the individual items instead!updateItemQuantitiesByVariantId(updates: {[variantId: number]: number}): Promise<void>;// Find items by queryqueryItems(query: CartItemQuery): Promise<void>;// Empty the cartclear(): Promise<void>;}class CartItem {id: number;variantId: number;quantity: number;properties: CartAttributes;productId: number;key: string;title: string;price: number;// ... all of the properties you find on cart items, in camelCase// see https://shopify.dev/docs/themes/ajax-api/reference/cart#get-cart-js// Update the item in the cartchangeQuantity(quantity: number): Promise<void>;changeProperties(properties: CartAttributes); Promise<void>;change(quantity: null|number, properties: null|CartAttributes): Promise<void>;// Remove items from the cartremove(): Promise<void>;}type CartAttributes = Record<string, string|number|boolean>interface CartItemInput {id: number;quantity?: number;attributes: Record<string, string|number|boolean>;}interface CartItemQuery {variantId?: number;productId?: number;sku?: string;hasProperties?: string[];properties?: CartAttributes;}
typescriptclass Cart {items: CartItem[];attributes: CartAttributes;note: string;token: string;totalPrice: number;totalWeight: number;requiresShipping: boolean;currency: string;onChange: Event<void>;// Adds items to the cartaddItem(item: CartItemInput): Promise<void>;addItems(items: CartItemInput[]): Promise<void>;// Update the cartupdateNote(note: string): Promise<void>;updateAttributes(attributes: CartAttributes): Promise<void>;update(node: null|string, attributes: null|CartAttributes): Promise<void>;// Updating item quantities using this method (/cart/update.js)// is unsafe when attributes are used -- consider using the// change() methods on the individual items instead!updateItemQuantitiesByVariantId(updates: {[variantId: number]: number}): Promise<void>;// Find items by queryqueryItems(query: CartItemQuery): Promise<void>;// Empty the cartclear(): Promise<void>;}class CartItem {id: number;variantId: number;quantity: number;properties: CartAttributes;productId: number;key: string;title: string;price: number;// ... all of the properties you find on cart items, in camelCase// see https://shopify.dev/docs/themes/ajax-api/reference/cart#get-cart-js// Update the item in the cartchangeQuantity(quantity: number): Promise<void>;changeProperties(properties: CartAttributes); Promise<void>;change(quantity: null|number, properties: null|CartAttributes): Promise<void>;// Remove items from the cartremove(): Promise<void>;}type CartAttributes = Record<string, string|number|boolean>interface CartItemInput {id: number;quantity?: number;attributes: Record<string, string|number|boolean>;}interface CartItemQuery {variantId?: number;productId?: number;sku?: string;hasProperties?: string[];properties?: CartAttributes;}
Extending the cart interface
You can extend the cart, for example to add new utilty functions, by editing the theme/Cart.ts file.
typescript// theme/cart.jsimport { BaseCart, CartItem, CartItemOptions } from 'salvo-ts';class Cart extends BaseCart {/*** Adds an item to the cart and marks it as a gift.* The gift message will be attached as the _gift_message attribute.*/async addGiftItem(variantId: number, giftMessage: string, opts: CartItemOptions) {return await this.addItems([new CartItem(variantId, {...opts,attributes: {...opts.attributes,_gift_message: giftMessage,}}),]);}}// usageawait cart.addGiftItem(123456789, 'Enjoy!', {quantity: 2});
typescript// theme/cart.jsimport { BaseCart, CartItem, CartItemOptions } from 'salvo-ts';class Cart extends BaseCart {/*** Adds an item to the cart and marks it as a gift.* The gift message will be attached as the _gift_message attribute.*/async addGiftItem(variantId: number, giftMessage: string, opts: CartItemOptions) {return await this.addItems([new CartItem(variantId, {...opts,attributes: {...opts.attributes,_gift_message: giftMessage,}}),]);}}// usageawait cart.addGiftItem(123456789, 'Enjoy!', {quantity: 2});
Extending queryItems
You can extend the queryItems method by calling addQueryType, and augmenting the CartItemQuery interface:
typescript// types.d.tsmodule 'salvo-ts' {interface CartItemQuery {skuSuffix: string;}}// theme/cart.tsimport {BaseCart, CartItem, CartItemOptions, CartItemQuery} from 'salvo-ts';export default class Cart extends BaseCart {constructor() {super();// Add a new query option which will match items based on a SKU suffixthis.addQueryType('skuSuffix', (items: CartItem[], suffix: string) => {return items.filter((item: CartItem) => item.sku.endsWith(suffix));});}}// usageconst bulkItems = cart.queryItems({skuSuffix: '-PALLET'});
typescript// types.d.tsmodule 'salvo-ts' {interface CartItemQuery {skuSuffix: string;}}// theme/cart.tsimport {BaseCart, CartItem, CartItemOptions, CartItemQuery} from 'salvo-ts';export default class Cart extends BaseCart {constructor() {super();// Add a new query option which will match items based on a SKU suffixthis.addQueryType('skuSuffix', (items: CartItem[], suffix: string) => {return items.filter((item: CartItem) => item.sku.endsWith(suffix));});}}// usageconst bulkItems = cart.queryItems({skuSuffix: '-PALLET'});
Handling external updates
In most cases, SALVO-TS will automatically detect when third parties (i.e. apps) modify the cart and will automatically reload the cart state.
If this fails for some reason, you can manually notify the cart by calling notifyCartUpdatedExternally().