Modifying Item Options in a StoreFront
This page will show how to customize item options. If you need item variations instead, see this article: Modifying Item Variations in a StoreFront
StoreFront Item Variations
Helpful Links
Converting an Item from using Options to Variations (introduction to variations)
Item Variations (instructions for creating/configuring variations)
Adding ?uc-debug to then end of an item page containing variations will generate debugging output in the browser console.
Example: https://demo.ultracartstore.com/shop/TSHIRT.html?uc-debug
HTML Components
The item options are found in two places.
- The visible portions of the item options, such as the select boxes (or radio buttons) are found in the
template_item.vm
file. - The javascript that powers the item options is (usually) found in the
document_bottom.vm
file. This is a snippet file, so it is found in your/themes/<my theme name>/snippets/
directory and NOT in the templates directory tree.
Item Options Form Elements
#if($theme.attr('Item Show Options') == 'true') #foreach ($itemOption in $item.getOptions()) <div class="label-group"> #if($itemOption.getType() == "dropdown") <label for="option-select$velocityCount">$itemOption.getLabel()</label> <input type="hidden" name="OptionName${velocityCount}" value="$formatHelper.escapeHtml($itemOption.getName())" /> <select class="uc-option-field" data-item-id="$item.getMerchantItemID()" data-option-name="$itemOption.getName()" id="option-select${velocityCount}" name="OptionValue${velocityCount}"> #foreach ($dropDownValue in $itemOption.getDropDownValues()) <option value="$formatHelper.escapeHtml($dropDownValue)">$formatHelper.escapeHtml($dropDownValue)</option> #end </select> #end #if($itemOption.getType() == "single") <label for="option-single${velocityCount}">$itemOption.getLabel()</label> <input type="hidden" name="OptionName${velocityCount}" value="$formatHelper.escapeHtml($itemOption.getName())" /> <input class="uc-option-field" data-item-id="$item.getMerchantItemID()" data-option-name="$itemOption.getName()" id="option-single${velocityCount}" type="text" name="OptionValue${velocityCount}" /> #end #if($itemOption.getType() == "multiline") <label for="option-multiline${velocityCount}">$itemOption.getLabel()</label> <input type="hidden" name="OptionName${velocityCount}" value="$formatHelper.escapeHtml($itemOption.getName())" /> <textarea class="uc-option-field" data-item-id="$item.getMerchantItemID()" data-option-name="$itemOption.getName()" id="option-multiline${velocityCount}" type="text" name="OptionValue${velocityCount}"></textarea> #end #if($itemOption.getType() == "radio") <label>$itemOption.getLabel()</label> <br> <ul class='option-group'> #foreach($radioValues in $itemOption.getValues()) <li> <label for="option-radio${velocityCount}"> <input type="hidden" name="OptionName${velocityCount}" value="$formatHelper.escapeHtml($itemOption.getName())" /> <input class="uc-option-field" data-item-id="$item.getMerchantItemID()" data-option-name="$itemOption.getName()" type="radio" name="OptionValue${velocityCount}" value="$formatHelper.escapeHtml($radioValues.value)" />$formatHelper.escapeHtml($radioValues.value)</label> </li> #end </ul> #end #if($itemOption.getType() == "file attachment") <label for="option-file${velocityCount}">$itemOption.getLabel()</label> <input class="uc-option-field" data-item-id="$item.getMerchantItemID()" data-option-name="$itemOption.getName()" id="option-file${velocityCount}" type="file" name="$formatHelper.escapeHtml($itemOption.getName())" /> #end </div> #end #end
This code is part of the much larger <form> block within the template_item.vm
file
For the javascript to work correctly, item option form elements must contain these three attributes:
class="uc-option-field"
data-item-id="$item.getMerchantItemID()"
data-option-name="$itemOption.getName()"
Do not deviate from this notation.
Javascript Component
<script type="text/javascript" src="/catalog_5.1.js"></script> <!-- there will be a lot of other html components in between the catalog_5.1.js reference and the actual item variation code ... just FYI --> #if($item && $item.hasVariations() && "$cart" != "") ## THIS WILL CONTAIN A LARGE CODE BLOCK FOR ITEM VARIATIONS. IT WAS REMOVED TO AVOID CONFUSION IN THIS DOCUMENTATION. #elseif($item && $item.hasOptions() && "$cart" != "") <script type='text/javascript'> //this is dependent on jquery and catalog_5.1.js console.log('initializing catalog options'); #set($currencyCode = $cart.getCurrency().getSymbol()) // this can be named anything or assigned anywhere. Or not even assigned to a variable. // I just chose window.itemOptions so it would be easy to debug var itemOptionsOptions = { // really? options options? SMH merchantItemId: "$item.getMerchantItemID()", optionMatrix: ${item.getOptionMatrixJson()}, multimedia: ${item.getMultimediaAsJson()}, currencyCode: "$currencyCode" }; //end of itemOptions options hash. window.itemOptions = new ultracart.ItemOptions(jQuery, itemOptionsOptions); window.itemOptions.on('option-changed', function(event, option_data){ console.log('option-change event triggered', option_data); window.console.log(window.itemOptions.getSelectedValues()); }); window.itemOptions.on('multimedia-selected', function(event, multimediaOid){ console.log('multimedia-selected event triggered, multimediaOid:', multimediaOid); var multimediaImageIndex = parseInt(jQuery("li.product-image[data-multimedia-oid='" + multimediaOid + "']").attr('index')); if(!isNaN(multimediaImageIndex)){ jQuery('#main-item-image-viewer-holder').slickGoTo(multimediaImageIndex); } }); window.itemOptions.on('multimedia-unselected', function(event){ console.log('multimedia-unselected event triggered (no extra data provided)'); // revert back to the original image, which is always at index 0. jQuery('#main-item-image-viewer-holder').slickGoTo(0); }); </script> #end
Helper Methods
/**
* takes an oid and looks for a matching multimedia record, returning if found
* @param {number} multimediaOid
* @return {Multimedia} if found, else null
*/
var multimediaRecord = itemOptions.getMultimedia(multimediaOid)
/**
* examine all the form elements tied to this item variation and return back an array (in order) of selected values and
* all supporting information. This is a very powerful method to use in conjunction with the option-changed event to
* construct a detailed view of additional costs from the result of options selected.
* @return {Object[]}
*/
var itemOptionsSnapshot = itemOptions.getSelectedValues()
Events
All events except the item-unselected
and multimedia-unselected
event will contain a data parameter (2nd parameter).
please notice that the variation event is plural (options-changed) while the options event is singular (option-changed)
Item Variation Events
option-changed
This event fires whenever an item option changes value (customer selects a different value).
data:
/**
* @typedef {Object} ItemOptionEventOptionChange
* @property {string} fieldOptionName Name of the item option. For example: 'Size', 'Color', 'Picture Orientation'
* @property {string} fieldValue Name of the current value selected. Could be empty string ('') denoting no selection was made.
* Or, it will contain the value customer has selected. For example: 'Medium', 'Blue', 'Landscape', etc.
* @property {ItemOption} selectedOption This will have all the possible meta data for the current item option
* @property {ItemOptionValue} selected Value convenience property containing the selected value. this could also be found
* by iterating the selectedOption.values and matching the value to the fieldValue. This will be null if the fieldValue
* is an an empty string ('').
*/
multimedia-selected
This event fires whenever a multimedia (image) is applicable to the currently selected variations. This can occur based on a selection from one of the select boxes (or radio buttons), or when enough selections are made to narrow down to a single child item which has an assigned multimedia image.
/**
* @typedef {Object} Multimedia contains high level information about one of the multimedia objects associated with the parent item
* @property {string} type
* @property {boolean} isDefault
* @property {number} imageWidth
* @property {number} imageHeight
* @property {string} viewUrl
* @property {string} viewSsl
* @property {string} description
* @property {string} code
* @property {string} merchantItemId
* @property {boolean} excludeFromGallery
* @property {string} filename
* @property {number} multimediaOid
*/
multimedia-unselected
This event fires whenever the customer has changed their selections (or cleared them out) to the point where no images can be found to match the current set of variation choices.
This event does not have any extra data associated with it.