Product Filter
Filter form
{# minimal usage #}
<i:filter-form name="default">
{% for attribute in attributes %}
<i:filter-attribute attribute="attribute">
<a data-role="show-more">{% trans 'template.filter.more' %} (%n)</a>
</i:filter-attribute>
{% endfor %}
<div data-role="reset">
{% trans 'template.filter.active' %}
<a data-role="reset">{% trans 'template.filter.reset' %}</a>
</div>
<button type="submit">{% trans 'template.filter.submit-button' %}</button>
</i:filter-form>Attributes of filter-form
| Attribute | Required | Default | Description |
|---|---|---|---|
| name | yes | --- | name |
| auto-scroll | true | --- | automatically scroll to results (true/false) |
Filter attribute
<i:filter-form name="default">
{% for attribute in attributes %}
<i:filter-attribute attribute="attribute" choice-wrap="div">
<a data-role="show-more">{% trans 'template.filter.more' %} (%n)</a>
<a data-role="show-less">{% trans 'template.filter.less' %}</a>
</i:filter-attribute>
{% endfor %}
<button type="submit">{% trans 'template.filter.submit-button' %}</button>
</i:filter-form>The "show-more"/"show-less" link is only displayed when needed. The optional %n placeholder is replaced with the number of hidden values. Expand/collapse functionality is handled globally and the expanded state is saved to LocalStorage.
Attributes of filter-attribute
| Attribute | Required | Default | Description |
|---|---|---|---|
| attribute | yes | --- | Twig object |
| choice-wrap | no | span | wrapper tag |
| checkbox-class | no | --- | CSS classes for the checkbox input |
| img-format | no | 30x30 | image size (same as in i:img) |
| img-sizes | no | --- | responsive sizes for the image (same as in i:img) |
Filter values
Renders active filter values with the ability to remove individual values or reset the entire filter.
<i:filter-values name="default">
{% for value in values %}
<a href="{{value.url}}">{{value.name}}</a>
{% endfor%}
<a href="{{resetUrl}}">{% trans 'template.filter.reset' %}</a>
</i:filter-values>Attributes of filter-values
| Attribute | Required | Description |
|---|---|---|
| name | yes | object name |
Filter placeholder
<i:filter-placeholder name="default" config="default" />Attributes of filter-placeholder
| Attribute | Required | Description |
|---|---|---|
| name | yes | object name |
| config | yes | filter group code |
| hide-empty | no | do not render the form if there are no filters to display |
| always-collapsed | no | collapse all filters regardless of the admin panel settings |
Filter placeholder values
The i:filter-placeholder-values block must not be wrapped in a condition checking for an active filter! If the filter is not active, it will render as an empty div. If it were hidden by a parent condition, it could not appear dynamically during filtering.
<i:filter-placeholder-values name="default" config="default" />Attributes of filter-placeholder-values
| Attribute | Required | Description |
|---|---|---|
| name | yes | object name |
| config | yes | filter group code |
Price Slider (legacy filters)
Price slider for the filter. Uses jQuery UI Slider (must be enabled in plugins). No JavaScript initialization is needed -- inserting the tag into the template sets everything up automatically. Elements marked with the from and to roles are used to display the current price.
{# minimal usage #}
<i:price-slider>
<div data-role="slider"></div>
<span data-role="from"></span> - <span data-role="to"></span>
</i:price-slider>
{# visible input #}
<i:price-slider>
<div data-role="slider"></div>
<input data-role="from"></input> - <input data-role="to"></input>
</i:price-slider>
{# custom classes (any) #}
<i:price-slider>
<div data-role="slider" class="slider-range" ></div>
<div class="range">
<em>{%trans%}od {%endtrans%} <span class="range-from" data-role="from"></span> do <span class="range-to" data-role="to"></span></em>
</div>
</i:price-slider>To customize the slider behavior, use standard jQuery UI events (documentation). First add a custom class to the slider, then modify its behavior in external JavaScript:
$(document).on('slide', '.slider-range', function(event, ui) {
alert(ui.value);
});Slider properties can be changed the same way:
$('.slider-range').slider('option', 'animate', 'fast');Complete Filter Template
A production _filter.tpl combining the filter form with active filter values. This example is simplified from the buran template. The filter form includes a mobile-specific header (close button + title), a scrollable body with attribute sections, and submit/reset buttons at the bottom:
{# _filter.tpl #}
<i:filter-form name="default" class="filter" auto-scroll="false">
<div class="filter__in">
{# Mobile-only header with close button #}
<p class="filter__header hide-for-medium">
<a class="toggle-filter" role="button" aria-label="{% trans 'template.close' %}">
<svg class="icon"><use xlink:href="{{ images('symbol-defs.svg#bi-cross') }}"></use></svg>
</a>
<strong>{% trans 'template.filter.headline' %}</strong>
</p>
{# Scrollable filter body with attribute sections #}
<div class="filter__body scrollbox">
{% for attribute in attributes %}
<div class="filter-section filter-section--{{ attribute.id }}">
<i:filter-attribute attribute="attribute" checkbox-class="spec-check">
{# "Show more" link -- only displayed when values are hidden #}
{# %n is replaced with the count of hidden values #}
<a data-role="show-more" class="filter-more">template.filter.more (%n)</a>
</i:filter-attribute>
</div>
{% endfor %}
</div>
{# Submit and reset buttons #}
<div class="filter-buttons">
<button type="submit" class="button">
{% trans 'template.filter.submit-button' %}
</button>
{# data-role="reset" wrapper is only visible when a filter is active #}
<div data-role="reset">
<a data-role="reset">{% trans 'template.filter.reset-button' %}</a>
</div>
</div>
</div>
</i:filter-form>
{# Active filter values -- displays selected filters with individual remove links #}
{# Each value.url removes that single filter value; resetUrl clears all filters #}
<i:filter-values name="default" class="filter-active">
<div class="filter-active__in">
<a href="{{ resetUrl }}" class="reset-filter">
<svg class="icon"><use xlink:href="{{ images('symbol-defs.svg#bi-cross') }}"></use></svg>
{% trans 'template.filter.reset-button' %}
</a>
{% for value in values %}
<a href="{{ value.url }}" class="filter-active__value">
<svg class="icon"><use xlink:href="{{ images('symbol-defs.svg#bi-cross') }}"></use></svg>
{{ value.name }}
</a>
{% endfor %}
</div>
</i:filter-values>The data-role="reset" wrapper is only visible when a filter is active. The data-role="show-more" / data-role="show-less" links toggle automatically; their expanded/collapsed state persists in LocalStorage. On mobile, the filter typically opens as a slide-in panel triggered by a toggle-filter button in the listing page.
Integration with Listing
The filter connects to a product listing through i:filter-placeholder and i:filter-placeholder-values placed inside i:list-container. Here is the buran vypis.tpl (category listing page) simplified to show how all components wire together -- the filter form, sort dropdown, active filter pills, product grid, and multiple pagination slots:
{# vypis.tpl (category listing page) #}
<h1>{{ this.nazev }}</h1>
<i:list-container type="product" class="main-listing">
{# Mobile filter toggle button (opens the filter as a slide-in panel) #}
<div class="hide-for-medium">
<a class="toggle-filter" role="button" aria-label="{% trans 'template.filter' %}">
<svg class="icon"><use xlink:href="{{ images('symbol-defs.svg#bi-filter') }}"></use></svg>
<span>{% trans 'template.filter.all-filters' %}</span>
</a>
</div>
{# Filter form placeholder -- renders _filter.tpl content here #}
<i:filter-placeholder name="default" config="default" always-collapsed />
{# Sort dropdown (defined in _listing.tpl as i:list-pagination name="sort") #}
<div data-role="pagination" data-name="sort"></div>
{# Result count (defined in _listing.tpl as i:list-pagination name="numbers") #}
<div data-role="pagination" data-name="numbers"></div>
{# Grid style toggle (defined in _listing.tpl as i:list-pagination name="grid") #}
<div data-role="pagination" data-name="grid"></div>
<hr />
{# Active filter pills with remove links -- must NOT be wrapped in a conditional! #}
<i:filter-placeholder-values name="default" config="default" />
{# Empty state message #}
<div data-role="empty"></div>
{# Product grid #}
<div data-role="list" class="listing"></div>
{# Bottom pagination area with multiple named sections #}
<div class="pagination">
<div data-role="pagination" data-name="scroll"></div>
<div data-role="pagination" data-name="pages"></div>
<div data-role="pagination" data-name="add"></div>
<div data-role="pagination" data-name="numbers"></div>
</div>
</i:list-container>Key points:
i:filter-placeholderrenders the filter form (defined in_filter.tpl) inside the listing container. Theconfigattribute refers to the filter group code configured in the admin panel.i:filter-placeholder-valuesrenders the active filter selections with remove links (defined byi:filter-valuesin_filter.tpl). It must not be wrapped in a conditional -- it renders as an empty div when no filter is active and appears dynamically during AJAX filtering.- The
nameattribute on both placeholders must match thenameused ini:filter-formandi:filter-valuesin_filter.tpl. - The
always-collapsedattribute oni:filter-placeholdercollapses all filter groups regardless of the admin panel settings. - Multiple
data-role="pagination"elements with differentdata-namevalues can coexist in the same container. Each renders the correspondingi:list-paginationdefinition from_listing.tpl. The samedata-namecan appear multiple times (e.g.numbersat the top and bottom). - On mobile, the
.toggle-filterbutton opens the filter panel (styled as a slide-in overlay). The close button inside the filter form (see Complete Filter Template) dismisses it.
For more about the listing components (list-item definitions, pagination variants, source patterns) used alongside filters.
Available CSS classes
Flags
- filter-flag-in-stock - in stock
- filter-flag-new - new arrival
- filter-flag-promo - promotion
- filter-flag-discount - discounted