简体   繁体   English

如何防止 ajax post request 提交两次?

[英]How to prevent ajax post request from submitting twice?

I have a button subscribe that should submit a post request via ajax to my controller for insertion to my table.我有一个订阅按钮,它应该通过 ajax 向我的 controller 提交一个帖子请求,以便插入我的表。

This is how my view look like:这就是我的看法:

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8">
            <div class="flash-message"></div>
            <div class="card">
                <div class="card-header">
                    <div class="level">
                        <span class="flex"><a href="{{route('profile',$thread->creator->name)}}">{{$thread->creator->name}}</a> posted:
                            {{$thread->title}}
                        </span>
                        @if(auth()->check())
                        @if($subscription)
                        <button class="btn btn-secondary" id="unsubscribe">Unsubscribe</button>
                        @else
                        <button class="btn btn-danger" id="subscribe">Subscribe</button>
                        @endif
                        @endif
                        @can('update',$thread)
                        <a href="{{$thread->path()}}/edit" class="btn btn-link">Edit Thread</a>
                        <form action="{{$thread->path()}}" method="POST">
                            @csrf
                            @method('delete')
                            <button class="btn btn-link" type="submit">Delete Thread</button>
                        </form>
                        @endcan
                    </div>
                </div>
                <div class="card-body">
                {{$thread->body}}
                </div>
            </div>

..............

My app.blade:我的 app.blade:

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>

    <!--jQuery/share.js -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=" crossorigin="anonymous"></script>
    <script src="{{ asset('js/share.js') }}"></script>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    <style>
        body{
            padding-bottom:100px;
        }
        .level{
            display: flex;
            align-items: center;
        }
        .flex{
            flex: 1;
        }
    </style>
</head>
<body>
      <div id="app">
        @include('layouts.nav')
        <main class="py-4">
            @yield('content')
        </main>
    <flash message="{{session('flash')}}"></flash>
    </div>
</body>

<style>
    .btn-width{
        min-width: 70px;
      }
</style>


</html>

The code calling the button:调用按钮的代码:

<script type="application/javascript">

    $(document).ready(function(){
        $('#subscribe').click(function(e){
            e.preventDefault();
            $.ajaxSetup({
                  headers: {
                      'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                  }
              });
              
            $.ajax({
                url: "{{route('subscription.store')}}",
                method:'POST',
                data: {
                    thread_id: "{{$thread->id}}",
                },
                success:function(response){
                    $('div.flash-message').html(response);
                   
                },
                error:function(error){
                    console.log(error);
                }
            });
        });
    });
From what I could tell, there is no other element that shares the same id as my button. 据我所知,没有其他元素与我的按钮具有相同的 id。 And, my button is not in a form submit so it should not be called twice. 而且,我的按钮不在表单提交中,所以它不应该被调用两次。 Inspecting dev tools shows no error and in the network tab, two requests are called identically with the same initiator. 检查开发工具显示没有错误,并且在网络选项卡中,两个请求被相同的发起程序调用。

So, I am kinda wondering why would this happen.所以,我有点想知道为什么会发生这种情况。 Shouldn't an ajax post request submit the request once only? ajax 发布请求不应该只提交一次请求吗?

I would really like to get to the bottom of this as most of the other similar issues have calling the submit twice while my code is only supposed to call it once.我真的很想深入了解这一点,因为大多数其他类似问题都调用了两次提交,而我的代码只应该调用一次。 Instead, it makes two insertion to my db.相反,它对我的数据库进行了两次插入。

What else can I do to figure out the root cause of the issue?我还能做些什么来找出问题的根本原因?

Is it possible that your javascript is being loaded twice somehow?您的 javascript 是否有可能以某种方式被加载两次? That would attach two identical listeners and send the request twice on a single click.这将附加两个相同的侦听器并单击两次发送请求。 If you put a console.log inside of the event handler, do you see that twice as well?如果您在事件处理程序中放置一个console.log,您是否也会看到两次?

Also, apparently, .click adds a separate event listener for each element that matches the selector passed to the jQuery object, whereas .on only adds a single one.此外,显然, .click为与传递给 jQuery object 的选择器匹配的每个元素添加了一个单独的事件侦听器,而.on仅添加一个。 . . What would happen if you did this instead?如果你这样做会发生什么?

$(document).ready(function () {
  $("#subscribe").on("click", function(e) {
    e.preventDefault();
    $.ajaxSetup({
      headers: {
        "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
      },
    });

    $.ajax({
      url: "{{route('subscription.store')}}",
      method: "POST",
      data: {
        thread_id: "{{$thread->id}}",
      },
      success: function (response) {
        $("div.flash-message").html(response);
      },
      error: function (error) {
        console.log(error);
      },
    });
  });
});

you are calling you function twice one in document ready and second on button click remover document你打电话给你 function 两次,一次在文档准备就绪,第二次在按钮单击卸妆文档

Have you tried giving return false?你试过给return false吗? like this:像这样:

$(document).ready(function(){
    let subscribeClick = function() {
     $.ajaxSetup({
              headers: {
                  'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
              }
          });
          
        $.ajax({
            url: "{{route('subscription.store')}}",
            method:'POST',
            data: {
                thread_id: "{{$thread->id}}",
            },
            success:function(response){
                $('div.flash-message').html(response);
               
            },
            error:function(error){
                console.log(error);
            }
        });
        return false;
    }
    $('#subscribe').click(function(e){
        e.preventDefault();
        e.stopImmediatePropagation();
        subscribeClick(); 
    });
});

You can try these options:您可以尝试以下选项:

(1) Use async: false in your ajax call to stop the execution of other code until you receive response of the current ajax call. (1) 在您的 ajax 调用中使用async: false来停止其他代码的执行,直到您收到当前 ajax 调用的响应。

 $('#subscribe').click(function(e) {
    e.preventDefault();
    $.ajaxSetup({
        headers: {
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }
    });

    $.ajax({
        url: "{{route('subscription.store')}}",
        method: 'POST',
        async: false,
        data: {
            thread_id: "{{$thread->id}}",
        },
        success: function(response) {
            $('div.flash-message').html(response);

        },
        error: function(error) {
            console.log(error);
        }
    });
});

OR或者

(2) You can use stopPropagation() method of the Event interface which prevents further propagation of the current event in the capturing and bubbling phases. (2) 您可以使用 Event 接口的stopPropagation()方法来防止在捕获和冒泡阶段进一步传播当前事件。

$('#subscribe').click(function(e) {
    e.preventDefault();
    e.stopPropagation();
    $.ajaxSetup({
        headers: {
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }
    });

    $.ajax({
        url: "{{route('subscription.store')}}",
        method: 'POST',
        async: false,
        data: {
            thread_id: "{{$thread->id}}",
        },
        success: function(response) {
            $('div.flash-message').html(response);

        },
        error: function(error) {
            console.log(error);
        }
    });
});

OR或者

(3) Use a variable that stores the status of the request. (3) 使用存储请求状态的变量。

var isLoading = false;
$(document).ready(function() {
    $('#subscribe').click(function(e) {
        if (!isLoading ) { 
            isLoading = true; //make true when request starts
            e.preventDefault();
            $.ajaxSetup({
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                }
            });

            $.ajax({
                url: "{{route('subscription.store')}}",
                method: 'POST',
                data: {
                    thread_id: "{{$thread->id}}",
                },
                success: function(response) {
                    $('div.flash-message').html(response);
                    isLoading = false; //make false when response is received
                },
                error: function(error) {
                    console.log(error);
                    isLoading = false; //make false when error is received
                }
            });
        }
    });
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM