jQuery回调调用了两次

时间:2012-02-04 15:15:54

标签: javascript coffeescript

我正在关注Rails演员以实现订阅结算。就在这里:

http://railscasts.com/episodes/288-billing-with-stripe

我的代码几乎完全相同,但我将一些额外的字段传递给Stripe。问题是,javascript中的错误消息总是在提交按钮被点击时显示错误消息...即使成功收费。我不确定Strip是否会返回触发错误的内容,或者是否存在JS问题。

jQuery ->
  Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'))
  subscription.setupForm()

subscription =
  setupForm: ->
    $('#new_membership').submit ->
      $('input[type=submit]').attr('disabled', true)
      if $('#card_number').length
        subscription.processCard()
        false
      else
        true

  processCard: ->
    card =
      number: $('#card_number').val()
      cvc: $('#card_code').val()
      expMonth: $('#card_month').val()
      expYear: $('#card_year').val()
    Stripe.createToken(card, subscription.handleStripeResponse)

  handleStripeResponse: (status, response) ->
    if status == 200
      $('#membership_stripe_card_token').val(response.id)
      $('#new_membership')[0].submit()
    else
      $('#stripe_error').text(response.error.message)
      $('input[type=submit]').attr('disabled', false)

这是用于错误处理的示例条带。他们首先翻转过程并测试错误:

function stripeResponseHandler(status, response) {
    if (response.error) {
        ...
        //show the errors on the form
        $(".payment-errors").html(response.error.message);
    } else {
        var form$ = $("#payment-form");
        // token contains id, last4, and card type
        var token = response['id'];
        // insert the token into the form so it gets submitted to the server
        form$.append("<input type='hidden' name='stripeToken' value='" + token + "'/>");
        // and submit
        form$.get(0).submit();
    }
}

更新:所以我可以告诉你发生了什么,但不知道为什么或如何修复它。

