Filtros de Produto
O Filtros de Produto é o componente que atende as necessidades de filtrar os produtos de um dado conjunto, definido por uma Tag. Na grande maioria dos casos esse componente é implementado na página de listagem de produtos. Nesse artigo você confere como adicionar esse componente na sua loja, e como adaptar os parâmetros para seu negócio.
📘 Demonstração do componente
Visualize como é o componente acessando o Filtros de Produto Demo. Recomendamos utilizar a versão mais atualizada do componente: (
v18).
Tipos de filtro
Seção intitulada “Tipos de filtro”Por padrão, na Olist Ecommerce, à medida que vamos selecionando os filtros, as opções vão se mantendo mesmo que no momento não tenhamos produtos em determinada tag/atributo.
Temos a opção de ir “afunilando” os filtros conforme as opções vão sendo selecionadas. Para isso em admin/config/gerais/configuracoes-adicionais inserir o seguinte comando search_use_api: true.
Para desativar, no mesmo local inserir search_use_api: false.
Mostrar apenas os produtos disponíveis
Seção intitulada “Mostrar apenas os produtos disponíveis”Por padrão na plataforma sempre ao fazer a chamada de produtos, o {% load_products %} serão trazidos também os produtos indisponíveis.
Para excluir os produtos indisponíveis do front do site em /admin/config/gerais/extras marque a opção Listar somente produtos disponíveis?.
Implementação
Seção intitulada “Implementação”Os filtros de produtos, como todos os outros componentes, já vêm implementados nos templates da Olist Ecommerce. Caso você precise fazer esta implementação manualmente, ou alterar o seu funcionamento, deve-se, primeiro, dar atenção à importação dos arquivos JavaScript e CSS do componente. Junto a isso, você deve adicionar o objeto {{ aggregations }}.
🚧 Objeto
{{ aggregations }}Para que o filtro de produtos comporte-se de forma dinâmica, utilizamos o objeto
{{ aggregations }}. Os atributostypesepropertiesdesse objeto serão utilizados no código JavaScript de declaração do componente.
Entre todos os componentes, os filtros são os que possuem maior diversidade de formas, de acordo com as escolhas selecionadas nos seus parâmetros. O parâmetro mode é responsável por grande parte desta variação, pois controla a forma de exibição do filtro, podendo variar entre horizontal, vertical, popup ou aba lateral.
Para cada uma dessas opções do parâmetro mode, os templates contam com um conjunto específico de configurações no seu arquivo SCSS (assets/sass/content/tag/_filters.scss). Ao escolher uma das opções para usar, aconselha-se buscar em qual dos templates ela já foi implementada e utilizar o seu arquivo _filters.scss como ponto de partida do processo de personalização.
Caso opte-se por usar a aba lateral (drawer) no parâmetro mode, deve-se incluir um botão para controlar a exibição do componente, semelhante ao exemplo abaixo.
<button aria-label="Abrir filtro" class="button-default -outlined" id="open-filter-mobile"> <svg class="icon"> <use xlink:href="#icon-filter"/> </svg> FILTROS</button>const mobileButton = document.querySelector('#open-filter-mobile')
if (mobileButton) mobileButton.addEventListener('click', () => { FilterComponent.show()})Além do mode, há uma série de outros parâmetros que podem ser configurados de acordo com as necessidades da loja. Essas configurações se concentram na função setFilters do arquivo filters.js.
setFilters: function() { const { root } = FilterComponent; const tags = FilterComponent.setTagTypes() const properties = FilterComponent.setProperties() const customProperties = FilterComponent.setCustomProperties() const priceSettings = FilterComponent.setPricesRanges() const mode = (window.innerWidth <= 1024) ? 'drawer' : 'vertical'
const componentFilters = new Vnda.Component.ProductsFilter({ mode, filtersStartOpen: false, showAppliedFilters: false, hasSort: true, filterOnClick: false, resetMode: 'all', tags, properties, customProperties, price: priceSettings.price, priceProps: priceSettings.priceProps });
componentFilters.render(root); FilterComponent.instance = componentFilters root.dispatchEvent(new Event('vnda:filter-component-loaded'))},Como preparar o window.filterSettings
Seção intitulada “Como preparar o window.filterSettings”Para que o JavaScript monte o componente corretamente, o Liquid precisa renderizar a raiz do filtro e definir window.filterSettings antes da inicialização.
A raiz do componente pode ser renderizada desta forma:
<div id="component-products-filter-root"></div>O objeto window.filterSettings segue este formato:
window.filterSettings = { script: "{{ 'products-filter.v[version].js' | replace: '[version]', component_version | component_path }}", styles: "{{ 'products-filter.v[version].css' | replace: '[version]', component_version | component_path }}", aggregations: {{ aggregations | to_json }}, patterns: {{ patterns_list | to_json }}, has_sort: {{ has_sort }}, prices_range: [ {% if ranges %} {% for range in ranges %} {% assign values = range | strip | split: '-' %} [{{ values[0] | times: 1.00 }}, {{ values[1] | times: 1.00 }}], {% endfor %} {% endif %} ]}Esse objeto fornece ao componente:
- os assets que serão carregados
- as agregações da listagem
- os dados auxiliares para filtros visuais
- as faixas prontas de preço
- a flag de ordenação
Exemplo completo no Liquid
Seção intitulada “Exemplo completo no Liquid”O trecho abaixo mostra um exemplo completo de como preparar os dados do filtro no Liquid antes da inicialização do JavaScript.
{% load_banners position: 'filtro-preco' limit: 1 %}{% for banner in loaded_banners %} {% if banner.description %} {% assign ranges = banner.description | strip_html | split: ',' %} {% endif %}{% endfor %}
{% assign patterns_list = '' %}{% assign patterns = '' %}{% assign attributes_to_check = aggregations.properties.property1 %}
{% if aggregations.variant_attributes.size > 0 %} {% for custom_attribute in aggregations.variant_attributes %} {% assign custom_attributes = custom_attributes | concat: custom_attribute[1] %} {% endfor %}
{% assign attributes_to_check = attributes_to_check | concat: custom_attributes %}{% endif %}
{% for attribute in attributes_to_check %} {% if attribute.value contains '|' %} {% unless attribute.value contains '#' %} {% if patterns == blank %} {% load_tags types: 'estampa' %} {% assign patterns = loaded_tags %} {% endif %}
{% assign pattern_image = '' %} {% for pattern in patterns %} {% if attribute.value contains pattern.name %} {% assign pattern_image = pattern.image_url | resize: '25x25' %} {% endif %} {% endfor %}
{% capture patterns_list %} {{ patterns_list }},{ "value": "{{ attribute.value }}", "pattern": "{{ pattern_image }}" } {% endcapture %} {% endunless %} {% endif %}{% endfor %}
{% assign patterns_list = patterns_list | prepend: '[' | append: ']' | remove_first: ',' | from_json %}
{% assign component_version = '19' %}{% if current_shop.settings contains 'component_version_products_filter' %} {% assign settings_version = current_shop.settings.component_version_products_filter %} {% if settings_version != blank %} {% assign component_version = settings_version %} {% endif %}{% endif %}
{% unless has_sort %} {% assign has_sort = false %}{% endunless %}
<div id="component-products-filter-root"></div>
<script> window.filterSettings = { script: "{{ 'products-filter.v[version].js' | replace: '[version]', component_version | component_path }}", styles: "{{ 'products-filter.v[version].css' | replace: '[version]', component_version | component_path }}", aggregations: {{ aggregations | to_json }}, patterns: {{ patterns_list | to_json }}, has_sort: {{ has_sort }}, prices_range: [ {% if ranges %} {% for range in ranges %} {% assign values = range | strip | split: '-' %} [{{ values[0] | times: 1.00 }}, {{ values[1] | times: 1.00 }}], {% endfor %} {% endif %} ] }</script>Nesse fluxo:
aggregationsfornece categorias, atributos e preçopatterns_listalimenta filtros visuais de cor ou estamparangesalimentaprices_rangecomponent_versiondefine quais assets do componente serão carregados
Como o JavaScript monta categorias, atributos e preço
Seção intitulada “Como o JavaScript monta categorias, atributos e preço”Depois que window.filterSettings estiver disponível, o componente pode ser inicializado:
FilterComponent.init();A montagem dos filtros no JavaScript acontece em etapas.
Categorias
Seção intitulada “Categorias”As categorias são montadas por setTagTypes(), a partir de settings.aggregations.types.categoria.
setTagTypes: function() { const { settings } = FilterComponent; const tags = [];
if (settings.aggregations) { const aggregations = settings.aggregations
if (aggregations.types.categoria) tags.push({ title: 'Categoria', type: 'categoria', style: 'list', options: FilterComponent.sortArrayValues(aggregations.types.categoria, 'title') }) }
return tags}Atributos principais
Seção intitulada “Atributos principais”Os atributos principais são montados por setProperties(), a partir de:
aggregations.properties.property1aggregations.properties.property2aggregations.properties.property3
Exemplo de montagem de property1:
if (aggregations.properties.property1.length > 0) properties.push({ title: 'Cor', property: 'property1', style: 'colors', colorsProps: { showTitle: true, showColor: true, }, options: FilterComponent.sortArrayValues(aggregations.properties.property1, 'value'), patterns: settings.patterns})Exemplo de montagem de property2:
if (aggregations.properties.property2.length > 0) properties.push({ title: 'Tamanho', property: 'property2', style: 'list', options: FilterComponent.sortArrayValues(aggregations.properties.property2, 'value'), patterns: settings.patterns});Exemplo de montagem de property3:
if (aggregations.properties.property3.length > 0) properties.push({ title: 'Fragrância', property: 'property3', style: 'list', options: FilterComponent.sortArrayValues(aggregations.properties.property3, 'value'), patterns: settings.patterns});Atributos customizados
Seção intitulada “Atributos customizados”Os atributos adicionais são montados por setCustomProperties(), a partir de aggregations.variant_attributes.
setCustomProperties: function () { const { settings } = FilterComponent; const customProperties = [];
if (settings.aggregations) { const aggregations = settings.aggregations
if (aggregations.variant_attributes) { for (const [key, value] of Object.entries(aggregations.variant_attributes)) { const options = FilterComponent.sortArrayValues(value, 'value') const hasAnyColor = options.some(option => option.value.includes('|')); const style = hasAnyColor ? 'colors' : 'list';
customProperties.push({ title: key, style, ...(hasAnyColor && { colorsProps: { showTitle: true, showColor: true, } }), property: key, options, patterns: settings.patterns }); }; } }
return customProperties}Quando algum valor contém |, o atributo é tratado como filtro visual. Nos demais casos, ele é tratado como lista.
O preço é montado por setPricesRanges(), combinando os limites mínimo e máximo com as faixas prontas, quando existirem.
setPricesRanges: function() { const { settings, showPriceSlider } = FilterComponent let priceSettings = { price: false, priceProps: false }
if (showPriceSlider) { priceSettings.price = [ settings.aggregations.min_price, settings.aggregations.max_price ] }
if (settings.prices_range.length > 0) { priceSettings.priceProps = { mode: 'options', options: settings.prices_range } priceSettings.price = [ settings.aggregations.min_price, settings.aggregations.max_price ] }
return priceSettings}Criação do componente final
Seção intitulada “Criação do componente final”Depois de montar categorias, atributos e preço, setFilters() reúne essas estruturas e instancia o Vnda.Component.ProductsFilter.
setFilters: function() { const { root, settings } = FilterComponent; const tags = FilterComponent.setTagTypes(); const properties = FilterComponent.setProperties(); const customProperties = FilterComponent.setCustomProperties(); const priceSettings = FilterComponent.setPricesRanges(); const hasSort = settings.has_sort || false;
const componentFilters = new Vnda.Component.ProductsFilter({ tags, properties, customProperties, price: priceSettings.price, priceProps: priceSettings.priceProps });
componentFilters.render(root); FilterComponent.instance = componentFilters root.dispatchEvent(new Event('vnda:filter-component-loaded'))}🚧 Importante
setFilters()não busca os dados do filtro. Ela apenas reúne categorias, atributos e preço que já foram preparados anteriormente a partir dewindow.filterSettings.
Parâmetros do componente
Seção intitulada “Parâmetros do componente”Os parâmetros definem as características dos Filtros de Produto. Veja na tabela abaixo os parâmetros e o que eles determinam no componente. Note que os parâmetros que possuem valores padrões estão sinalizados na coluna Default, e as opções de entrada que o parâmetro pode receber estão apresentados na coluna Tipos/Opções de entrada.
| Parâmetro | Descrição | Tipos/Opções | Default | Obrigatório |
|---|---|---|---|---|
mode | Define como o componente é exibido. | drawer, horizontal ou vertical | drawer | Não |
filtersStartOpen | Define se as opções de filtro iniciarão abertas. Aplica-se aos modos drawer e vertical. | true ou false | false | Não |
showAppliedFilters | Define se a seção de filtros aplicados será exibida. | true ou false | false | Não |
sideDrawer | Define a posição do componente quando o modo for drawer. | right ou left | right | Não |
hasInputSearch | Habilita o campo de busca. | true ou false | false | Não |
hasSort | Habilita a ordenação dos resultados. | true ou false | false | Não |
filterOnClick | Carregamento automático ao clicar em uma opção do filtro. | true ou false | false | Não |
resetMode | Define como limpar os filtros: um botão para tudo (all), para cada (each) ou nenhum (none). | all, each ou none | none | Não |
tags | Define o filtro de categorias (tags). Veja parâmetro tags. | * | * | Não |
properties | Define o filtro de propriedades (permite múltiplos tipos). | * | * | Não |
sort | Forma de ordenação (requer hasSort ativo). Veja parâmetro sort. | grid ou list | * | Não |
price | Carrega valores de limite inferior e superior para o filtro de preço. | * | * | Não |
priceProps | Define opções de valores para o filtro de preço. Veja parâmetro priceProps. | * | * | Não |
patterns | URLs que definem estampas para filtros de propriedade. Veja parâmetro patterns. | * | * | Não |
Parâmetro tags
Seção intitulada “Parâmetro tags”tags = aggregations.types.categoria && [{ title: "Categoria", type: "categoria", style: "list", options: aggregations.types.categoria}];Parâmetro sort
Seção intitulada “Parâmetro sort”sort: { style: "grid", // estilo de como as opções são mostradas ( 'grid' ou 'list')};Parâmetro priceProps
Seção intitulada “Parâmetro priceProps”priceProps: { mode: 'options', options: [ [0, 99.99], [100, 199.99], [200, 299.99] ]}Parâmetro patterns
Seção intitulada “Parâmetro patterns”patterns: [ { "value": "Xadrez | estampa-xadrez", "pattern": "http://via.placeholder.com/30x30" }],Eventos
Seção intitulada “Eventos”A partir da versão 15, os filtros disparam eventos toda vez que: o filtro for carregado, uma opção de filtro for aberta, uma opção de filtro for fechada ou quando o filtro for fechado (caso o mode seja drawer). O trecho abaixo indica os eventos:
// Evento disparado sempre que uma opção de filtro for abertawindow.addEventListener('vnda:filter-component-item-open', (event) => { console.log(event)})
// Evento disparado sempre que uma opção de filtro for fechadawindow.addEventListener('vnda:filter-component-item-close', (event) => { console.log(event)})
// Evento disparado quando o filtro for carregadowindow.addEventListener('vnda:filter-component-loaded', (event) => { console.log(event)})
// Evento disparado quando o filtro for fechado. Somente para o mode drawerwindow.addEventListener('vnda:filter-component-closed', (event) => { console.log(event)})O método disponível para esse componente é o toggle(). Ele abre o filtro se ele estiver fechado e fecha o drawer se ele estiver aberto (para os casos de mode drawer).
Exemplo de implementação
Seção intitulada “Exemplo de implementação”Confira como foi feita a implementação do filtro de produtos no Template1 acessando os arquivos: