WoJ
WoJ

Reputation: 29993

Is this TypeError linked to a wrong this.$emit?

I am building a component which is supposed to get an Array of Objects from the parent, display them and allow for some of the Objects to be deleted (= the child sends to the parent a new Array).

A previous answer was very useful to understand the child -> parent communication but I am now stuck on an error I do no understand:

Vue.component('search-box', {
  template: '#search-box-template',
  props: ['who'],
  methods: {
    deleteID: function(p) {
      var user = _.filter(this.who, function(w) {
        return w.id === p.id
      })
      this.$emit('delete-id', user)
    }
  }
})
var vm = new Vue({
  el: '#root',
  data: {
    who: [{
      "name": "john",
      "id": 1
    }, {
      "name": "mary",
      "id": 2
    }]
  }
})
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>


<div id="root">
  Hello in parent
  <search-box v-bind:who="who" v-on:delete-id="who"></search-box>
  who is: {{who}}
</div>

<template id="search-box-template">
    <div>
        <ul>
            <li v-for="p in who">person: {{p.name}}, id: {{p.id}} <button v-on:click="deleteID(p)">delete</button></li>
        </ul>
    </div>
</template>

Running this code correcly lists the users, but when trying to delete one I get in the console

vue.js:1739 Uncaught TypeError: fns[i].apply is not a function
    at VueComponent.invoker (vue.js:1739)
    at VueComponent.Vue.$emit (vue.js:2210)
    at VueComponent.deleteID (search.html:31)
    at Proxy.boundFn (vue.js:170)
    at click (eval at makeFunction (vue.js:9323), <anonymous>:2:164)
    at HTMLButtonElement.invoker (vue.js:1743)
invoker @   vue.js:1739
Vue.$emit   @   vue.js:2210
deleteID    @   search.html:31
boundFn @   vue.js:170
click   @   VM9358:2
invoker @   vue.js:1743

What does this error mean? Is the $emit correct?

Upvotes: 0

Views: 246

Answers (2)

WoJ
WoJ

Reputation: 29993

Following up on Linus' excellent answer, for the record below is my corrected code which emits and Array as when a user is deleted.

Vue.component('search-box', {
  template: '#search-box-template',
  props: ['who'],
  methods: {
    deleteID: function(p) {
                var user = _.filter(this.who, function (w) {
                return w.id != p.id
            })
      this.$emit('delete-id', user)
    }
  }
})

var vm = new Vue({
  el: '#root',
  data: {
    who: [{
      "name": "john",
      "id": 1
    }, {
      "name": "mary",
      "id": 2
    }]
  },
  methods: {
    handleDelete(users) {
      this.who = users
    }
  }
})
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>


<div id="root">
  Hello in parent
  <search-box v-bind:who="who" v-on:delete-id="handleDelete"></search-box>
  who is: {{who}}
</div>

<template id="search-box-template">
    <div>
        <ul>
            <li v-for="p in who">person: {{p.name}}, id: {{p.id}} <button v-on:click="deleteID(p)">delete</button></li>
        </ul>
    </div>
</template>

Upvotes: 0

Linus Borg
Linus Borg

Reputation: 23978

There's two problems:

  1. You _.filter returns an arry, so you don't emit the user, but an array containing the user. That filter is also completely unnecessary, because palready is the user object that you intend to filter for. But that code is not the cause of the error.
  2. v-on:delete-id="who" ere you pass an object (who) to the event listener, but it expects a function. That's what's raising the error.

A working version of your code would look like this:

Vue.component('search-box', {
  template: '#search-box-template',
  props: ['who'],
  methods: {
    deleteID: function(p) {
      this.$emit('delete-id', p)
    }
  }
})

var vm = new Vue({
  el: '#root',
  data: {
    who: [{
      "name": "john",
      "id": 1
    }, {
      "name": "mary",
      "id": 2
    }]
  },
  methods: {
    handleDelete(person) {
      const index = this.who.indexOf(person)
      this.who.splice(index, 1)
    }
  }
})
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>


<div id="root">
  Hello in parent
  <search-box v-bind:who="who" v-on:delete-id="handleDelete"></search-box>
  who is: {{who}}
</div>

<template id="search-box-template">
    <div>
        <ul>
            <li v-for="p in who">person: {{p.name}}, id: {{p.id}} <button v-on:click="deleteID(p)">delete</button></li>
        </ul>
    </div>
</template>

Upvotes: 2

Related Questions