For discussing the development and usage of Checkout UI extensions, post-purchase extensions, web pixels, Customer Accounts UI extensions, and POS UI extensions
Hi everyone,
I've developed a new app feature that allows customers to add multiple variants to their shopping cart with just one click.
This is quite simple to implement (see the code snippet below for a simplified example).
However, there's a problem with this code: some merchants have theme-specific callbacks on their submit buttons. Some redirects to the cart page, while others open a cart drawer.
In most themes, there are 'buy buttons' blocks included. I'm wondering if it's possible for merchants to use these blocks with my app feature without having to edit their theme code. Unfortunately, in the themes I've looked at, the section and product form ID information isn't passed to app blocks, so I haven't found a way to avoid having to create custom 'Add to Cart' buttons.
Is there any solution? Or the best I can do is allow user to add custom javascript?
<form action='/cart/add'>
<input name="items[0]id" value="id123"/>
<input name="items[0]quantity" value="2"/>
<input name="items[1]id" value="id456"/>
<input name="items[1]quantity" value="4"/>
<button type="submit"/>
</form>
Hi Infalodon,
This is a tricky issue as you found, there's no way to completely ensure that the buy button will trigger a cart drawer or redirect to cart/ checkout. Theme developers could use different techniques to control this, and merchants could change their theme settings that would affect what call-back function is used - so there's no "one-size-fits-all" approach possible here. What you could do is to create offer settings within your app's configuration to allow merchants to specify the IDs of their theme's form elements or any other relevant selectors. This way, your app could dynamically adjust to work with the merchant's specific theme setup? Is this an app that will be on the Shopify App Store?
Liam | Developer Advocate @ Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog
Thank your for your answer
Indeed, ask to merchant for form id would be a good solution, hoping it's not a dynamic one. I would try it out!
This is a shopify app that's already available on the Shopify App Qtore yes!
Best solution I've found is the following one:
1. I allow merchants to configure a form selector. By default I use
'form:has([name="id"]):has(button[type="submit"])'
2. Then, I disabled the `name='id'` input and inject variants into the product form
export interface ShopifyCartItem {
id: string;
quantity: number;
properties?: { [key: string]: string };
}
/**
* Updates the product form with the given items.
* @Anonymous items - An array of ShopifyCartItem objects representing the items to update the form with.
*/
public updateProductForm(items: ShopifyCartItem[]) {
// Remove all previously injected elements and add the new ones
this.form.querySelectorAll(`input.planet-dataset`).forEach((el) => {
this.form.removeChild(el);
});
const inputId = this.form.querySelector(
`input[name=id]`
) as HTMLInputElement;
if (inputId) {
inputId.setAttribute("disabled", "true");
}
items.forEach(({ id, quantity }, index) => {
const existingIdInput = this.form.querySelector(
`input[name="items[${index}]id"]`
);
const idInput = existingIdInput || document.createElement("input");
idInput.setAttribute("type", "hidden");
idInput.setAttribute(`name`, `items[${index}]id`);
idInput.setAttribute(`value`, id.toString());
idInput.setAttribute("class", "planet-dataset");
if (!existingIdInput) {
this.form.appendChild(idInput);
}
const existingQtyInput = this.form.querySelector(
`input[name="items[${index}]quantity"]`
);
const qtyInput = existingQtyInput || document.createElement("input");
qtyInput.setAttribute("type", "hidden");
qtyInput.setAttribute(`name`, `items[${index}]quantity`);
qtyInput.setAttribute(`value`, quantity.toString());
qtyInput.setAttribute("class", "planet-dataset");
if (!existingQtyInput) {
this.form.appendChild(qtyInput);
}
});
}
3. Then, I manually click on the button using javascript.
Until now it has been working with a lot of themes with two known exceptions:
1. it doesn't work with the 'Pop up' cart of dawn themes because it expects a response from `/cart/add` with a payload of 'id' and quantity', not an array of items, so it breaks.
2. Some themes work by getting info of quantity and variant selector of the theme, no amtter what's in the form.
For now, the only workaround I've found is set a weird form selector so it's not found, and then I catch the 'FormNotFound' error and do as I was doing before: redirect to the cart after using the AJAX API.