Search in filters

Display a searchbox on top of facet filters

Display a searchbox on top of facet filters

When typing, the facet filters items are filtered

When typing, the facet filters items are filtered

For filters that contain so many values that it has become quite a chore to look through them all when filtering, you might want to have a dedicated search box to find things quickly. This feature can be enabled and configured per specific filter at Tweakwise App > Filtertemplates.

📘

Read our product guide on how to configure the Search within filters in detail.

Once this setting is enabled, our Frontend API response will contain extra fields for those specific filters:

  • issearchable: true/false boolean to indicate if search is available within the facet
  • searchplaceholder: placeholder text for the facet search
  • searchnoresultstext: text for when there are no results for the facet search

This information is needed to do a proper implementation in you platforms storefront.

<navigation>
  <facets>
    <facet>
      <facetsettings>
        ...
        <issearchable>true/false boolean to indicate if search is available within the facet</issearchable>
        <searchplaceholder>Placeholder text for the facet search</searchplaceholder>
        <searchnoresultstext>Text for when there are no results for the facet search</searchnoresultstext>
      </facetsettings>
      ...
    </facet>
  </facets>
  ...
</navigation>
{
  "navigation": {
    "facets": [
      {
        "facetsettings": {
          "issearchable": true, // boolean to indicate if search is available within the facet
          "searchplaceholder": "Placeholder text for the facet search",
          "searchnoresultstext": "Text for when there are no results for the facet search"
        }
      }
    ]
  }
}

The issearchable field marks an indicator that this feature is enabled for the specific filter. You should use this setting to determine whether to show or not to show the functional behavior in your frontend.

If set to true, we expect you to show a search box within your specific filter and develop the actual behavior yourself. No extra API request should be made for this search, as all information (filter values) is available in the given API response already. The actual search behavior should be executed client-side.

Example

<template>
    <div v-if="isSearchable" class="relative mb-4">
        <input v-model="searchTerm" :placeholder="searchPlaceholder"
            class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 text-sm placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-primary sm:leading-6" />

        <XMarkIcon v-if="searchTerm" class="absolute m-auto mr-2 inset-0 text-gray-400 size-6 cursor-pointer"
            @click="searchTerm = ''" />
    </div>

    <template v-if="searchTerm && visibleAttributes.length === 0">
        <div>{{ noResultText }}</div>
    </template>

    <div class="space-y-3">
        <div class="flex relative items-center" v-for="attribute in visibleAttributes" :key="attribute.attributeid">
            <span :class="['text-sm text-gray-600', attribute.isselected && 'font-semibold']">
                {{ attribute.title }}
            </span>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { computed } from 'vue';
import { XMarkIcon } from '@heroicons/vue/20/solid';

function normalize(str: string): string {
	// make sure the search works for characters with accents, etc
  const fallbackMap = {
    'ß': 'ss',
    'Æ': 'AE', 'æ': 'ae',
    'Ø': 'O', 'ø': 'o',
    'Œ': 'OE', 'œ': 'oe',
    'Þ': 'Th', 'þ': 'th',
    'Đ': 'D', 'đ': 'd',
    'Ł': 'L', 'ł': 'l',
    'Ħ': 'H', 'ħ': 'h'
  };

  return str
    .normalize('NFD')
    .replace(/\p{Diacritic}/gu, '')
    .split('')
    .map(char => fallbackMap[char] || char)
    .join('')
    .toLowerCase();
}

const props = defineProps<{
    facet: IApiNavigationFacet;
}>();

const searchTerm = ref<string>('lo');

const isSearchable = computed(() => {
    return props.facet.facetsettings?.issearchable ?? false;
})

const searchPlaceholder = computed(() => {
    return props.facet.facetsettings?.searchplaceholder ?? '';
})

const searchAttributes = computed(() => {
  if (!searchTerm.value) return props.facet.attributes;
  
		const processedSearchTerm = normalize(searchTerm.value.trim());

    return props.facet.attributes.filter((attribute) => {
        const processedAttributeName = normalize(String(attribute.title).trim());
        return processedAttributeName.includes(processedSearchTerm);
    });
});

const visibleAttributes = computed(() => {
    const amount = props.facet.facetsettings?.nrofshownattributes;
    const attributes = searchAttributes.value;

    if (!searchTerm.value) {
        return attributes?.slice(0, amount);
    }
    return attributes;
});

const noResultText = computed(() => {
    return props.facet.facetsettings?.searchnoresultstext ?? '';
})

</script>

Note: this example uses a normalize function to support a search with diacritic and special character support:

function normalize(str: string): string {
	// make sure the search works for characters with accents, etc
  const fallbackMap = {
    'ß': 'ss',
    'Æ': 'AE', 'æ': 'ae',
    'Ø': 'O', 'ø': 'o',
    'Œ': 'OE', 'œ': 'oe',
    'Þ': 'Th', 'þ': 'th',
    'Đ': 'D', 'đ': 'd',
    'Ł': 'L', 'ł': 'l',
    'Ħ': 'H', 'ħ': 'h'
  };

  return str
    .normalize('NFD')
    .replace(/\p{Diacritic}/gu, '')
    .split('')
    .map(char => fallbackMap[char] || char)
    .join('')
    .toLowerCase();
}