2

Yep, it's me again. I'm trying to filter an array based on an array of strings. So while a single string filter is easy with Vue...

<div v-for="item in items | filterBy 'words' in 'property'">

...multiple search strings becomes more complex. There's been several questions about how to do this on StackOverflow already, but very few answers. Currently I'm trying to repurpose the custom filter found here for my needs, but it's not working.

My use case: I have an array of groups (checkboxes) that the user can select to filter an array of people. Each person is assigned to one or more groups so if any one of their groups is selected by the user, that person should show up.

So my templates look like this:

    <template v-for="group in ensemble_groups">
      <input name="select_group[]" id="group_@{{ $index }}"
        :value="group"
        v-model="selected_groups"
        type="checkbox">
      <label for="group_@{{ $index }}">@{{ group }}</label>
    </template>

    <template v-for="person in cast | filterBy selectGroups">
      <div>@{{ person.actor_name }}</div>
    </template>

You see my custom filter selectGroups there? Here's my Vue arrays:

  selected_groups: [],
  ensemble_groups: ["Leads","Understudies","Children","Ensemble"],

  cast: [
    {
      actor_name: "Dave",
      groups: ["Leads"],
    },
    {
      actor_name: "Jill",
      groups: ["Leads"],
    },
    {
      actor_name: "Sam",
      groups: ["Children","Ensemble"],
    },
    {
      actor_name: "Austin",
      groups: ["Understudies","Ensemble"],
    },
  ],

And finally here's the custom filter. I can't tell if it's even being triggered or not, because when I click on a group checkbox, nothing happens.

filters: {
  selectGroups: function() {
    if (!selected_groups || selected_groups.length === 0) {
      return cast;
    }
    return this.recursiveFilter(cast, selected_groups, 0);
  }
},
methods: {
  recursiveFilter: function(cast, selected_groups, currentPosition) {
    if (currentPosition+1 > selected_groups.length)
      return cast;
    var new_cast;
    new_cast = cast.filter(function(person) {
      for (group of person.groups) {
        if (group.value == selected_groups[currentPosition])
          return true;
      }
    });
    return this.recursiveFilter(new_cast, selected_groups, currentPosition+1);
  }
}

So if the user selects Leads only Dave and Jill should appear. If the user then checks Children, Dave, Jill, and Sam should appear. I'm so close!

1
  • What I would love is a filterBy modifier that does something like <template v-for="person in cast | filterBy any(selected_groups) in 'groups'" Commented Oct 14, 2016 at 18:41

3 Answers 3

1

I would use a computed property instead of a filter and a method.

I'd go through each cast member and if any of their groups is in selected_groups I'd allow it through the filter. I'd so this using Array.some.

results: function() {
  var self = this
  return self.cast.filter(function(person) {
    return person.groups.some(function(group) {
      return self.selected_groups.indexOf(group) !== 1
    })
  })
},

Here's a quick demo I set up, might be useful: http://jsfiddle.net/crswll/df4Lnuw6/8/

Sign up to request clarification or add additional context in comments.

1 Comment

Just come across it at times really.
0

Since filters are deprecated (in v-for, see Bill's comment), you should get into the habit of making computeds to do filtery things.

(If you're on IE, you can't use includes without a polyfill; you can use indexOf...>=0 instead.)

new Vue({
  el: '#app',
  data: {
    selected_groups: [],
    ensemble_groups: ["Leads", "Understudies", "Children", "Ensemble"],

    cast: [{
      actor_name: "Dave",
      groups: ["Leads"],
    }, {
      actor_name: "Jill",
      groups: ["Leads"],
    }, {
      actor_name: "Sam",
      groups: ["Children", "Ensemble"],
    }, {
      actor_name: "Austin",
      groups: ["Understudies", "Ensemble"],
    }, ]
  },
  computed: {
    filteredCast: function() {
      const result = [];

      for (const c of this.cast) {
        if (this.anyMatch(c.groups, this.selected_groups)) {
          result.push(c);
        }
      }

      return result;
    }
  },
  methods: {
    anyMatch: function(g1, g2) {
      for (const g of g1) {
        if (g2.includes(g)) {
          return true;
        }
      }
      return false;
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.min.js"></script>
<div id="app">
  <template v-for="group in ensemble_groups">
    <input name="select_group[]" id="group_@{{ $index }}" :value="group" v-model="selected_groups" type="checkbox">
    <label for="group_@{{ $index }}">@{{ group }}</label>
  </template>

  <template v-for="person in filteredCast">
    <div>@{{ person.actor_name }}</div>
  </template>
</div>

2 Comments

Filters are deprecated in what version? That's good to know, as I'm only just beginning with Vue. I sure don't wanna start off with old code.
Filters on v-for are deprecated. You can use them in {{ ... }} though.
0
var demo = new Vue({
    el: '#demo',
    data: {
        search: 're',

        people: [
          {name: 'Koos', age: 30, eyes:'red'},
          {name: 'Gert', age: 20, eyes:'blue'},
          {name: 'Pieter', age: 12, eyes:'green'},
          {name: 'Dawid', age: 67, eyes:'dark green'},
          {name: 'Johan', age: 15, eyes:'purple'},
          {name: 'Hans', age: 12, eyes:'pink'}
        ]
    },
    methods: {
      customFilter: function(person) {
          return person.name.indexOf(this.search) != -1
          || person.eyes.indexOf(this.search) != -1
          ;
      }
    },
   
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div id="demo">
    <input type="text" class="form-control" v-model="search"/>
    
    <br/>
    <table class="table">
      <thead>
        <tr>
          <th>name</th>
          <th>eyes</th>
          <th>age</th>
        </tr>
      </thead>
      <tr v-for="person in people | filterBy customFilter">
        <td>{{ person.name }}</td>
        <td>{{ person.eyes }}</td>
        <td>{{ person.age }}</td>
      </tr>
  </table>
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.