9

I am using Laravel and trying to learn Vue.js. I have a delete request that is working properly and deleting the object from the database. The problem is that it is not being removed from the DOM after the successful deletion. I am using the $remove method and passing it the full object, so I know I'm missing something.

As a side note, I have a main.js as an entry point with a PersonTable.vue as a component. The PersonTable.vue holds the template and script for that template.

Here is my Laravel view:

<div id="app">
    <person-table list="{{ $persons }}">

    </person-table>
</div>

And here is my `PersonTable.vue:

<template id="persons-template">
    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <h1>Persons List</h1>
                <table class="table table-hover table-striped">
                    <thead>
                    <tr>
                        <td>First Name</td>
                        <td>Last Name</td>
                        <td>Email</td>
                        <td>Gender</td>
                    </tr>
                    </thead>
                    <tbody>
                    <tr v-for="person in list">
                        <td>{{person.first_name }}</td>
                        <td>{{person.last_name }}</td>
                        <td>{{person.email }}</td>
                        <td>{{person.gender }}</td>
                        <td><span @click="deletePerson(person)">X</span>
                    </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</div>
</template>

<script>
export default {

    template: '#persons-template',

    props: ['list'],

    methods: {
        deletePerson: function(person) {
            this.$http.delete('/person/' + person.id).then(
                function(response) {
                    this.persons.$remove(person);
                }
            );
        }

    },

    created: function() {
        this.persons = JSON.parse(this.list);
    }

};
</script>

And my main.js entry point:

var Vue = require('vue');

Vue.use(require('vue-resource'));

var Token = document.querySelector('meta[name="_token"]').getAttribute('content');

Vue.http.headers.common['X-CSRF-TOKEN'] = Token;

import PersonTable from './components/PersonTable.vue';

new Vue({


    el: '#app',

    components: { PersonTable },

})
dericcain
  • 2,182
  • 7
  • 30
  • 52
  • Are you updating the Vue.data after completing the delete request? – ggdx Mar 21 '16 at 13:16
  • try to handle the error response in your post request to see what are you getting, if you are getting an error, then the part of your code that remove the data is never executed – Yerko Palma Mar 21 '16 at 14:45
  • @DanWhite No. At least I am not explicitly updating it. Not sure how to do that. – dericcain Mar 21 '16 at 15:25
  • @YerkoPalma I added a `console.log(person)` after the `this.persons.$remove(person)` and the `console` is showing the object of the clicked person. – dericcain Mar 21 '16 at 15:26
  • You should remove the created method in your component, I think that this is what is causing the problem. Why do you need this line? Tell me if I am right and I will post it as answer... But I think this is it :/ – Hammerbot Mar 23 '16 at 15:34
  • @El_Matella If I do not wait for the `created` event, the JSON is not parsed correctly. I think that I need to somehow sync the data property with the created property but I'm not sure how. Of course, I could be completely wrong too – dericcain Mar 23 '16 at 15:36
  • Ok, I didn't read well what you did but I think it is because you are not using the $set method and Vue is losing reactivity, I posted an answer tell me if it works :) I don't think that you need to sync any data here, the list is into your component. You would need to think it if your list was out of the component – Hammerbot Mar 23 '16 at 15:45

3 Answers3

7

I think you need to bind this to the response function:

function(response) {
    this.persons.$remove(person);
}.bind(this)

That way when you do this.persons you are still referring to the Vue component

edit: could try -

props:['personJson'],
data:function(){
  return {
    persons:[]
  }
},
ready:function(){
  this.persons = JSON.parse(this.personJson)
}

Thinking maybe since persons is a string initially, Vue isn't binding the reactive capabilities properly?

Jeff
  • 24,623
  • 4
  • 69
  • 78
  • I tried that and it's still not working. :( When looking in the Vue dev tools, the object is not being removed when I click delete. Now, once I refresh, it is removed since it is, in fact, being removed from the database. – dericcain Mar 21 '16 at 17:32
  • hmm. Does this issue persist if you use `track-by="index"` or if you remove that parameter completely? – Jeff Mar 21 '16 at 17:36
  • Okay.. I was wrong. It does delete from the array. The view component is not updating. I was thinking it would be "live" in the dev tools but it is not. I removed the `track-by="id"` and it is still doing the same thing. Is there a way to update the view? I'm thinking that I read that there is no need to do that. If that's the case, then why isn't this working? – dericcain Mar 21 '16 at 17:40
  • This gives me an error of `Uncaught SyntaxError: Unexpected token u`. Makes me think the JSON is not being parsed correctly or something. – dericcain Mar 23 '16 at 16:54
  • did you update the prop to be `personJson`? Unexpected token `u` generally means you JSON parsed something that was `undefined`. Should be `` – Jeff Mar 23 '16 at 17:07
  • I edited the original question because I had too many variables named `persons` and it was making it hard to separate what did what. Essentially, I replaced everywhere that you had `personJson` with `list`. Now, I am getting 6,000+ warnings in the console for ` Duplicate value found in v-for="person in list": "\"". Use track-by="$index" if you are expecting duplicate values.` and no data is displaying in the table. – dericcain Mar 23 '16 at 17:19
  • in the data function, instantiate `persons` to an empty array. in the `ready()` function, decode `list` and set that value to `this.persons`. then do `v-for="person in persons"`. The error you're getting means you're looping through a big JSON string. so change `v-for` to use `persons`, and do the decoding in `ready()` instead of `created()` to make sure the prop has been set up already (not sure if that matters) – Jeff Mar 23 '16 at 17:40
2

I think that you need to use the this.$set in your created method, if you don't, I am afraid that Vue would lose reactivity.

In your created method, could you try the following:

export default {

    template: '#persons-template',

    props: ['persons'],

    methods: {
        deletePerson: function(person) {
            var self = this;
            this.$http.delete('/person/' + person).then(
                function(response) {
                    self.persons.$remove(person);
                }
            );
        }

    },

    created: function() {
        this.$set('persons',JSON.parse(this.persons));
    }

};
Hammerbot
  • 15,696
  • 9
  • 61
  • 103
  • I tried that and it's not working. It is doing the same thing. When I `console.log(response)`, I am getting a response object with a `200` and the person is actually deleted from the database, but the row is not removed from the table. What gives??? – dericcain Mar 23 '16 at 15:46
-2

Finally figured it out. I needed to pass the JSON data to my data property of the component. Here is the code.

In the blade file:

<div id="app">
    <person-table list="{{ $persons }}">

    </person-table>
</div>

In my PersonTable.vue file:

<template id="persons-template">
    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <h1>Persons List</h1>
                <table class="table table-hover table-striped">
                    <thead>
                    <tr>
                        <td>First Name</td>
                        <td>Last Name</td>
                        <td>Email</td>
                        <td>Gender</td>
                    </tr>
                    </thead>
                    <tbody>
                    // Notice how I am using persons here instead of list
                    <tr v-for="person in persons">
                        <td>{{person.first_name }}</td>
                        <td>{{person.last_name }}</td>
                        <td>{{person.email }}</td>
                        <td>{{person.gender }}</td>
                        <td><span class="delete person" @click="deletePerson(person)"><i class="fa fa-close"></i></span>
                        </td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</template>

<script>
    export default {

        template: '#persons-template',

        props: ['list'],

        data: function() {
            return {
                persons: []
            }
        },

        methods: {
            deletePerson: function(person) {
                this.$http.delete('/person/' + person.id).then(
                    function(response) {
                        this.persons.$remove(person);
                     }
                );
            },
        },

        created: function() {
            // Pushing the data to the data property so it's reactive
            this.persons = JSON.parse(this.list);
        },

    };
</script>

Thanks to everyone for their contributions. I almost ditched Vue because of how long it has taken to fix this error.

Community
  • 1
  • 1
dericcain
  • 2,182
  • 7
  • 30
  • 52
  • may I ask why you chose to accept your own answer after we worked through it together on mine? – Jeff Mar 23 '16 at 17:46
  • @Jeff While your answer was very helpful (and I did upvote it) it was not the full answer. I still had to make some changes after your answer. If it would have been the solution, then I certainly would have marked it as the answer. Believe me, I lost 50 points because of the bounty even though I selected my own answer, so I have nothing to gain other than to point people to the correct solution. Make sense? – dericcain Mar 23 '16 at 17:49
  • @Jeff Wow! Did you give me a downvote because I did not select your answer? – dericcain Mar 23 '16 at 17:50
  • I worked through it with you over a couple days and my instructions are very precisely the code you just posted. My instructions in the comments and our discussion are this exact code. Yes I am the downvote on your answer. The only difference in my answer's code and yours is you chose to use `list` instead of `personJson` – Jeff Mar 23 '16 at 17:51
  • That's hilarious. I will mark your answer correct so that you can feel good about having answered the question. Just know that it is not the full solution. You never specifically mentioned to update the blade file property. Also, you cannot use `personJson` because when the blade file is rendered, the prop is put in lowercase to make `personjson`, which gives an error. Again, not the solution. What I posted is the FULL solution to the problem which I stated. Like I said, I will mark yours the answer so you can be happy. – dericcain Mar 23 '16 at 17:56
  • I don't mean to be hostile. I am not able to remove my vote on your answer at this point unless it is edited. As I did mention in the comments, you need to use kebab-case on the prop and it will come in as camelCase: ``. I answer a lot of Vue related questions here so hopefully I can be more helpful next time for you. Thanks. – Jeff Mar 23 '16 at 18:01