0

I'm a frustrated Vue.js noobie coming from jQuery.

I'm trying to do something very basic for a simple project: Delete an article but only after ajax response. While waiting, for the response there's a spinner. (I don't want components or vue files. It's a simple, single file app)

I've been hacking away at it for a few days now and I can't grasp some basic concepts it seems. (Or I want it to behave like jquery)

Fiddle

      window.app = new Vue({
        el: '#app',
        data: {
          name: '',
          posts: [],
          loadingItems: [],
        },
        created() {
            this.fetchData(); 
        },        
        methods:{
            fetchData() {
                axios.get('https://jsonplaceholder.typicode.com/posts').then(response => {
                    this.posts = response.data.slice(0,20);

                });
            },
            removeItem(item) {
                
                var index = this.posts.indexOf(item);
                
                //var loadingIndex = this.loadingItems.push(item) - 1;
                //console.log(loadingIndex);
                
                item.isLoading = true;
                
                //Update vue array on the fly hack - https://vuejs.org/2016/02/06/common-gotchas/
                this.posts.splice(index, 1,item); 


                axios.post('//jsfiddle.net/echo/json/', "json='{status:success}'&delay=2")
                .then(response => {
                    
                    this.posts.splice(index, 1);
                    
                    //this.loadingItems.splice(loadingIndex, 1);
                    //this.loadingItems.pop(item);
                    
                    //item.isLoading = false;
                    //this.posts.splice(index, 1,item); 
                });                
            }
        },
        computed: {
          showAlert() {
            return this.name.length > 4 ? true : false
          }
        }
      })
<div id="app">
  <div v-for="(post,index) in posts" :key="post.id">
    
    <b-card class="mb-2">
      <b-card-title>{{post.title}}</b-card-title>
      <b-card-text>{{post.body}}</b-card-text>

      <a href="#" @click.prevent="removeItem(post)" class="card-link">
        Remove
        <span v-show="post.isLoading" class="spinner"></span>
      </a>

    </b-card>
  </div>
</div>

Works fine for deleting them 1 by 1 one but not when you click on multiple at the same time, since the index is different by the time the request comes back and it splices the wrong item.

What I've tried so far:

First, it took me a day to figure out that item.isLoading = true; won't work if it wasn't present when the data was first observed (or fetched). However, I don't want to add the property to the database just for a loading animation. So the workaround was to do this.posts.splice(index, 1,item); to "force" Vue to notice my change. Already feels hacky.

Also tried using an array LoadingItems and pushing them while waiting. Didn't work due to the same problem: don't know which one to remove based on index alone.

Studying the TODO app didn't help since it's not quite addressing handling async ajax responses or adding properties at runtime.

Is the best way to do it by using post.id and then trying to pass it and find it back in the array? Really confused and thinking jQuery would have been easier.

Any help will be appreciated. Thanks!

Miro
  • 8,402
  • 3
  • 34
  • 72

1 Answers1

1

Works fine for deleting them 1 by 1 one but not when you click on multiple at the same time, since the index is different by the time the request comes back and it splices the wrong item.

Don't save the index in a variable. Calculate it every time you need it:

removeItem(item) {
  item.isLoading = true;
  this.posts.splice(this.posts.indexOf(item), 1, item);
  axios.post('/echo/json/', "json='{status:success}'&delay=2")
    .then(response => {
        this.posts.splice(this.posts.indexOf(item), 1);
    });
}
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Awesome! Works great. Why didn't I think of that lol?.... Am I doing this right? Is there a better way of doing this with Vue? Looks a bit heavy. Thanks a lot! – Miro Oct 09 '20 at 20:32
  • 1
    @Miro Not sure if Vue has a special facility for that ([doesn't look like it](https://stackoverflow.com/questions/43046332/how-to-remove-an-item-from-an-array-in-vue-js)). If you need it in several places, and are not fundamentally opposed to modifying the built-in object prototypes (which is typically frowned upon, but personally I can't see anything wrong with it), then Sugar.js [has you covered](https://sugarjs.com/docs/#/Array/remove). There are quite a few other helpful additions in Sugar.js you might end up finding useful. – Tomalak Oct 10 '20 at 06:21