Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop multiple clicks vue.js

In my vue.js application there items can be removed.

The div element looks like this:

 <div class="ride-delete" @click="delete">
      <p>Delete</p>
 </div>

This is the method that handles the click:

methods: {
    delete ()
    {
      swal({
        title: "Weet u het zeker?",
        text: "Het is niet mogelijk deze handeling te herstellen!",
        cancelButtonText: 'Stop',
        type: "error",
        showCancelButton: true,
        confirmButtonColor: "#DD6B55",
        confirmButtonText: "Ja, verwijder deze rit.",
        closeOnConfirm: false
      }, () => {
        RideService.destroy(this.ride)
          .then(() => {
            swal({
              title: "Rit succesvol verwijderd",
              type: "success",
              showCancelButton: false,
              timer: 2000,
              showConfirmButton: false
            });
            this.$router.go('/administratie/ritten');
          });
      });
    }
  }

So how can I make sure that if the user clicks 3 times fast after each other there will only be send one request. Right now there are send 3. So the button should be disabled if the user clicks once on it.

--EDIT--

import swal from 'sweetalert';
import RideService from '../../services/RideService';

export default {

  data () {
    return {
      ride: { user: {}, location: {}, type: {} },
      deleting: false
    }
  },

  route: {
  data ({ to }) {
      return RideService.show(this.$route.params.rideId)
        .then(function(data) 
        {
          this.ride = data.data.ride;
        }.bind(this));
    }
  }, 

  methods: {
    remove ()
    {
      if (!this.deleting) {
        this.deleting = true
        swal({
          title: "Weet u het zeker?",
          text: "Het is niet mogelijk deze handeling te herstellen!",
          cancelButtonText: 'Stop',
          type: "error",
          showCancelButton: true,
          confirmButtonColor: "#DD6B55",
          confirmButtonText: "Ja, verwijder deze rit.",
          closeOnConfirm: false
        }, () => {
          RideService.destroy(this.ride)
            .then(() => {
              swal({
                title: "Rit succesvol verwijderd",
                type: "success",
                showCancelButton: false,
                timer: 2000,
                showConfirmButton: false
              });
              this.deleting = false
              this.$router.go('/administratie/ritten');
            });
        });

        this.deleting = false
      }  
    }
  }
}       
</script>

--EDIT 2--

<template>
  <div class="row center">
    <div class="panel ride">
      <div class="ride-title bar-underline">
        <div class="ride-item-title">
          <strong class="ride-item-title-body">Rit van {{ ride.created_at }}</strong>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Naam</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.user.name }}</p>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Locatie van</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.location.from }}</p>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Locatie naar</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.location.from }}</p>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Beschrijving</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.location.description }}</p>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Kmz</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.location.kmz }}</p>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>kmp</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.location.kmp }}</p>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Uren</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.location.hour }} uur</p>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Google maps</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.location.maps }}</p>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Datum</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.created_at }}</p>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Tijd</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.time }}</p>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Factureerbare tijd</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.billabletime }} uur</p>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Type</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.type.name }}</p>
        </div>
      </div>

      <div class="ride-item">
        <div class="ride-edit">
          <p>Edit</p>
        </div>

        <div class="ride-delete" @click="remove">
          <p>Delete</p>
        </div>
      </div>
    </div>
  </div>            
</template>

<script>

import swal from 'sweetalert';
import RideService from '../../services/RideService';

export default {

  data () {
    return {
      ride: { user: {}, location: {}, type: {} },
      processing: false
    }
  },

  route: {
  data ({ to }) {
      return RideService.show(this.$route.params.rideId)
        .then(function(data) 
        {
          this.ride = data.data.ride;
        }.bind(this));
    }
  }, 

  methods: {
    remove ()
    {
       if (this.processing === true) {
        return;
      } 
        this.processing = true
        swal({
          title: "Weet u het zeker?",
          text: "Het is niet mogelijk deze handeling te herstellen!",
          cancelButtonText: 'Stop',
          type: "error",
          showCancelButton: true,
          confirmButtonColor: "#DD6B55",
          confirmButtonText: "Ja, verwijder deze rit.",
          closeOnConfirm: false
        }, () => {
          RideService.destroy(this.ride)
            .then(() => {
              swal({
                title: "Rit succesvol verwijderd",
                type: "success",
                showCancelButton: false,
                timer: 2000,
                showConfirmButton: false
              });
              this.processing = false
              this.$router.go('/administratie/ritten');
            });
        });

        this.processing = false
    }
  }
}       
</script>
like image 272
Jamie Avatar asked Sep 21 '16 06:09

Jamie


3 Answers

Since Vue 2.1.4, there's a really simple solution to this:

Change:

<div class="ride-delete" @click="delete">
   <p>Delete</p>
</div>

To:

<div class="ride-delete" @click.once="delete">
   <p>Delete</p>
</div>

@click.once only runs the target method one time.

In my case, it solved a problem with login, where multiple clicks would append path chunks to the URL, like this: localhost:8000/admin/oms/admin/oms/admin/oms.

Here the description in the official Vue documentation: https://vuejs.org/v2/guide/events.html#Event-Modifiers

like image 182
Jenam Avatar answered Oct 06 '22 05:10

Jenam


I'd suggest storing the state of the async request in a data property (e.g. processing: false and when the user clicks on the element, set it to true), and then within the delete() method check the state to continue or stop the code. Finally, reset the state in success/failure handlers.

For instance:

new Vue({
  el: '#app',
  
  data: {
    processing: false
  },
  
  methods: {
    delete(el) {
      // terminate the function
      // if an async request is processing
      if (this.processing === true) {
        return;
      } 
      
      // set the async state
      this.processing = true;
      
      var paragraphs = Array.from(this.$el.querySelectorAll('p'));
      
      // simulating the async request
      setTimeout(() => {
        if (paragraphs.length) {
          paragraphs.shift().remove();
        }
        
        // on success or failure
        // reset the state
        this.processing = false;
      }, 3000);
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.16/vue.js"></script>
<div id="app">
  Processing: {{ processing }} <br>
  <button @click.prevent="delete()">
    Click here to delete a paragraph 
  </button>

  <p v-for="1 in 3">
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officiis officia adipisci, omnis cum odit modi perspiciatis aliquam voluptatum consectetur. Recusandae nobis quam quisquam magnam blanditiis et quos beatae quasi quia!
  </p>
like image 35
Hashem Qolami Avatar answered Oct 06 '22 06:10

Hashem Qolami


Try this

<div class="ride-delete" v-show="!deleting" @click="delete">
    <p>Delete</p>
</div>

methods: {
  delete ()
  {
    if (!this.deleting) {
      this.deleting = true

      swal({
        title: "Weet u het zeker?",
        text: "Het is niet mogelijk deze handeling te herstellen!",
        cancelButtonText: 'Stop',
        type: "error",
        showCancelButton: true,
        confirmButtonColor: "#DD6B55",
        confirmButtonText: "Ja, verwijder deze rit.",
        closeOnConfirm: false
      }, () => {
        RideService.destroy(this.ride)
          .then(() => {
            swal({
              title: "Rit succesvol verwijderd",
              type: "success",
              showCancelButton: false,
              timer: 2000,
              showConfirmButton: false
            });
            this.deleting = false;
            this.$router.go('/administratie/ritten');
          });
      });
    }
  }
}
like image 20
Primoz Rome Avatar answered Oct 06 '22 04:10

Primoz Rome