Prevent unnecessary clicks with Vue.js

In my vue.js application, there is a feature to remove items.

The following code snippet shows the div element:

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

This is the function used to handle the click event:

methods: {
    delete ()
    {
      swal({
        title: "Are you sure?",
        text: "This action cannot be undone!",
        cancelButtonText: 'Cancel',
        type: "error",
        showCancelButton: true,
        confirmButtonColor: "#DD6B55",
        confirmButtonText: "Yes, delete this ride.",
        closeOnConfirm: false
      }, () => {
        RideService.destroy(this.ride)
          .then(() => {
            swal({
              title: "Ride successfully deleted",
              type: "success",
              showCancelButton: false,
              timer: 2000,
              showConfirmButton: false
            });
            this.$router.go('/administration/rides');
          });
      });
    }
  }

To prevent sending multiple requests when the user clicks rapidly, the button should be disabled after the first click.

--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: "Are you sure?",
          text: "This action cannot be undone!",
          cancelButtonText: 'Cancel',
          type: "error",
          showCancelButton: true,
          confirmButtonColor: "#DD6B55",
          confirmButtonText: "Yes, delete this ride.",
          closeOnConfirm: false
        }, () => {
          RideService.destroy(this.ride)
            .then(() => {
              swal({
                title: "Ride successfully deleted",
                type: "success",
                showCancelButton: false,
                timer: 2000,
                showConfirmButton: false
              });
              this.deleting = false
              this.$router.go('/administration/rides');
            });
        });

        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">Ride on {{ ride.created_at }}</strong>
        </div>
      </div>

      <div class="ride-item bar-underline">
        <div class="ride-item-title">
          <p>Name</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>From Location</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>To Location</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>Description</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>Hours</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.location.hour }} hours</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>Date</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>Time</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>Billable Time</p>
        </div>

        <div class="ride-item-content">
          <p>{{ ride.billabletime }} hours</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: "Are you sure?",
          text: "This action cannot be undone!",
          cancelButtonText: 'Cancel',
          type: "error",
          showCancelButton: true,
          confirmButtonColor: "#DD6B55",
          confirmButtonText: "Yes, delete this ride.",
          closeOnConfirm: false
        }, () => {
          RideService.destroy(this.ride)
            .then(() => {
              swal({
                title: "Ride successfully deleted",
                type: "success",
                showCancelButton: false,
                timer: 2000,
                showConfirmButton: false
              });
              this.processing = false
              this.$router.go('/administration/rides');
            });
        });

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

Answer №1

Starting from Vue version 2.1.4, a straightforward solution has been introduced for this issue:

Replace:

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

With:

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

The use of @click.once ensures that the specified method is executed only once.

In a scenario like mine, this resolved an issue during login, where repeated clicks caused multiple path chunks to be appended to the URL, leading to:

localhost:8000/admin/oms/admin/oms/admin/oms
.

You can find more details in the official Vue documentation here: https://v2.vuejs.org/v2/guide/events.html#Event-Modifiers

Answer №2

One approach could be to keep track of the async request status in a data property (e.g. processing: false). When the user triggers the action, set it to true, then check this state within the delete() method to determine whether to proceed or not. Remember to reset the state in both success and failure scenarios.

Here's an example:

