Search code examples
javascriptvue.jsbootstrap-vue

Filter BootstrapVue table using comma separated string with OR condition in input field


I have a BootstrapVue table that looks like this;

enter image description here

The code(credit goes to this answer) for the table is here;

new Vue({
  el: '#app',
  data() {
    return {
      filter: '',
      items: [
        { id: 1, first_name: "Mikkel", last_name: "Hansen", age: 54 },
        { id: 2, first_name: "Kasper", last_name: "Hvidt", age: 42 },
        { id: 3, first_name: "Lasse", last_name: "Boesen", age: 39 },
        { id: 4, first_name: "Kasper", last_name: "Hansen", age: 62 },
        { id: 5, first_name: "Mads", last_name: "Mikkelsen", age: 31 },
      ]
    }
  }
})

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue@2.6.1/dist/bootstrap-vue.min.js"></script>

<link href="https://unpkg.com/bootstrap-vue@2.6.1/dist/bootstrap-vue.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap@4.4.1/dist/css/bootstrap.min.css" rel="stylesheet" />

<div id="app" class="p-5">
  <b-input v-model="filter" placeholder="Filter table.."></b-input>
  <hr />
  <b-table :items="items" :fields="fields" :filter="filter">
  </b-table>
</div>

Based on the above code, table rows will be filtered based on the string inside the input field. Unfortunately, this is not what I want. What I want is for the input field to be able to accept a comma-separated string and the sub-strings can be used to filter the table rows in an OR condition. For example, if the string is 54, Hvidt, then the first and second rows will be filtered.

I am using BootstrapVue, vue.js 2.6


Solution

  • BootstrapVue allows writing your own logic for filtering, using a custom filter function.

    In order for it to work, you need:

    • a :filter specified
    • the filter value to be truthy. If it's falsey, the filter function is bypassed (no filtering occurs).
    • a :filter-function specified, taking two arguments: row and current filter value (optional and not needed: you can read it from this.filter).

    Here's an example, splitting current filter value by , (comma), normalising the fragments (trim & toLowerCase), and returning whether or not any fragment is found within the normalised values of the row.

    new Vue({
      el: '#app',
      data() {
        return {
          filter: '54, Hvidt',
          items: [
            { id: 1, first_name: "Mikkel", last_name: "Hansen", age: 54 },
            { id: 2, first_name: "Kasper", last_name: "Hvidt", age: 42 },
            { id: 3, first_name: "Lasse", last_name: "Boesen", age: 39 },
            { id: 4, first_name: "Kasper", last_name: "Hansen", age: 62 },
            { id: 5, first_name: "Mads", last_name: "Mikkelsen", age: 31 }
          ]
        }
      },
      methods: {
        normalizeString(s) {
          return `${s}`.trim().toLowerCase();
        },
        filterFn(row) {
          return this.filter.split(',')
            .map(this.normalizeString)
            .some(
              term => Object.values(row)
                .map(this.normalizeString)
                .some(
                  val => val.indexOf(term) > -1
                )
            );
        }
      }
    })
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
    <script src="https://unpkg.com/bootstrap-vue@2.6.1/dist/bootstrap-vue.min.js"></script>
    
    <link href="https://unpkg.com/bootstrap-vue@2.6.1/dist/bootstrap-vue.css" rel="stylesheet" />
    <link href="https://unpkg.com/bootstrap@4.4.1/dist/css/bootstrap.min.css" rel="stylesheet" />
    
    <div id="app" class="p-5">
      <b-input v-model="filter" placeholder="Filter table..."></b-input>
      <hr />
      <b-table :items="items" :filter="filter" :filter-function="filterFn">
      </b-table>
    </div>

    Important note: the filter function provided here is demonstrative, it doesn't take into account BootstrapVue's built-in filter-ignored-fields and filter-included-fields. Nor does it take into account the filterByFormatted field definition.

    If you need any of those features, you'll need to improve it yourself.

    I guess it's needless to point out the filter function has access to the entire component and can be modified in any way. The row is displayed if the function returns a truthy value and is not displayed if it returns a falsey value.