Once a field has been rendered {{ field|add_class }} how can you add additional classes? For example, let's say you want to build a variable and simply apply that. See the example below.
If field.type = Select
CSSSTYLES += 'form-select'
If field.error
CSSSTYLES += 'error'
{{ field|append_attr:"class:CSSSTYLES" }}
<form class="mt-3" method="POST" action="{% url 'profile' %}" autocomplete="off" novalidate>
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
<div class="mb-3">
<label class="form-label" id="{{ field.id_for_label }}">
{{ field.label }}
</label>
{% if field.errors %}
{{ field|append_attr:"class:form-control is-invalid is-invalid-lite" }}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% else %}
{{ field|append_attr:"class:form-control" }}
{% endif %}
{% endfor %}
{% for field in form.visible_fields %}
<div class="mb-3">
<label class="form-label" id="{{ field.id_for_label }}">
{{ field.label }}
</label>
{% if field.errors %}
{% if field.field.widget.input_type == 'select' %}
{{ field|add_class:"form-select is-invalid is-invalid-lite" }}
{% else %}
{{ field|add_class:"form-control is-invalid is-invalid-lite" }}
{% endif %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% else %}
{% if field.field.widget.input_type == 'select' %}
{{ field|append_attr:"class:form-select" }}
{% else %}
{{ field|append_attr:"class:form-control" }}
{% endif %}
{% endif %}
{% endfor %}
Related
i have created custom code sidebar filter. if filter by one tag its working fine, suppose if we click another tag no product details are showing.
Snippet/sidebar-filter.liquid
{% for block in section.blocks %}
{% assign heading = block.settings.filter_heading %}
{% assign tags = block.settings.filter_tags | split: ',' %}
<div class="collection-filter-tag-category">
<h4> {{ heading }} </h4>
<div class="filter-li-scroll">
{% for t in tags %}
{% assign tag = t | strip %}
{% if current_tags contains tag %}
<li class="active">
{{ tag | link_to_remove_tag : tag }}
</li>
{% else %}
<li class="">
{{ tag | link_to_add_tag : tag }}
</li>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
Templates/collection.liquid:
{% section 'collection-template' %}
Templates/collection-template.liquid:
<div id="collection-product-filter-pane">
<div class="sidebar_filter">
{% include 'sidebar-filter' %}
</div>
<div class="page-width" id="Collection">
{% if section.settings.layout == 'grid' %}
{% case section.settings.grid %}
{% when '2' %}
{%- assign grid_item_width = 'medium-up--one-half' -%}
{% when '3' %}
{%- assign grid_item_width = 'small--one-half medium-up--one-third' -%}
{% when '4' %}
{%- assign grid_item_width = 'small--one-half medium-up--one-quarter' -%}
{% when '5' %}
{%- assign grid_item_width = 'small--one-half medium-up--one-fifth' -%}
{% endcase %}
<div class="grid grid--uniform{% if collection.products_count > 0 %} grid--view-items{% endif %}">
{% for product in collection.products %}
<div class="grid__item grid__item--{{section.id}} {{ grid_item_width }}">
{% include 'product-card-grid', max_height: max_height %}
</div>
{% else %}
{% comment %}
Add default products to help with onboarding for collections/all only.
The onboarding styles and products are only loaded if the
store has no products.
{% endcomment %}
{% if collection.handle == 'all' and collection.all_vendors.size == 0 and collection.all_types.size == 0 %}
<div class="grid__item">
<div class="grid grid--uniform">
{% for i in (1..limit) %}
<div class="grid__item {{ grid_item_width }}">
<div class="grid-view-item">
<a href="#" class="grid-view-item__link">
<div class="grid-view-item__image">
{% capture current %}{% cycle 1, 2, 3, 4, 5, 6 %}{% endcapture %}
{{ 'product-' | append: current | placeholder_svg_tag: 'placeholder-svg' }}
</div>
<div class="h4 grid-view-item__title">{{ 'homepage.onboarding.product_title' | t }}</div>
<div class="grid-view-item__meta">
<span class="product-price__price">$19.99</span>
</div>
</a>
</div>
</div>
{% endfor %}
</div>
</div>
{% else %}
{%- assign is_empty_collection = true -%}
{% endif %}
{% endfor %}
</div>
{% else %}
<div class="list-view-items">
{% for product in collection.products %}
<a href="{{ product.url | within: collection }}" class="list-view-item">
{% include 'product-card-list', product: product %}
</a>
{% else %}
{% comment %}
Add default products to help with onboarding for collections/all only.
The onboarding styles and products are only loaded if the
store has no products.
{% endcomment %}
{% if collection.handle == 'all' and collection.all_vendors.size == 0 and collection.all_types.size == 0%}
{% for i in (1..4) %}
<a href="#" class="list-view-item">
<div class="list-view-item__image-column">
<div class="list-view-item__image-wrapper">
<div class="list-view-item__image">
{%- assign placeholder = 'placeholder-product-' | append: i -%}
{% include placeholder %}
</div>
</div>
</div>
<div class="list-view-item__title-column">
<div class="list-view-item__title">{{ 'homepage.onboarding.product_title' | t }}</div>
</div>
<div class="list-view-item__price-column">
<span class="product-price__price">$19.99</span>
</div>
</a>
{% endfor %}
{% else %}
{%- assign is_empty_collection = true -%}
{% endif %}
{% endfor %}
</div>
{% endif %}
{% if is_empty_collection %}
<div class="grid__item small--text-center">
<p class="text-center">{{ 'collections.general.no_matches' | t }}</p>
</div>
{% endif %}
{% if paginate.pages > 1 %}
{% include 'pagination' %}
{% endif %}
</div>
</div>
if filter by another tag no product list coming
please help me to get result, I'm stucking two days pls someone help me
I would like to change my variant display from dropdown (select) to using buttons instead. I cannot find any up to date guides elsewhere online. I am using the Debut shopify theme.
it currently looks like this dropdown but I want to change this to buttons with the sizes. i.e. so you click your desired size button and then click the standard 'add to cart' button (which is already in place, but currently for the dropdown) below it.
Here is the code:
{% capture "form_classes" -%}
product-form product-form-{{ section.id }}
{%- unless section.settings.show_variant_labels %} product-form--hide-variant-labels {% endunless %}
{%- if section.settings.enable_payment_button and product.has_only_default_variant %} product-form--payment-button-no-variants {%- endif -%}
{%- if current_variant.available == false %} product-form--variant-sold-out {%- endif -%}
{%- endcapture %}
{% form 'product', product, class:form_classes, novalidate: 'novalidate', data-product-form: '' %}
{% unless product.has_only_default_variant %}
<div class="product-form__controls-group">
{% for option in product.options_with_values %}
<div class="selector-wrapper js product-form__item">
<label {% if option.name == 'default' %}class="label--hidden" {% endif %}for="SingleOptionSelector-{{ forloop.index0 }}">
{{ option.name }}
</label>
<select class="single-option-selector single-option-selector-{{ section.id }} product-form__input"
id="SingleOptionSelector-{{ forloop.index0 }}"
data-index="option{{ forloop.index }}"
>
{% for value in option.values %}
<option value="{{ value | escape }}"{% if option.selected_value == value %} selected="selected"{% endif %}>{{ value }}</option>
{% endfor %}
</select>
</div>
{% endfor %}
</div>
{% endunless %}
<select name="id" id="ProductSelect-{{ section.id }}" class="product-form__variants no-js">
{% for variant in product.variants %}
<option value="{{ variant.id }}"
{%- if variant == current_variant %} selected="selected" {%- endif -%}
>
{{ variant.title }} {%- if variant.available == false %} - {{ 'products.product.sold_out' | t }}{% endif %}
</option>
{% endfor %}
</select>
{% if section.settings.show_quantity_selector %}
<div class="product-form__controls-group">
<div class="product-form__item">
<label for="Quantity-{{ section.id }}">{{ 'products.product.quantity' | t }}</label>
<input type="number" id="Quantity-{{ section.id }}"
name="quantity" value="1" min="1" pattern="[0-9]*"
class="product-form__input product-form__input--quantity" data-quantity-input>
</div>
</div>
{% endif %}
<div class="product-form__error-message-wrapper product-form__error-message-wrapper--hidden{% if section.settings.enable_payment_button %} product-form__error-message-wrapper--has-payment-button{% endif %}"
data-error-message-wrapper
role="alert"
>
<span class="visually-hidden">{{ 'general.accessibility.error' | t }} </span>
{% include 'icon-error' %}
<span class="product-form__error-message" data-error-message>{{ 'products.product.quantity_minimum_message' | t }}</span>
</div>
<div class="product-form__controls-group product-form__controls-group--submit">
<div class="product-form__item product-form__item--submit
{%- if section.settings.enable_payment_button %} product-form__item--payment-button {%- endif -%}
{%- if product.has_only_default_variant %} product-form__item--no-variants {%- endif -%}"
>
<button type="submit" name="add"
{% unless current_variant.available %} aria-disabled="true"{% endunless %}
aria-label="{% unless current_variant.available %}{{ 'products.product.sold_out' | t }}{% else %}{{ 'products.product.add_to_cart' | t }}{% endunless %}"
class="btn btn--rounded product-form__cart-submit{% if section.settings.enable_payment_button %} btn--secondary-accent{% endif %}"
data-add-to-cart>
<span data-add-to-cart-text>
{% unless current_variant.available %}
{{ 'products.product.sold_out' | t }}
{% else %}
{{ 'products.product.add_to_cart' | t }}
{% endunless %}
</span>
<span class="hide" data-loader>
{% include 'icon-spinner' %}
</span>
</button>
{% if section.settings.enable_payment_button %}
{{ form | payment_button }}
{% endif %}
</div>
</div>
{% endform %}
</div>
Thanks a lot!
Replace your select boxes with input radio buttons:
{%- for variant in product.variants -%}
<input
type="radio"
name="id"
class="variant-option"
value="{{ variant.id }}"
id="variant-option-{{ variant.id }}"
{% unless variant.available %} disabled{% endunless %}
{% if product.selected_variant.id == variant.id %} checked{% endif %}
>
<label for="variant-option-{{ variant.id }}">{{ variant.title }}</label>
{%- endfor -%}
And then just add some styles to display these inputs as buttons e.g:
.variant-option + label {
background-color: #f2f2f2;
padding: 10px 15px;
margin: 0 5px;
}
.variant-option:checked + label {
background-color: #000;
color: #fff;
}
.variant-option[disabled] + label {
opacity: 0.5;
}
.variant-option {
display: none;
}
I'm customizing my page to upload multiple files (photos). This is the code that I have so far and according to this link it should work:
https://shopify.dev/tutorials/customize-theme-get-customization-information-for-products
in the link go to "Allow file uploads" if you want to check.
{% form 'product', product, class:form_classes, data-product-form: '', enctype:"multipart/form-data" %}
{% unless product.has_only_default_variant %}
<div class="product-form__controls-group">
{% for option in product.options_with_values %}
<div class="selector-wrapper js product-form__item">
<select class="single-option-selector single-option-selector-{{ section.id }} product-form__input"
id="SingleOptionSelector-{{ forloop.index0 }}"
data-index="option{{ forloop.index }}"
>
{% for value in option.values %}
<option value="{{ value | escape }}"{% if option.selected_value == value %} selected="selected"{% endif %}>{{ value }}</option>
{% endfor %}
</select>
</div>
{% endfor %}
</div>
{% endunless %}
<select name="id" id="ProductSelect-{{ section.id }}" class="product-form__variants no-js">
{% for variant in product.variants %}
<option value="{{ variant.id }}"
{%- if variant == current_variant %} selected="selected" {%- endif -%}
>
{{ variant.title }} {%- if variant.available == false %} - {{ 'products.product.sold_out' | t }}{% endif %}
</option>
{% endfor %}
</select>
{% if section.settings.show_quantity_selector %}
{% comment %}
<div class="product-form__controls-group">
<div class="product-form__item">
<input type="number" id="Quantity-{{ section.id }}"
name="quantity" value="1" min="1" pattern="[0-9]*"
class="product-form__input product-form__input--quantity" data-quantity-input
>
</div>
</div>
<div class="qtydiv">
{% comment %}<label for="Quantity" class="quantity-selector">Quantity</label>{% endcomment %}
<div class="qtybox">
<span class="btnqty qtyminus icon icon-minus">-</span>
<input type="text" id="quantity" name="quantity" value="1" min="1" class="quantity-selector quantity-input" readonly="">
<span class="btnqty qtyplus icon icon-plus">+</span>
</div>
</div>
{% endcomment %}
{% endif %}
<div class="product-form__error-message-wrapper product-form__error-message-wrapper--hidden{% if section.settings.enable_payment_button %} product-form__error-message-wrapper--has-payment-button{% endif %}"
data-error-message-wrapper
role="alert"
>
<span class="visually-hidden">{{ 'general.accessibility.error' | t }} </span>
{% include 'icon-error' %}
<span class="product-form__error-message" data-error-message>{{ 'products.product.quantity_minimum_message' | t }}</span>
</div>
<div class="product-form__controls-group product-form__controls-group--submit">
<div class="qtydiv">
{% comment %}<label for="Quantity" class="quantity-selector">Quantity</label>{% endcomment %}
<div class="qtybox">
<span class="btnqty qtyminus icon icon-minus">-</span>
<input type="text" id="quantity" name="quantity" value="1" min="1" class="quantity-selector quantity-input" readonly="">
<span class="btnqty qtyplus icon icon-plus">+</span>
</div>
</div>
<div class="product-form__item product-form__item--submit
{%- if section.settings.enable_payment_button %} product-form__item--payment-button {%- endif -%}
{%- if product.has_only_default_variant %} product-form__item--no-variants {%- endif -%}"
>
<button type="submit" name="add"
{% unless current_variant.available %} aria-disabled="true"{% endunless %}
aria-label="{% unless current_variant.available %}{{ 'products.product.sold_out' | t }}{% else %}{{ 'products.product.add_to_cart' | t }}{% endunless %}"
class="btn product-form__cart-submit{% if section.settings.enable_payment_button %} btn--secondary-accent{% endif %}"
data-add-to-cart>
<span data-add-to-cart-text>
{% unless current_variant.available %}
{{ 'products.product.sold_out' | t }}
{% else %}
{{ 'products.product.add_to_cart' | t }}
{% endunless %}
</span>
<span class="hide" data-loader>
{% include 'icon-spinner' %}
</span>
</button>
{% if section.settings.enable_payment_button %}
{{ form | payment_button }}
{% endif %}
</div>
<input required class="required product-form__input" id="photo" type="file" name="properties[Photo]" multiple>
</div>
</div>
{% endform %}
This is my form that according to the link must have the attribute
enctype = "multipart / form-data".
At the bottom of the form it is in input type = "file".
<input required class="required product-form__input" id="photo" type="file" name="properties[Photo]" multiple>
And this is the code in the cart-template.liquid.
<div class="list-view-item__title">
<a href="{{ item.url }}" class="cart__product-title" data-cart-item-title>
{{ item.product.title }}<br>
{% assign property_size = item.properties | size %}
{% if property_size > 0 %}
{% for p in item.properties %}
{{ item.properties.count }}
{% assign first_character_in_key = p.first | truncate: 1, '' %}
{% unless p.last == blank or first_character_in_key == '_' %}
{{ p.first }}:
{% if p.last contains '/uploads/' %}
<a class="lightbox" href="{{ p.last }}">{{ property_size }}</a>
{% else %}
{{ p.last }}
{% endif %}
<br>
{% endunless %}
{% endfor %}
{% endif %}
</a>
</div>
The problem is the property_size variable gets value one even if I upload two or more images, when in fact I should return the amount of properties that the item has.
Can anyone help me please? What I am doing wrong?
The Debut theme probably has changed since this tutorial was uploaded, and since it is not supported, probably won't be updated.
If you add a type="text" input instead of a type="file" the property gets added as expected. This happens because the _initAddToCart function (assets/theme.js line 6447) gets the form HTML element and pass it along to another function.
var $data = $(this.selectors.productForm, this.$container);
this._addItemToCart($data);
Then in the _addItemToCart function (assets/theme.js line 6513), the form is serialized to a string:
var params = {
url: '/cart/add.js',
data: $(data).serialize(),
dataType: 'json',
};
$.post(params).done(...
This function will ignore the file inputs, as explained in the jQuery documentation:
Data from file select elements is not serialized.
To send the file, the FormData browser API can be used. But to use it with the jQuery AJAX call, some properties must be set:
var params = {
url: '/cart/add.js',
// data: $(data).serialize(),
dataType: 'json',
// Disable the jQuery data processing, and send the FormData object
contentType: false,
processData: false,
data: new FormData($(data).get()[0])
};
$.post(params).done(...
However, since the HTML file <input> has name="properties[Photo]", only the last of the uploaded files will be added to the cart item's Photo property.
I tried with name="properties[Photo][]", but the cart/add.js call only returns an error 500 with the message "Internal Server Error".
So to upload multiple files you could add some custom code to the _addItemToCart function, to get each File from the <input> and add to the FormData with a different key for each file.
I want to display colors and sizes separately on frontend to filter the collection in shopify. But when I do, it shows color and size in same variable with slash in it.
Here is the image
Here is the code that i try
<div class="size-filter clearfix mt-4">
<h4>SIZE</h4>
<hr>
{% assign available_sizes = '' %}
{% for product in collections.frontpage.products limit: limit %}
{% for variant in product.variants %}
{% if variant.title != 'Default Title' %}
{% unless available_sizes contains variant.title %}
{% if variant.available %}
{% capture available_sizes %}{{ available_sizes }}, {{ variant.title }}{% endcapture %}
<div class="form-check">
<input class="form-check-input coll-filter" type="checkbox" value="{{variant.title}}" id=""
{% if current_tags contains tag %} checked {%endif%}>
<label class="form-check-label" for="">
{{variant.title}}
</label>
</div>
{% endif %}
{% endunless %}
{% endif %}
{% endfor %}
{% endfor %}
</div>
Please help me to fix this error, i stuck here from last 3 days.
Error Solved but got this output:
You can do use the options_with_values object:
{% for product_option in product.options_with_values %}
<h5>{{ product_option.name }}</h5>
{% for value in product_option.values %}
<input id="{{product_option|handle}}-{{value|handle}}" type="checkbox" value="{{ value }}" />
<label for="{{product_option|handle}}-{{value|handle}}"></label>
{% endfor %}
{% endfor %}
But you will need to write some Javascript in order to tie it to the main select which holds the variants ids.
On my Shopify store I need to be able to hide sizes in the dropdown that are no longer available. I have tried multiple times adding the code that Shopify suggests here but I am using the Retina Out of the Sandbox theme and add that code to the product-form.liquid file and what happens is only 1 size becomes available no matter what. My store is in dire need of this feature because we sell tons of closeout shoes no longer available so when a customer searches for a size products that have a sold out size 9 still show because it is not hidden on the dropdown, it just says Sold Out, here is my code. Apologies if the formatting is not so nice looking, this is what came with my theme.
product-form.liquid
{% if product.available %}
<form action="/cart/add" method="post" class="clearfix product_form" data-money-format="{{ shop.money_format }}" data-shop-currency="{{ shop.currency }}" id="product-form-{{ product.id }}">
{% if settings.display_inventory_left %}
<div class="items_left">
{% if product.variants.first.inventory_management == "shopify" and product.variants.first.inventory_quantity > 0 %}
<p><em>{{ product.variants.first.inventory_quantity }} {{ settings.inventory_left_text | escape }}</em></p>
{% endif %}
</div>
{% endif %}
{% if product.options.size > 1 %}
<div class="select">
<select id="product-select-{{ product.id }}" name='id'>
{% for variant in product.variants %}
<option {% if variant == product.selected_or_first_available_variant %}selected="selected"{% endif %} value="{{ variant.id }}">{{ variant.title }}</option>
{% endfor %}
</select>
</div>
{% elsif product.options.size == 1 and (product.variants.size > 1 or product.options[0] != "Title") %}
<div class="select">
<label>{{ product.options[0] }}:</label>
<select id="product-select-{{ product.id }}" name='id'>
{% for variant in product.variants %}
<option {% if variant == product.selected_or_first_available_variant %}selected="selected"{% endif %} value="{{ variant.id }}">{{ variant.title }}</option>
{% endfor %}
</select>
</div>
{% else %}
<input type="hidden" name="id" value="{{ product.variants.first.id }}" />
{% endif %}
{% if settings.display_product_quantity %}
<div class="left">
<label for="quantity">Quantity:</label>
<input type="number" min="1" size="2" class="quantity" name="quantity" id="quantity" value="1" />
</div>
{% endif %}
<div class="purchase clearfix {% if settings.display_product_quantity %}inline_purchase{% endif %}">
{% if settings.cart_return == 'back' %}
<input type="hidden" name="return_to" value="back" />
{% endif %}
<input type="submit" name="add" value="{{ settings.add_to_cart_text | escape }}" class="action_button add_to_cart" />
</div>
</form>
{% if product.variants.size > 1 or product.options.size > 1 %}
<script type="text/javascript">
// <![CDATA[
$(function() {
$product = $('#product-' + {{ product.id }});
new Shopify.OptionSelectors
("product-select-{{ product.id }}",{
product: {{ product | json }},
onVariantSelected: selectCallback{% if product-form == 'product' %},
enableHistoryState: true{% endif %}
});
{% if product.options.size == 0 %}
{% for variant in product.variants %}
{% unless variant.available %}
jQuery('.single-option-selector option').filter(function() { return jQuery(this).html() === {{ variant.title | json }}; }).remove();
{% endunless %}
{% endfor %}
jQuery('.single-option-selector').trigger('change');
{% endif %}
// ]]>
</script>
{% endif %}
{% endif %}
A couple of small things I noticed:
{% if product.options.size == 0 %} should be {% if product.options.size == 1 %} (see here).
You're missing the closing brackets for $(function() {.... You need }); before the closing </script> tag.
This seems to work for me now:
{% if product.variants.size > 1 or product.options.size > 1 %}
<script type="text/javascript">
// <![CDATA[
$(function() {
$product = $('#product-' + {{ product.id }});
new Shopify.OptionSelectors
("product-select-{{ product.id }}",{
product: {{ product | json }},
onVariantSelected: selectCallback{% if product-form == 'product' %},
enableHistoryState: true{% endif %}
});
{% if product.options.size == 1 %} // *** should be 1, not 0 ***
{% for variant in product.variants %}
{% unless variant.available %}
jQuery('.single-option-selector option').filter(function() { return jQuery(this).html() === {{ variant.title | json }}; }).remove();
{% endunless %}
{% endfor %}
jQuery('.single-option-selector').trigger('change');
{% endif %}
}); // *** missing closing brackets here ***
// ]]>
</script>
{% endif %}