Vue-select: Missing prop in 2.4.0: `filter-function` / `filter-by`

Created on 6 Mar 2018  路  14Comments  路  Source: sagalbot/vue-select

The documentation for VueSelect claims there is a filter-function property for specifying how to filter the list of options: https://sagalbot.github.io/vue-select/docs/Api/Props.html

Looking at the source code for Select.vue, however, it looks like this was renamed to filter-by

Looking at the output in the /dist/ directory, however, this property is completely gone in version 2.4.0. I cannot find any property which has the desired functionality

Most helpful comment

@stevendesu that's really important feedback, thanks. 2.5.0 is basically ready to go. I need to merge feature/filterable-prop. I got stuck spending way too much time on naming wanting the API to be perfect. Feel free to PR against that branch.

All 14 comments

Reviewing the commits, it looks like the last build (2.4.0) was compiled January 9th, but the filter-function property was added January 13th. Since then it seems to have been renamed several times:

  • filter-comparator
  • filter-match
  • filter-by

However there hasn't been a new release since it was added

Is there a schedule for releases? The project I'm working on requires a multi-select for over 60,000 numbers, however it's not as simple as "all numbers 1 through 60,000" -- it's a sparse list of numbers ranging from 1 to 350,000

With the current search logic if I type "5" the whole app locks up for 10 seconds while it attempts to render roughly 10,000 elements including "15", "25", "35", etc. Only to filter further when I type the next digit.

I would like to customize the logic to optimize it for my use case: only return numbers which start with the digit I've typed (and if that's still too slow, only return returns after 2 digits have been typed)

One other feature that might be nice, but isn't entirely necessary, is the ability to choose a filter function based on what they've entered into the search field. In my particular use case, I may eventually extend it from just numbers to include a text string for what the number means: 7843 - Time Warner Cable. In this case if they type a number ("7") I can filter for any results that start with that number, but if they type a letter then I have to filter for every result that contains that letter. This could be done using currying like so:

<template>
    <v-select
        :options="options"
        v-model="selected"
        :get-filter-function="getFilterFunction"
    ></v-select>
</template>

<script>
export default {
    data() => ({
        options: [...],
        selected: null
    }),
    methods: {
        getFilterFunction(search) {
            if (isNaN(parseInt(search)) {
                return function(option, label, search) {
                    return label.toLowerCase().indexOf(search.toLowerCase()) >= 0;
                }
            } else {
                return function(option, label, search) {
                    return label.indexOf(search) == 0;
                }
            }
        }
    }
};
</script>

Looking a bit closer at the source, it looks like the new filter prop (also not in 2.4.0) would allow for my currying solution:

<template>
    <v-select
        :options="options"
        v-model="selected"
        :filter="filter"
    ></v-select>
</template>

<script>
export default {
    data() => ({
        options: [...],
        selected: null
    }),
    methods: {
        filter(options, search) {
            if (isNaN(parseInt(search)) {
                return options.filter(option => {
                    let label = this.getOptionLabel(option);
                    return label.toLowerCase().indexOf(search.toLowerCase()) >= 0;
                });
            } else {
                return options.filter(option => {
                    return option.indexOf(search) == 0;
                }
            }
        }
    }
};
</script>

So really I'm just waiting on a 2.5.0 release

After locally building the latest commit to test out the functionality, there is one small change I would recommend or request. Currently the filter method is only being called if this.search.length is not false-y (that is, if they've typed something into the search). Otherwise it just returns _all options_

In my case (with roughly 60,000 options) this means that when I first click into the search box, everything freezes before I even get an opportunity to type in the first digit.

By running the filter method every time (even when nothing has been typed) it can be left to the discretion of the developer if they want to return any values when no search input has been provided

@stevendesu that's really important feedback, thanks. 2.5.0 is basically ready to go. I need to merge feature/filterable-prop. I got stuck spending way too much time on naming wanting the API to be perfect. Feel free to PR against that branch.

Is the issue related to a custom filter though? Wouldn't the same happen for such many records even when not using custom filter at all?

@ropmansk It's true that there is an issue for many records freezing the browser - whether or not you use custom filters. I was interested in custom filters so I could implement my own solution to the larger issue - but some people may want a simpler solution like a "max results returned" property with a sensible default like 25

Then whatever the filter returns, you only render results.slice(0, maxResults)

Is there any way to use the filter function in 2.4 (or previous release) ?

I don't think so... probably only master branch

@gergo2007 looks like you need to use the latest commit on the master brach and build the component yourself, at the moment. But yeah, the filter prop was a must-have, thank you!

Hi all,

if you would like to add a filter-function, but don't want to compile the source yourself: Extend the VueSelect component and override the filteredOptions computed property.

<script>
import VueSelect from 'vue-select'

export default {
  extends: VueSelect,
  name: 'ExtendedSelect',
  computed: {
    filteredOptions() {
      if (!this.filterable && !this.taggable) {
        return this.mutableOptions.slice()
      }
      let options = this.mutableOptions.filter((option) => {
        if (typeof option === 'object' && option.hasOwnProperty(this.label)) {
          // add line here
          return this.filterFunc(option.label)
        } else if (typeof option === 'object' && !option.hasOwnProperty(this.label)) {
          return console.warn(`[vue-select warn]: Label key "option.${this.label}" does not exist in options object.\nhttp://sagalbot.github.io/vue-select/#ex-labels`)
        }

        // add line here
        return this.filterFunc(option)
      })
      if (this.taggable && this.search.length && !this.optionExists(this.search)) {
        options.unshift(this.search)
      }
      return options
    },
  },
  methods: {
    filterFunc (option) {
      // My filter function replaces spaces with regex '.+' to match my sql query
      var str = this.escapeRegExp(this.search).toLowerCase().replace(' ', '.+')
      var regex = new RegExp(str, "g");
      return option.toLowerCase().search(regex) > -1
    },
    /**
     * https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
     */
    escapeRegExp (str) {
      return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
    },
  }
}
</script>

This is my approach until the next version is released.

@mortenege thanks you so much.

I modify your function to compatible my project. Search (utf8 character) related @issue #517

filterFunc (option) {
            // My filter function replaces spaces with regex '.+' to match my sql query
            //option = stripAccents(option);

            option = option.normalize('NFD').replace(/[\u0300-\u036f]/g, "");
            let _search = this.search.normalize('NFD').replace(/[\u0300-\u036f]/g, "");

            var str = this.escapeRegExp(_search).toLowerCase().replace(' ', '.+');
            var regex = new RegExp(str, "g");
            return option.toLowerCase().search(regex) > -1;
        },  

This prop is finally shipping in v2.5.0. There's about 18 PRs that have been merged into the release and lots of new features. Should solve a lot of folks issues.

for those of you still looking, here's how I managed to change the function globally

import vSelect from 'vue-select'
Vue.component('v-select', {
    extends: vSelect,
    props: {
        filterBy: {
            type: Function,
            default(option, label, search){
                return stringMatchesSearch(label || '', search);
            }
        }
    }
});
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jluterek picture jluterek  路  3Comments

lau-a picture lau-a  路  3Comments

rafalolszewski94 picture rafalolszewski94  路  3Comments

rudykaze picture rudykaze  路  3Comments

threeaccents picture threeaccents  路  3Comments