Home » vue » VueJS: filter select lists

VueJS: filter select lists

Posted by: admin November 26, 2021 Leave a comment

Questions:

I have a VueJS application. I have two lists: A, B

  • A contains all elements
  • B contains some elements from A

I have <select> tags. Each select tag must hold the value of a B element, and should have the option to select other values of A which are not contained in B.

Question: how can I properly filter the elements of A which don’t exist in B, with exception for the element the tag has selected at that point? A generic solution is to iterate over each element and unset the undesired ones from lists. The problem is Vue has reactive properties, which means when I change the list for a custom select, change is propagated to other selects as well.

I’ve wrapped my head for over an hour. I believe I’m building scenarios for a simple question which has a simple solution, but I can’t find it. Please assist me.

const app = new Vue({
  el: '#app',
  data() {
    return {
      b: [],
      a: [
        1, 2, 3, 4, 5
      ],
    }
  },
  methods: {
    filterElements() {
      return this.a.filter((a, a_index) => {
        return !this.b.find((b, b_index) => {
          return a === b
        })
      })
    }
  }

})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <template v-for="i in b.length">
	<select v-model="b[i-1]">
	    <option value="0" disabled>Select a value</option>
	    <!--<option v-for="(item, index) in filterElements()" :value="item">{{ item }}</option> doesn't work -->
	    <option v-for="(item, index) in a" :value="item">{{ item }}</option>
	</select>
	<br>
			
  </template>
  <br>
  <button @click="b.push(0)">+</button>
  <br>
  <br>
  <div>Selected elements: {{ b.join(', ') }}</div>
</div>

Answers:

You can simply use a computed property to contained the filtered version of A

To track changes in B you have to use $set to update the elements in B.
Otherwise B will not be reactive. More information here, https://vuejs.org/v2/guide/reactivity.html#For-Arrays

const app = new Vue({
  el: '#app',
  data() {
    return {
      b: [],
      a: [
        1, 2, 3, 4, 5
      ],
    }
  },
  computed: {
    filteredA() {
      return this.a.filter((a, a_index) => {
        return !this.b.find((b, b_index) => {
          return a === b
        })
      })
    }
  },
  methods: {
    updateB(e, i) {
      // use $set to update array elements to maintain reactivity
      this.$set(this.b, i, +e);
    }
   }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <template v-for="i in b.length">
	<select :value="b[i-1]" @input="updateB($event.target.value, i-1)">
            <option :value="b[i-1]">{{b[i-1] || "Select a value"}}</option>
	    <!--<option v-for="(item, index) in filterElements()" :value="item">{{ item }}</option> doesn't work -->
	    <option v-for="(item, index) in filteredA" :value="item" :key="`${item}-${index}`">{{ item }}</option>
	</select>
	<br>
			
  </template>
  <br>
  <button @click="b.push(0)">+</button>
  <br>
  <br>
  <div>Selected elements: {{ b.join(', ') }}</div>
</div>

###

No need to manually filter. Here is a solution with looping your a array, and binding the selected values to b:

https://stackoverflow.com/a/50648659/13602136

###

If I understand correctly you want to do an array substraction (element in A that are not in B):

function aNotInB(a, b) {
  return a.filter(aElement => {
    // .find() returns undefined if nothing is found
    // (b.find(element) === undefined) returns true if a element is not found in b
    return b.find(bElement => aElement === bElement) === undefined
  })
}

a = [1, 2, 3, 4, 5]
b = [2, 3, 4]


console.log(aNotInB(a,b))    // [1, 5]