Have your say in Community Polls: What was/is your greatest motivation to start your own business?

Getting product variant ID from product.options

Getting product variant ID from product.options

ViteEris
Visitor
2 0 1

Photos below!

 

So I'm working on creating a little bit of code to dynamically generate add to cart buttons and corresponding variant menus within the Debut theme so customers visiting our shop can add items to cart from the collections pages without needing to select an individual collection.  I found the most basic code for this on the forums here, but I wanted to go a little further with it and add a drop-down selection menu for all the various options each variant of one of our collections has.  I've managed to achieve most of this with the following code, which loops through each product for unique options and then adds items to a selection menu based off potential unique choices found within each option:
 

<form method="post" action="/cart/add">
      {% if product.variants.size > 1 %}
            {% for option in product.options %}
            	<label for="SingleOptionSelector-{{forloop.index0}}"> {{ option }} </label>
            	<select id="SingleOptionSelector-{{forloop.index0}}" class="single-option-selector single-option-selector-product-template product-form__input" data-index="option{{forloop.index}}" name="id">
    				{% capture option_index %}option{{ forloop.index }}{% endcapture %} 
    				{% assign option_values = product.variants | map: option_index | uniq %}
                    {% for choice in option_values %}
                 		<option value="{{ choice }}"> {{ choice }} </option>
                  	{% endfor %}
            	</select>
            {% endfor %}
            {% endif %}
            
            <input min="1" type="number" id="quantity" name="quantity" value="1"/>
  			<input type="submit" value="Add to cart" class="btn" />
</form>

 

However, the problem I'm running into is that I lack the ability to take the selections from these different drop-down menus and tie them to a specific variant ID, resulting in them being unable to add to cart due to the error of "array contains unpermitted members: id," which I've pinpointed to being a lack of proper variant or product id tied to the selection upon submission.  I have a much simpler solution that only has one drop-down menu for each individual variant title, but this is suboptimal for my use case as we have bundle packages that require multiple different selections, and results in drop-down menus that are 20 or more items long of mostly redundant items.  I've attached this code as well though, as it might help someone who only has one-option variants:

 

<form method="post" action="/cart/add">
 {% if product.variants.size > 1 %}
            	<label for="SingleOptionSelector-0">Select a size</label>
					<select id="SingleOptionSelector-0" class="single-option-selector single-option-selector-product-template product-form__input" data-index="option1" name="id">
  						{%for variant in product.variants %}
                     		<option value="{{ variant.id }}"> {{ variant.title }} </option>
                      	{% endfor %}
				</select>
            <input min="1" type="number" id="quantity" name="quantity" value="1"/>
  			<input type="submit" value="Add to cart" class="btn" />
</form>


Alternatively if someone out there knows how to dynamically generate these kinds of drop-down menus in the collections page while also creating the <select id="ProductSelect-product-template" class="product-form__variants no-js" name="id" data-productid="{{product.id}}"> hidden menu that appears in the individual collection selection page, I'd love some help with this.

I've attached a photo with my dynamically-generated menus, each menu inherits the Option name from the product ID and displays it as a label, all of the different selections within that option tree are also pulled and added as selections to the menuI've attached a photo with my dynamically-generated menus, each menu inherits the Option name from the product ID and displays it as a label, all of the different selections within that option tree are also pulled and added as selections to the menu

 

 

This is a demonstration of the second snippet of code which will add the item to cart properly, but unfortunately results in these exorbantly long menus that are just awful to work with, so I'd like to avoid it if possibleThis is a demonstration of the second snippet of code which will add the item to cart properly, but unfortunately results in these exorbantly long menus that are just awful to work with, so I'd like to avoid it if possible

Replies 6 (6)

ViteEris
Visitor
2 0 1