new Vue({
  el: '#app',
  
  data: {
    processing: false
  },
  
  methods: {
    delete(el) {
      // Exit function if async request is already running
      if (this.processing === true) {
        return;
      } 
      
      // Set async state to indicate ongoing request
      this.processing = true;
      
      var paragraphs = Array.from(this.$el.querySelectorAll('p'));
      
      // Simulate async request with a delay
      setTimeout(() => {
        if (paragraphs.length) {
          paragraphs.shift().remove();
        }
        
        // Reset state on success/failure
        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>

Answer №3

It's usually not recommended to use @click.once as it can interfere with form validation and may require resubmitting the form. Instead, a better approach is to utilize a flag like loading or processing in this scenario.

Answer №4

Give this a try

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

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

      swal({
        title: "Are you sure?",
        text: "This action cannot be undone!",
        cancelButtonText: 'Stop',
        type: "error",
        showCancelButton: true,
        confirmButtonColor: "#DD6B55",
        confirmButtonText: "Yes, delete this ride.",
        closeOnConfirm: false
      }, () => {
        RideService.destroy(this.ride)
          .then(() => {
            swal({
              title: "Ride successfully deleted",
              type: "success",
              showCancelButton: false,
              timer: 2000,
              showConfirmButton: false
            });
            this.deleting = false;
            this.$router.go('/administration/rides');
          });
      });
    }
  }
}

Answer №5

It is recommended to utilize the @click.once modifier that comes with vue version 2.1.4 or higher.

Answer №6

Effortless way (using vue directive)

<button v-click="ajaxPromiseFn">Prevent multiple clicks with ease</button>

// Easily prevent multiple clicks using the npm package vue-stop-multiple-click
const stopMultipleClick = require('vue-stop-multiple-click')
Vue.directive('click', stopMultipleClick)

Check out the online demonstration here

Visit the project's GitHub page

Get the npm package here

Answer №7

Give this a shot!

<div class="remove-ride" v-on:click="remove()">
    <p>Remove</p>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Refresh the browser tab's content

How can I reload the specific content of a browser tab? I am looking for a solution that does not rely solely on jQuery (although I prefer it if there are more options available). Here is what I need: I have a page with many links to other pages (the fo ...

As you scroll, this smooth marquee effect gently shimmers with each movement

I've been trying out this code to create a scrolling marquee text, but the issue is that after 7000 milliseconds it starts to jitter, which doesn't make the text look good. Any suggestions on how I can fix this problem? Let me know! <script ...

Creating a Python server for an Angularjs application and capturing user input

Can you offer me some assistance, whether it's just a hint or a useful tip that could improve my current approach? I have created a series of forms using HTML and AngularJS. Each form collects input data from users, which is then stored in a JSON str ...

Update the user information quickly

In my Express application, I have implemented several routes and a login function. Each user has a balance associated with their data, which is stored in an 'express-session'. However, when the user refreshes the page, I need the balance to be up ...

The Plupload internal version seems to be incorrect. The file is labeled as 2.3.9, however, the actual version within the file is 2.3

We recently identified a security vulnerability issue with plupload 2.3.6 being deemed as vulnerable. To address this, we downloaded version 2.3.9 from the official Plupload website here: Upon inspection, we found that the zip file is labeled as 2.3.9, bu ...

Refreshing the page using location.reload triggers a reload of various elements

I am currently working on a website that supports multiple languages and utilizes a cookie named nav_lang to determine the user's preferred language for navigation. Whenever a user selects a new language, the cookie is updated accordingly and the page ...

Tips on using Visual Studio Code to troubleshoot Angular 4 unit tests

I am working on an Angular 4 project with Material design in Visual Studio Code. The setup is done using angular/cli. Currently, I have been writing unit tests using Karma and Jasmine. However, when trying to debug the tests by setting breakpoints, it doe ...

Having trouble triggering drag events efficiently with d3-drag on SVG elements

drawAll refers to a two-dimensional array where each drawAll[i] element represents a drawing containing all the strokes. A stroke is essentially a string representing a series of 2D points that can be utilized to generate a <path>. I am currently at ...

Session management functions properly in Postman, however, encountering issues when attempting to use it on a web

Working on a NodeJS project using express-session to handle sessions. When sending a post request to http://localhost:5500/login, a session is created with an additional property userid. Upon making a get request to http://localhost:5500/ using Postman, th ...

Send multipart form data to a different server via pipe

I need assistance with handling a POST request on my Node Express server for uploading images through multipart form data. Currently, my Express app is set up to use body parser which does not support multipart bodies and suggests using alternative librari ...

When the remove button is pressed, I want the checkbox to become unchecked

I need help with unchecking a checkbox after confirming the removal of items. Can someone provide guidance on how to achieve this? Below is the code I am using: JavaScript: $(".rem-btn").click(function(){ var remConf = confirm("Are you sure you ...

I've come across this ajax url that seems promising, but I keep getting a GET 404 (Not Found)

I am attempting to validate using ajax and php. This is the code I have for my ajax: function PrintRecibopapel() { recibo = document.getElementById("txtCod").value; if(recibo == "") { alert("You must save the receipt before pr ...

Learn how to instruct ajax to fetch the designated information and retrieve corresponding data from the database based on the selected criteria

Looking for some help with my 2 select boxes. The first box allows users to choose a brand, while the second box should display products from that brand fetched from the database. Unfortunately, I'm not familiar with AJAX and the script provided by a ...

The combination of VueJS and Webpack Dev Server is having trouble hot reloading URL subpaths

My application operates within the subdirectory http://localhost:8080/admin_suffix The variable suffix is an ENV variable that I can modify and define in a .env file. After starting the webpack dev server, accessing http://localhost:8080/admin_suffix fun ...

Receive JSON data in URL as an array in PHP

What I aim to accomplish is: I have a JSON object structured like below var foo = { format:"json", type:"test", id:"26443" }; and my goal is to convert it into a URL in the following format 'http://example.com/a:3:{s:6:"format";s:4:" ...

Tips on managing a GET request sent using axios with special characters and accents

When constructing a web page using VUE.JS, a GET request is made with Axios. In the form fields, special characters and accents may be present depending on the user's input. For instance, if the surname entered in the form field is 'Ruíz' ...

I am in search of a regular expression to validate a Jordanian phone number, whether it includes the country code or not

Looking for a regex pattern that can validate phone numbers beginning with either 0096279, 0096278, 0096277, or 079, 078, 077. ...

Steps to duplicate a Vuex store and incorporate it into a fresh store

In my endeavor to create a method in Electron that allows for the duplication of Vuex commits from one Window to another using IPC, I aim to simplify developers' usage of the Vuex store across different browser windows without the need for manual IPC ...

What defines a suitable application of the ref feature in React?

How can the ref attribute be effectively used in React? I understand that it is considered a shortcut that may go against the principles of React DOM, but I want to know the specifics of how and why. I'm currently evaluating if my use case justifies t ...

Tips for successfully transferring an image through an XMLHttpRequest

I found this helpful resource at: I decided to test out the second block of code. When I made changes in the handleForm function, it looked like this: function handleForm(e) { e.preventDefault(); var data = new FormData(); f ...