我添加了一个控制台日志语句,现在可以看到handleStripeResponse被调用两次,一次当用户点击提交时,它返回一个200,然后它再次出现(可能是因为表单然后必须发布到Rails应用程序进行实际处理?它返回一个0,它会启动错误消息。但是 - 因为Rails现在处理处理服务器端,所以费用会通过。

这是编译后的JS,如果有帮助:

(function() {
  var subscription;

  jQuery(function() {
    Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'));
    return subscription.setupForm();
  });

  subscription = {
    setupForm: function() {
      return $('#new_membership').submit(function() {
        $('input[type=submit]').attr('disabled', true);
        if ($('#card_number').length) {
          subscription.processCard();
          return false;
        } else {
          return true;
        }
      });
    },
    processCard: function() {
      var card;
      card = {
        number: $('#card_number').val(),
        cvc: $('#card_code').val(),
        expMonth: $('#card_month').val(),
        expYear: $('#card_year').val()
      };
      return Stripe.createToken(card, subscription.handleStripeResponse);
    },
    handleStripeResponse: function(status, response) {
      if (status === 200) {
        $('#membership_stripe_card_token').val(response.id);
        return $('#new_membership')[0].submit();
      } else {
        $('#stripe_error').text(response.error.message);
        return $('input[type=submit]').attr('disabled', false);
      }
    }
  };

}).call(this);

6 个答案:

答案 0 :(得分:5)

我遇到了同样的问题。我不知不觉地两次调用javascript。

一次(明确地)在页面上:

= javascript_include_tag 'stripe'

然后(无意中)在application.js

//= require_tree .

结果是jQuery文档就绪函数被调用两次,导致所有侦听器被设置两次。

根据您的应用程序设置方式,您应该删除一个或另一个包含。对于我的应用程序,最好从我的application.js中删除该行。

棘手的资产管道!

答案 1 :(得分:1)

有同样的问题。简单地拿了jQuery - &gt;在存储条带代码的coffeescript文件的顶部之外,并且双提交已停止。得到了Nathan Colgate上面的提示。

答案 2 :(得分:0)

所以,到目前为止我的hacky解决方案是这样做的,因为第二次通过回调它一直返回0。

 else if status == 0
      $('#stripe_error').text('Processing...')

至少这样,用户不会收到错误消息,并且不会重新激活提交按钮。

答案 3 :(得分:0)

我遇到了同样的问题。仍然不确定是什么导致它。我找到的解决方案是为coffeescript添加不同的验证:

subscription =
  setupForm: ->
    $('#new_subscription').submit ->
      $('input[type=submit]').attr('disabled', true)
      if $('#subscription_stripe_card_token').val
        true
      else
        subscription.processCard()
        false

请注意,对于'if'语句,而不是检查:

if $('#card_number').length

而是我检查了一下:

if $('#subscription_stripe_card_token').val

这样,当表单第一次提交时,它会成功返回'stripe_card_token'的值,因此它不会再次提交表单。我还不知道为什么使用railscast中的card_number.length方法无效。我认为@subscription对象可能有问题,因为它没有识别它现在有一个stripe_card_token值,因此没有删除card_number字段......但我不知道

<div class="field">  
  <%= f.label :email %>  
  <%= f.text_field :email %>  
</div>    
<% if @subscription.stripe_card_token %>  
  Credit card has been provided  
<% else %>  
  <div class="field">  
    <%= label_tag :card_number, "Credit Card Number " %>  
    <%= text_field_tag :card_number, nil, name: nil %>  

希望这有助于在这里找到最终答案!

答案 4 :(得分:0)

对于阅读此内容的人:如果您遇到类似问题,我建议您查看&#34;查看页面来源&#34;或者相当于看看是否包含了两次javascripts。

我正在关注相同的Railscast并遇到同样的问题。我花了很长时间才愿意承认这一点,但我的问题是在困倦的时候复制粘贴了一行来自轨道广播的笔记。原因是应用程序javascripts被包含两次,一次是自动生成的rails包含在application.html.erb中:

<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>

并第二次复制粘贴来自railscast的线条:

<%= javascript_include_tag "https://js.stripe.com/v1/", "application" %>

因此包括&#34;申请&#34;两次。我只是通过移除其中一个来解决它。

答案 5 :(得分:0)

所以今天在工作中我们遇到了类似的问题:

  • 使用Rails 4.2
  • 将Ajax表单(remote = true)与Rails一起提供的内置jquery-ujs.js脚本一起使用
  • 使用Stripe作为支付网关(它们提供了可通过iframe处理某些信用卡信息的脚本)
  • 提交表单(使用自定义ajax逻辑)时,它会提交两次表单,一次用于Ruby的全局事件表单提交处理程序,一次用于“我们的自定义”事件处理程序

Stripe希望通过与Stripe自己的域进行Ajax处理来首先提交表单,然后返回结果时,它希望使用其响应数据填充表单中的某些隐藏字段。发生这种情况后,Stripe希望我们像往常一样提交表单。

对于我们来说,存在一个冲突,即Rails和Stripe都对表单提交做出了反应,并且没有进行协调。由于我们无法轻松更改jquery-ujs.js,因此我们研究了它们如何编码其全局表单“提交”事件处理程序。

事实证明,一个Rails对象作为jQuery插件公开,其中有一个防止jquery-ujs.js处理程序处理任何事件的功能-整洁!

所以对我们来说,解决方法是致电...

 $.rails.stopEverything(event);

...在Stripe提交事件处理程序内(这是我们项目的特殊情况)

以下是Stripe事件处理程序的代码,用于其他上下文:

// Create a token or display an error when the form is submitted.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function (event)
{
    $.rails.stopEverything(event); // Dont let Rails' global form submit handler handle this event (in jquery-ujs.js)
    event.preventDefault();

    stripe.createToken(card).then(function(result)
    {
        if (result.error)
        {
            // Inform the user if there was an error
            var errorElement = document.getElementById('card-errors');
            errorElement.textContent = result.error.message;
            $('#stripe-btn').prop('disabled', false)
            $('.fa-spin').remove()
        }
        else
        {
            // Send the token to your server
            stripeTokenHandler(result.token);
        }
    });
});