Skip to main content
Version: 0.1.x

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.

typescript
import { cart } from '@/theme';
await cart.addItems(..)
typescript
import { cart } from '@/theme';
await cart.addItems(..)

Accessing items

All items

typescript
for (let item of cart.items) {
log.debug('got item', item);
}
typescript
for (let item of cart.items) {
log.debug('got item', item);
}

By line

typescript
const item = cart.items[4];
typescript
const item = cart.items[4];

Items with SKU

typescript
const bracelets = cart.queryItems({sku: 'bracelet'});
typescript
const bracelets = cart.queryItems({sku: 'bracelet'});

Items of product or variant

typescript
const selectionPacks = cart.queryItems({productId: 1243456789});
const chocolateFlavours = cart.queryItems({variantId: 5688912344});
typescript
const selectionPacks = cart.queryItems({productId: 1243456789});
const chocolateFlavours = cart.queryItems({variantId: 5688912344});

Items with properties

typescript
const giftItems = cart.queryItems({hasProperties: ['_gift_message']});
typescript
const giftItems = cart.queryItems({hasProperties: ['_gift_message']});

Items with property values

typescript
const fitmentProducts = cart.queryItems({properties: {_install_method: 'fitment'}});
typescript
const fitmentProducts = cart.queryItems({properties: {_install_method: 'fitment'}});

Adding items

Individually, by variant ID:

typescript
await cart.addItem({id: 124567890})
typescript
await cart.addItem({id: 124567890})

Individually, with qty and attributes:

typescript
await cart.addItem({
id: 124567890,
quantity: 4,
attributes: {
_gift_message: 'Enjoy!',
}
});
typescript
await cart.addItem({
id: 124567890,
quantity: 4,
attributes: {
_gift_message: 'Enjoy!',
}
});

In bulk:

typescript
await cart.addItems([
{id: 1234567890},
{id: 1234567890, quantity: 2},
{
id: 1234567890
quantity: 4,
attributes: {
_gift_message: 'Enjoy!',
}
},
]);
typescript
await cart.addItems([
{id: 1234567890},
{id: 1234567890, quantity: 2},
{
id: 1234567890
quantity: 4,
attributes: {
_gift_message: 'Enjoy!',
}
},
]);

Cart note and attributes

Accessing and updating cart note:

typescript
const note = cart.note;
if (!note) {
await cart.updateNote(`The customer didn't specify any note`);
}
typescript
const note = cart.note;
if (!note) {
await cart.updateNote(`The customer didn't specify any note`);
}

Accessing and updating cart attributes:

typescript
const attributes = cart.attributes;
const newAttributes = {
...attributes,
_vat_number: 123456,
};
await cart.updateAttributes(newAttributes);
typescript
const 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:

typescript
async 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);
}
typescript
async 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

typescript
class Cart {
items: CartItem[];
attributes: CartAttributes;
note: string;
token: string;
totalPrice: number;
totalWeight: number;
requiresShipping: boolean;
currency: string;
onChange: Event<void>;
// Adds items to the cart
addItem(item: CartItemInput): Promise<void>;
addItems(items: CartItemInput[]): Promise<void>;
// Update the cart
updateNote(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 query
queryItems(query: CartItemQuery): Promise<void>;
// Empty the cart
clear(): 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 cart
changeQuantity(quantity: number): Promise<void>;
changeProperties(properties: CartAttributes); Promise<void>;
change(quantity: null|number, properties: null|CartAttributes): Promise<void>;
// Remove items from the cart
remove(): 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;
}
typescript
class Cart {
items: CartItem[];
attributes: CartAttributes;
note: string;
token: string;
totalPrice: number;
totalWeight: number;
requiresShipping: boolean;
currency: string;
onChange: Event<void>;
// Adds items to the cart
addItem(item: CartItemInput): Promise<void>;
addItems(items: CartItemInput[]): Promise<void>;
// Update the cart
updateNote(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 query
queryItems(query: CartItemQuery): Promise<void>;
// Empty the cart
clear(): 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 cart
changeQuantity(quantity: number): Promise<void>;
changeProperties(properties: CartAttributes); Promise<void>;
change(quantity: null|number, properties: null|CartAttributes): Promise<void>;
// Remove items from the cart
remove(): 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.js
import { 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,
}
}),
]);
}
}
// usage
await cart.addGiftItem(123456789, 'Enjoy!', {quantity: 2});
typescript
// theme/cart.js
import { 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,
}
}),
]);
}
}
// usage
await 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.ts
module 'salvo-ts' {
interface CartItemQuery {
skuSuffix: string;
}
}
// theme/cart.ts
import {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 suffix
this.addQueryType('skuSuffix', (items: CartItem[], suffix: string) => {
return items.filter((item: CartItem) => item.sku.endsWith(suffix));
});
}
}
// usage
const bulkItems = cart.queryItems({skuSuffix: '-PALLET'});
typescript
// types.d.ts
module 'salvo-ts' {
interface CartItemQuery {
skuSuffix: string;
}
}
// theme/cart.ts
import {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 suffix
this.addQueryType('skuSuffix', (items: CartItem[], suffix: string) => {
return items.filter((item: CartItem) => item.sku.endsWith(suffix));
});
}
}
// usage
const 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().