Balrizangor
Balrizangor

Reputation: 274

How to "submit" form to ajax script instead of back to Flask?

Goal: Emulate this example (longtask with bar) with one difference: I need to pass a list of values from the submitted form.

Here's my form.py:

class InstallForm(Form):
  servers = SelectField(u'Server Names', validators = [required()])
  prop_files = SelectField(u'Properties Files', validators = [required()])
  mod_files = SelectField(u'Mod Files', validators = [required()])
  build = SelectField(u'Build Type', validators = [required()])
  do_install = SelectField(u'Do Install?', validators = [required()])
  submit = SubmitField('Submit')

Here are the routes:

User initially comes to /installer

@servers.route('/installer', methods=['GET', 'POST'])
def installer():

    servers = UcxServer.query.order_by(UcxServer.name)
    file_options = Install(app.config['LOADER_LOCATION']).get_options()

    form = InstallForm()
    form.servers.choices = [(s.ip_address, s.name) for s in servers]
    form.prop_files.choices = [(f,f) for f in file_options['props']]
    form.mod_files.choices = [(f,f) for f in file_options['mods']]
    form.build.choices = [("'ci'",'ci'), ("'nightly'",'nightly')]
    form.do_install.choices = [('TRUE','TRUE'), ('FALSE','FALSE')]


    if form.validate_on_submit():
      install_list = [form.prop_files.data, form.mod_files.data,      
                      form.build.data, form.servers.data, form.do_install.data]

    return render_template('installer.html') <- what to do here?

return render_template('installer.html', form=form)

I'm using wtf.quick_form() to render this form.

Here's the template where I'm totally lost:

{{ wtf.quick_form(form) }}

<div id="progress"></div>

<script src="//cdnjs.cloudflare.com/ajax/libs/nanobar/0.2.1/nanobar.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
    function start_long_task() {
        // add task status elements
        div = $('<div class="progress"><div></div><div>0%</div><div>...</div><div>&nbsp;</div></div><hr>');
        $('#progress').append(div);

        // create a progress bar
        var nanobar = new Nanobar({
            bg: '#44f',
            target: div[0].childNodes[0]
        });

        // send ajax POST request to start background job
        $.ajax({
            type: 'POST',
            url: '/longtask',
            data: $('form').serialize()
            success: function(data, status, request) {
                status_url = request.getResponseHeader('Location');
                update_progress(status_url, nanobar, div[0]);
            },
            error: function() {
                alert('Unexpected errorS');
            }
        });
    }
    function update_progress(status_url, nanobar, status_div) {
        // send GET request to status URL
        $.getJSON(status_url, function(data) {
            // update UI
            percent = parseInt(data['current'] * 100 / data['total']);
            nanobar.go(percent);
            $(status_div.childNodes[1]).text(percent + '%');
            $(status_div.childNodes[2]).text(data['status']);
            if (data['state'] != 'PENDING' && data['state'] != 'PROGRESS') {
                if ('result' in data) {
                    // show result
                    $(status_div.childNodes[3]).text('Result: ' + data['result']);
                }
                else {
                    // something unexpected happened
                    $(status_div.childNodes[3]).text('Result: ' + data['state']);
                }
            }
            else {
                // rerun in 2 seconds
                setTimeout(function() {
                    update_progress(status_url, nanobar, status_div);
                }, 2000);
            }
        });
    }
    $(function() {
        $('#button').click(start_long_task);
    });

I also have a celery task in my routes, and a redirect route which have a Location header with the task.id so that I can get status updates.

EDIT1:

To be more specific:

When submit is clicked, the data that is selected in the form should go to:

$.ajax({
        type: 'POST',
        url: '/longtask',
        data: $('form').serialize()

instead of

if form.validate_on_submit():
    install_list = [form.prop_files.data, form.mod_files.data,    form.build.data, form.servers.data, form.do_install.data]
    flash(install_list)
    return render_template('installer.html', form=form)

Upvotes: 2

Views: 1850

Answers (2)

Balrizangor
Balrizangor

Reputation: 274

Solved the issue I was having. Here are some highlights for anyone else in the same boat.

  • abandoned wtf.quick_form New form looks like this:

     <form action="" id='ucx'>
     {{ form.hidden_tag() }}
     <dl>
     {{ render_field(form.servers) }}
     {{ render_field(form.prop_files) }}
     {{ render_field(form.mod_files) }}
     {{ render_field(form.build) }}
     {{ render_field(form.do_install) }}
     </dl>
     <p><input type="button" value="Install!" onclick="start_long_task()">
     </form>
    

Instead of using a submit, I'm using a button that links to the long task function. Inside the function I get the values of the form like this:

        myArray =  [];
        myArray.push(document.getElementById('prop_files').value);
        myArray.push(document.getElementById('mod_files').value);
        myArray.push(document.getElementById('build').value);
        myArray.push(document.getElementById('servers').value);
        myArray.push(document.getElementById('do_install').value);

I then pass this using AJAX back to the web server like this:

    .ajax({
            type: 'POST',
            url: '/longtask',
            data: JSON.stringify(myArray),
            contentType: "application/json; charset=utf-8",
            dataType: "json",

Hope this helps someone else. By the way, if there is something completely idiotic about the way I'm doing it, I'll be eager to learn.

Thanks!

Upvotes: 2

Celeo
Celeo

Reputation: 5682

Although you've added a listener to the user clicking the #button element, the form will still submit unless you add a handler for that specific action with jquery.submit():

$(function() {
    $('form').submit(function(event) {
        event.preventDefault();
        start_long_task();
    });
});

Upvotes: 1

Related Questions