ALRIGHT I figured it out myself.  For anybody interested in dynamically-generated drop down menus for variant selection in their collections screen, the entire code is:

          <form method="post" action="/cart/add">
            {% if product.variants.size > 1 %}
                {% for option in product.options %}
            	<label for="SingleOptionSelector-{{forloop.index0}}"> {{ option }} </label>
            	<select id="SingleOptionSelector-{{forloop.index0}}" class="single-option-selector single-option-selector-product-template product-form__input" data-index="option{{forloop.index}}" name="id">
    				{% capture option_index %}option{{ forloop.index }}{% endcapture %} 
    				{% assign option_values = product.variants | map: option_index | uniq %}
                    {% for choice in option_values %}
                 		<option value="{{ choice }}"> {{ choice }} </option>
                  	{% endfor %}
            	</select>
            
            	{% endfor %}
            	<select id="ProductSelect-product-template" class="product-form__variants no-js" name="id" data-productid="{{product.id}}">
                  	{% for option in product.variants %}
                 		 <option value="{{option.id}}"> {{option.title}}</option>
                  	{% endfor %}
            	</select>
            {% endif %}
            
            <input min="1" type="number" id="quantity" name="quantity" value="1"/>
  			<input type="submit" value="Add to cart" class="btn" />
		</form>

The second <select></select> is the key to retrieving the proper variant ID to feed into the submission, which basically loops through every possible variant in each product in the stack and builds a hidden option from the variant id and the title.

poutSG
Tourist
8 0 1

Hi there,

Thank you, this is very useful as I'm trying to do this for my store too!  May I ask where you inserted this code?

missea
Tourist
3 0 9

I am having the same issue and tried to implement your code and I am still having the issue that only the last dropdown with the actual variants is being reflected into the cart after add. If I choose different options from my first 2 drop downs (size and color) those sizes and colors don't get added to the cart. Are you having this issue? It's very frustrating that shopify has made it so hard to loop through unique variants.

jam_chan
Shopify Partner
927 23 190

Hi @ViteEris 

Thanks, the trick for the 2nd hidden drop-down list is useful. I am also making a special collection page to show all products with variant options. The user can select the variant and add to cart.

However, I have a question regarding performance. For a collection, it will show all options and all variants of all products. Did you do any pagination? Will the rendering become slow if the collection has more than a hundred products?

BYOB - Build Your Own Bundles, SPO - SEO App to research keywords & edit social link preview
ShanQureshi
Shopify Partner
7 0 0

Hi, it is useful to show different variant options but when different variants selected like color and size with different select option then how we can submit it without showing this 

<select id="ProductSelect-product-template" class="product-form__variants no-js" name="id" data-productid="{{product.id}}">
                  	{% for option in product.variants %}
                 		 <option value="{{option.id}}"> {{option.title}}</option>
                  	{% endfor %}
            	</select>

cobyambrose
Shopify Partner
4 0 1

Using multiple option selects to update a single master selector requires this code

For anyone looking for some code which allows you to convert between selected options and selected variants... I did it in JavaScript by having two translator functions. One converts an array of 0-indexed option selections to a single variant index and the other does the opposite. The maths behind it is kinda complicated but if you want to understand then open up a spreadsheet and type out the numbers and try to write a formula to convert between them.

 

let optionSizes = {
    {
        product.options_with_values | json
    }
}.map(_option => _option["values"].length);

function optionsToVariantIndex(_options) {
    // Takes an array of 0-indexed option indicies
    // Returns 0-indexed variant

    let addcumulator = 0;
    let multcumulator;
    for (let i = 0; i < _options.length - 1; i++) {
        multcumulator = _options[i];
        for (let j = i + 1; j < optionSizes.length; j++) {
            multcumulator *= optionSizes[j];
        }
        addcumulator += multcumulator;
    }
    addcumulator += _options[_options.length - 1];

    return addcumulator;
}

function variantIndexToOptions(_variant) {
    // Takes a 0-index of variant
    // Returns array of 0-indicies of options 

    let options = [];
    let operand;
    for (let i = 0; i < optionSizes.length - 1; i++) {
        operand = optionSizes[i + 1];
        for (let j = i + 2; j < optionSizes.length; j++) {
            operand *= optionSizes[j];
        }
        options.push(
            Math.floor(_variant / operand) % optionSizes[i] + 1
        );
    }
    options.push(_variant % optionSizes[optionSizes.length - 1] + 1);

    return options;
}