0

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?

WoJ
  • 27,165
  • 48
  • 180
  • 345

2 Answers2

2

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>
Linus Borg
  • 23,622
  • 7
  • 63
  • 50
  • Thank you Linus - this is excellent. I could not make out that a function (defined in the Vue instance) is expected. Now it works. I will keep my way of handling the chnage (that is - to emit an Array) as it will be populated in the component independently of the delete function (I will post an answer with that code). Thanks again, I understood a lot from your working example. – WoJ Jun 08 '17 at 10:23
  • Actually I realized that I was using `_.filter()` wrong and therefore also understood your comment about sending a single user (what I wanted to do is to delete that user, not to single it out - this is corrected in the code) – WoJ Jun 08 '17 at 10:26
0

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>
WoJ
  • 27,165
  • 48
  • 180
  • 345