How to submit a Flask-WTF form with Ajax

2019-04-15 18:19发布

问题:

I want to be able to take this form - get a stripe ID with stripe checkout (code implemented and working) and then submit the form via ajax and insert the stripe id into the hidden value in the form.

What jQuery code would allow me to do this?

class signupForm(Form):
forename = StringField('Forename', validators = [ Required()])
surname = StringField('Surname', validators = [Required()])
username = StringField('Username', validators = [Required(), Length(min = 4, max = 20)])
password1 = PasswordField('Password', validators = [Required(), Length(min = 6, max=50)])
password2 = PasswordField('Password', validators = [Required(), Length(min = 6, max=50)])
email = StringField('Email', validators = [Email(), Required()])
phone = StringField('Phone number', validators = [Required()])
addressLine1 = StringField('Address Line 1', validators = [Required()])
addressLine2 = StringField('Address Line 2')
town = StringField('Town', validators = [Required()])
county = StringField('County', validators = [Required()])
postcode = StringField('Postcode', validators = [Required()])
recurringDonationQuantity = StringField('If you would like to make a monthly recurring donation, please enter the amount in Sterling below. <br> Recurring Donation Amount')
stripetoken = HiddenField('')

My page:

            {% with messages = get_flashed_messages() %}
                {% if messages %}
                    <div class="alert alert-warning" role="alert">
                        <ul>
                                {% for message in messages %}
                                    <li>{{ message | safe }}</li>
                                {% endfor %}
                        </ul>
                    </div>
                {% endif %}
            {% endwith %}

            {{ wtf.quick_form(form) }}

I also have this javascrpt in-page

            <script>
            var handler = StripeCheckout.configure({
                key: '{{key}}',
                image: '{{ url_for("static", filename="logo.png") }}',
                locale: 'english',
                token: function(token,args) {
                    $('#stripe-token').val = token;
                    wt
                    console.log(token)
                }
            });


            $('#pay').on('click', function(e) {
                // Open Checkout with further options

                    if ('{{type}}' == 'normal') {
                        description = 'Normal Membership';
                        amount = 2500;

                        console.log('membership type is NORMAL')
                    } else {
                        description = 'Associate Membership';
                        amount = 2500;

                        console.log('membership type is ASSOCIATE')
                    }



                    handler.open({
                        name: 'My Organisation',
                        description: description,
                        amount: amount,
                        currency: 'GBP',
                        panelLabel: 'Okay',
                        billingAddress: true,
                        zipCode: true
                    });

                    e.preventDefault();
            });


            // Close Checkout on page navigation
            $(window).on('popstate', function() {
            handler.close();
            });
        </script>

回答1:

I might try something like this using jQuery's serialize method.

<script>
    $(document).ready(function(){    
        $('#submit').click(function() {
            console.log('submit clicked');

            //This part keeps you D.R.Y.
            url_params = $(this).serialize();

            //left this code the same...
            var csrftoken = $('meta[name=csrf-token]').attr('content')

            $.ajaxSetup({
                beforeSend: function(xhr, settings) {
                    if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                        xhr.setRequestHeader("X-CSRFToken", csrftoken)
                    }
                }
            })

            $.ajax({
                //more changes here
                url: url_params, //you may need to modify this to hit the
                                 //correct URL depending on your forms action param
                                 //ex: url: $(this).attr('action') + url_params,
                type: 'GET',     //GET instead of POST so we can use url_params
                contentType:'application/json;charset=UTF-8',
                success: function(response) {
                    console.log(response);
                },
                error: function(error) {
                    console.log(error);
                }
            });
        });
    });
</script>

Hopefully this points you in the right direction to avoid having to have so many fields hard-coded into your javascript.

You may also choose to keep the ajax call as a 'POST', to do that you would need to take a look at jQuery's serializeArray if you want to roll your own solution, or see the following code I adapted from this stackoverflow quesion:

// add a helper function to the $ jQuery object.
// this can be included in your page or hosted in a separate JS file.
$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] !== undefined) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return o;
};

// then inside your form page:
<script>
    $(document).ready(function(){    
        $('#submit').click(function() {
            console.log('submit clicked');

            //This part keeps you D.R.Y.
            data = $(this).serializeObject();

            //left this code the same...
            var csrftoken = $('meta[name=csrf-token]').attr('content')

            $.ajaxSetup({
                beforeSend: function(xhr, settings) {
                    if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                        xhr.setRequestHeader("X-CSRFToken", csrftoken)
                    }
                }
            })

            $.ajax({
                //more changes here
                data: data,
                url: '',
                contentType:'application/json;charset=UTF-8',
                success: function(response) {
                    console.log(response);
                },
                error: function(error) {
                    console.log(error);
                }
            });
        });
    });
</script>

The key to take away here is that you can use javascript or jQuery to read your <form> data and parameterize it. You want to avoid hard-coding your post data if at all possible. It's a hassle to do, and it's a bigger hassle to change it down the line.



回答2:

In the end I did this, I was hoping there was an easier way:

<script>
        $(document).ready(function(){    
            $('#submit').click(function() {
                console.log('submit clicked')

                data = {
                    'csrf_token': '{{ csrf_token() }}',
                    'csrftoken': '{{ csrf_token() }}',
                    'stripetoken': gtoken,
                    'forename': $('#forename').val(),
                    'surname': $('#surname').val(),
                    'username': $('#username').val(),
                    'password1': $('#password1').val(),
                    'password2': $('#password2').val(),
                    'email':  $('#email').val(),
                    'phone': $('#phone').val(),
                    'addressLine1': $('#addressLine1').val(),
                    'addressLine2': $('#addressLine2').val(),
                    'town': $('#town').val(),
                    'county': $('#county').val(),
                    'postcode': $('#postcode').val(),
                    'recurringDonationQuantity':  $('#recurringDonationQuantity').val(),
                }

                var csrftoken = $('meta[name=csrf-token]').attr('content')

                $.ajaxSetup({
                    beforeSend: function(xhr, settings) {
                        if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                            xhr.setRequestHeader("X-CSRFToken", csrftoken)
                        }
                    }
                })

                $.ajax({
                    url: '',
                    data: JSON.stringify(data, null, '\t'),
                    type: 'POST',
                    contentType:'application/json;charset=UTF-8',
                    success: function(response) {
                        console.log(response);
                    },
                    error: function(error) {
                        console.log(error);
                    }
                });
            });
        });
    </script>

There are so many csrf things because I haven't gotten around to working out which one made it work.