60

I’m still relatively new to Flask, and a bit of a web noob in general, but I’ve had some good results so far. Right now I’ve got a form in which users enter a query, which is given to a function that can take anywhere between 5 and 30 seconds to return a result (looking up data with the Freebase API).

The problem is that I can’t let the user know that their query is loading during this time, as the results page only loads once the function finishes its work. Is there a way I can display a loading message while that's going on? I found some Javascript that could display a loading message while page elements are still loading, but my waiting period happens before ‘render_template’.

I knocked together some example code, just to demonstrate my situation:

Python:

from flask import Flask
from flask import request
from flask import render_template
import time

app = Flask(__name__)

def long_load(typeback):
    time.sleep(5) #just simulating the waiting period
    return "You typed: %s" % typeback

@app.route('/')
def home():
    return render_template("index.html")

@app.route('/', methods=['POST'])
def form(display=None):
    query = request.form['anything']
    outcome = long_load(query)
    return render_template("done.html", display=outcome)

if __name__ == '__main__':
    #app.debug = True
    app.run()

Excerpt from index.html:

<body>
    <h3>Type anything:</h3>
    <p>
    <form action="." method="POST">
        <input type="text" name="anything" placeholder="Type anything here">
        <input type="submit" name="anything_submit" value="Submit">
    </form>
    </p>    
</body>

Excerpt from done.html:

<body>
    <h3>Results:</h3>
    <p>
        {{ display }}
    </p>
</body>

Any help would be greatly appreciated, I hope this example helps.

jrmedd
  • 625
  • 1
  • 6
  • 4
  • 2
    This is usually done with ajax in javascript. – Jakob Bowyer Jan 25 '13 at 15:43
  • 4
    essentially you take the data and return a page to the user straight away, while you are processing the data. Include a unique token in the reply. Then write a javascript program that runs in the browser in that page that uses the unique token to query every N seconds for an update. When it returns true (i.e. your processing is complete) grab that data and display it. – Paul Collingwood Jan 25 '13 at 15:52

5 Answers5

55

Add this to your index.html or js file (I'm assuming you have jQuery here, you could use standard javascript of course.):

<script type="text/javascript">// <![CDATA[
        function loading(){
            $("#loading").show();
            $("#content").hide();       
        }
// ]]></script>

Add this to you html or css file:

div#loading {
    width: 35px;
    height: 35px;
    display: none;
    background: url(/static/loadingimage.gif) no-repeat;
    cursor: wait;
    }

You can get an adequate GIF from http://www.ajaxload.info/. Download and put it into your static folder.

Then change your submission button to call above js function:

<input type="submit" name="anything_submit" value="Submit" onclick="loading();">

and add in a loading and a content div to you base html file:

<body>
    <div id="loading"></div>
    <div id="content">
        <h3>Type anything:</h3>
        <p>
        <form action="." method="POST">
            <input type="text" name="anything" placeholder="Type anything here">
            <input type="submit" name="anything_submit" value="Submit" onclick="loading();">
        </form>
        </p>
    </div>    
</body>

Now when you click 'Submit', the js function should hide your content and display a loading GIF. This will display until your data is processed and flask loads the new page.

jka.ne
  • 1,584
  • 1
  • 15
  • 19
  • 1
    Throwing in an indentation after div id="content" would help people see what is being hidden, methinks. – tandy Mar 30 '15 at 23:32
  • is anyone aware of a way to make this work with safari? It seems to me that the `onclick` function will not work. – archienorman Nov 01 '16 at 16:24
  • How about adding an `id` to your submit input element and using jQuery to bind a click function: https://api.jquery.com/click/ – jka.ne Nov 01 '16 at 17:10
  • 1
    @jka.ne Hello. Thanks for the code! It sheds some lights on my problem. I have the similar question and ended up on your answer. Once again, thanks. Just one quick question, how to adjust the `loadingimage.gif` to be on the center of the screen? Currently if I run my code and click the button then `loadingimage.gif` will be displayed in the top left-hand side of my screen. Thanks a lot! – arnold Jun 06 '17 at 08:04
  • 1
    @arnold - you can do this many ways. Why don't you try changing the loading div element's CSS: https://stackoverflow.com/a/10795144/3333687. You can make the position absolute, left 50%. – jka.ne Jun 06 '17 at 08:54
  • @jka.ne Thanks for the clues! Made it! – arnold Jun 06 '17 at 09:17
  • Is it also a solution to remove the `background: url(/static/loadingimage.gif) no-repeat;` from the CSS, and put `` in the 'loading' div? Is this essentially the same? Or is it different performance-wise? – Nils Mackay Dec 15 '17 at 03:25
  • How does it unhide then @jka.ne? – Shravya Mutyapu Jul 12 '21 at 08:26
  • i needed to add style="display: none" in order to work – HWid Dec 29 '21 at 12:22
  • http://ajaxload.info is not available. What will be the next best source? – Al Martins Jan 13 '22 at 21:37
  • Found it on http://ajaxloadingimages.net/ – Al Martins Jan 13 '22 at 21:45
  • 1
    Don't forget to add `` – Aditya Rajgor May 07 '22 at 06:13
  • Instead of an `onclick` event on the submit button, use an `onsubmit` event on the form. Something like: `
    `
    – TheTridentGuy supports Ukraine May 13 '23 at 16:01
