Search code examples
javascriptlistvue.jsvue-componentv-for

Error: "Error in v-on handler: "TypeError: this.filter is undefined"" in a list rendering in vue?


I am trying to build a component that creates filter buttons and then sends the type attribute in the filters object through the event bus to be handled elsewhere in my app. However, when I added the array of objects (filters) in the data section, I am getting an error of this.filter is not defined when I click on a button.

I would like to keep the filters array in this component because I am also trying to dynamically change the active class to whichever button has been clicked.

Am I missing something that has to do with props? Why am I unable to display the buttons when the data and v-for was on another component? These were the questions I have been asking myself in order of solving this issue.

<template>
  <div>
    <button
      v-for="(filter, index) in filters"
      :key="index"
      :class="{ active: index === activeItem }"
      @click="emitFilter(), selectItem(index)"
      :filter="filter"
    >
      {{ filter.name }}
    </button>
  </div>
</template>

<script>
import EventBus from '@/components/EventBus'
export default {
  props: {
    filter: { type: String }
  },
  data() {
    return {
      activeItem: 0,
      filterResult: '',
      filters: [
        { name: 'All', type: 'all' },
        { name: 'Holidays', type: 'holiday' },
        { name: 'Seasons', type: 'season' },
        { name: 'Events', type: 'custom' }
      ]
    }
  },
  methods: {
    emitFilter() {
      this.filterResult = this.filter
      EventBus.$emit('filter-catagories', this.filterResult)
    },
    selectItem(index) {
      this.activeItem = index
    }
  }
}
</script>

My button component is used in a filters component

<template>
  <div>
    <span>filters</span>
      <FilterBtn />
    </div>
  </div>
</template>

<script>
import FilterBtn from '@/components/FilterBtn'


export default {
  components: {
    FilterBtn
      }
  // data() {
  //   return {
  //     filters: [
  //       { name: 'All', type: 'all' },
  //       { name: 'Holidays', type: 'holiday' },
  //       { name: 'Seasons', type: 'season' },
  //       { name: 'Events', type: 'custom' }
  //     ]
  //   }
  // }
}
</script>

As you can see, the commented section is where I had my filters originally, but I had to move them to the button component in order to add the active class.


Solution

  • I'm assuming you were actually trying to access the filter iterator of v-for="(filter, index) in filters" from within emitFilter(). For this to work, you'd need to pass the filter itself in your @click binding:

    <button v-for="(filter, index) in filters"
            @click="emitFilter(filter)">
    

    Then, your handler could use the argument directly:

    export default {
      methods: {
        emitFilter(filter) {
          this.filterResult = filter
          //...
        }
      }
    }