Search code examples
ruby-on-railsajaxbraintreebraintree-rails

Rails ajax form: how to fix up fields before the form gets sent


In Rails 4.1.8

I'm working on a payment form (Braintree payments), I want it to work via Ajax. I can't use the standard Braintree form setup because that doesn't offer an onSuccess and onError method. But I can use a special javascript function that looks at the fields, talks to the Braintree server and returns a nonce, which then gets sent in the form. So...

I created a form and set remote: true. This allows rails_ujs to take over and ajax the form to my server. But before that happens I have to send a call to Braintree and get a response. The javascript function they provide does an async call. Therefore I can't hook into the Rails 'ajax:before' callback because the Braintree javascript call returns a value after the 'ajax:before' has passed on to the main rails_ujs which sends the form contents to the server and therefore I get a blank field at the server end.

The js function Braintree provide does have the normal callbacks, so I can hook into the success callback and trigger a form submit. So I tried hooking into the click event on the submit button, disabling the default, calling out to Braintree, then when I get a successful response I call

$('#myForm').submit()

Which does actually send the data across, but it's a normal non-xhr POST. Digging deep I found that the rails_ujs doesn't behave as if it's hooking onto the form.submit() event, but to the submit button click() event. So that by capturing that event, preventing the default behaviour, calling out to Braintree and then triggering a submit() event on the form, I'd completely cut out the Rails ajax stuff.

The crucial question is how do I trigger a Rails ajax form to do a remote send, without clicking on the submit button inside the form?

As a workaround I can either do a handcoded $.ajax call, or put a hidden submit button on the form and another visible button that does the Braintree call and then triggers a click on the hidden submit button. Which will at least get all the Rails form submit with csrf token stuff done for me. But I'd like to know why I can't call form.submit()


Solution

  • Remove remote: true for your form and swap to using native javascript to submit your form.

    braintree.setup(
      braintree_token,
      'dropin', {
      container: 'dropin',
      paymentMethodNonceReceived: function (event, nonce) {
        $('.braintree-checkout').append("<input type='hidden' name='payment_method_nonce' value='"+ nonce +"'></input>");
        $.ajax({
          url: braintree_charge_path,
          type: 'post',
          data: $('.braintree-checkout').serialize(),
          context: $('.braintree-checkout'),
          beforeSend: function(evt, xhr, settings){
            // Stuff to do before braintree submits, e.g. loading screen
          },
          complete: function(evt, xhr, status){
            // Stuff that has to be done regardless of success or error 
          },
          error: function(evt, xhr, status, error){
            // Your error messages
          }
        });
      }
    });

    This way you get full control over your form process flow