19

This can be done by using a div that contains a 'loading gif' image. When the submit button is clicked, the div is displayed using javascript. To implement this, you can take a look at this website: http://web.archive.org/web/20181023063601/http://www.netavatar.co.in/2011/05/31/how-to-show-a-loading-gif-image-while-a-page-loads-using-javascript-and-css/

Arno Moonens
  • 1,163
  • 1
  • 10
  • 19
  • Perfect, created a div containing the image then revealed it using jQuery. Thanks for your advice, still reading up on web programming but this expedites the process! – jrmedd Jan 25 '13 at 23:31
  • 1
    @jrmedd I have a very similar problem but am having trouble understanding where the solution goes in the flask application. Could you PM what a solution might look like in your sample code? – sedavidw Aug 08 '13 at 17:05
  • 2
    broken link up here in 2019 – rvictordelta Jul 12 '19 at 21:47
  • 2
    @rvictordelta Thank you for letting me know. I replaced the URL with an archived version. – Arno Moonens Jul 31 '19 at 08:35
  • This is great, but the problem is, it triggers even if the forms haven't been validated. How do I check for form validation before executing loader? I am using WTForms. – find_all Jul 09 '20 at 23:40
14

I found the purely CSS-dependent loader very useful. It does not depend on external resources:

https://www.w3schools.com/howto/howto_css_loader.asp

enter image description here

shosaco
  • 5,915
  • 1
  • 30
  • 48
2

This is a bit of an old topic, but I needed to deal with this problem today and came with a solution on my own. I'm running a machine learning model that recieves an image input from the user and does some magic.

Basically this is what I did.

On my index.html file, I called a loading function on my "Submit" button passing the filename, because I was going to use it later:

<form method="post" action="/loading/{{filename}}" 
enctype="multipart/form-data">
    <input type="submit" value="Submit!">
</form>

On Flask, I created a route just to render the loading screen before doing the time consuming task, also passing the filename ahead:

@app.route('/loading/<filename>', methods=['POST'])
def loading_model(filename):
    return render_template ("loading.html", filename=filename)

And then, on loading.html, I render my .gif animation and at the end I redirect the page to the time consuming task function:

<!doctype html>
<head>
    <link rel= "stylesheet" type= "text/css" href= "{{url_for('static',filename='styles/main.css') }}">
</head>

<div id="preloader">
    <div id="status">&nbsp</div>
    <h1 class="ml13">ANALYZING...</h1>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/2.0.2/anime.min.js> </script>
</div>

<title>Title</title>

<script src="{{url_for('static', filename='main.js')}}"></script>

<script> window.location.replace('/task/{{filename}}'); </script>

And then, final step, back to Flask, call the task function:

@app.route('/task/<filename>', methods=['POST', 'GET'])
def task(filename):
    # Do your stuff
return render_template ("results.html")

By doing this, the gif animation will keep playing whilst the function does its job, and then render the results or the next page you want.

You obviously have to edit the css file so that "preloader" and "status" behave like you wish, this is how I used it:

#preloader {
    background-color: white;
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
}

#status {
    background-image: url("lalala.gif");
    background-repeat: no-repeat;
    width: 800px;
    height: 600px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -400px;
    margin-left: -400px;
}

It worked out for me.

MrCrowley
  • 142
  • 5
0

Brilliant @jka.ne but confusing situation.

I only needed to introduce the loading gif while a button was clicked.

My solution was:

<script type="text/javascript">
function loading(){
  $("#loading").show();
  window.location.href="../target_html";     
}
</script>

Then:

<button type="button" class="xxx"  onclick="loading();">Run</button>

Finally:

<div id="loading"></div>
Al Martins
  • 431
  • 5
  • 13