Hi everyone. I'm developing my first flask project and I got stuck on the following problem:
I have a simple Flask app:
from flask import Flask, render_template
import map_plotting_test as mpt
app = Flask(__name__)
@app.route('/')
def render_the_map():
mpt.create_map()
return render_template("map.html")
if __name__ == '__main__':
app.run(debug=True)
Problem
mpt.create_map()
function here is just making the map, rendering it, then creating the map.html
file and saving it to the templates folder: templates/map.html
. It works pretty fine, but it takes some noticeable time to finish making the map (around 10-15 seconds).
The problem is that while this function is performed, I see just a blank screen in the browser, and only then does Flask render the finished map.html
file.
What I want
What I want to do is to show the loading screen instead of a blank screen while the create_map()
function is running. And when the function finishes its work and creates a map.html file - show rendered template to user just like return render_template("map.html")
does.
Is there a way to achieve this without much effort? I'm new to Flask, and I would be very grateful for a good explanation.
Thank you!!!
CodePudding user response:
Finally I found the solution!
Thanks to Laurel's answer. I'll just make it more nice and clear.
What I've done
I redesigned my Flask app, so it looks like this:
from flask import Flask, render_template
import map_plotting_module as mpm
app = Flask(__name__)
@app.route('/')
def loading():
return render_template("loading.html")
@app.route('/map')
def show_map():
return render_template("map.html")
@app.route('/create_map')
def create_map():
mpm.create_map()
return "Map created"
if __name__ == '__main__':
app.run()
When user opens the page, flask stars rendering the loading.html
file.
In this file you have to add the following code to the <head>
section:
<script>
function navigate() {
window.location.href = 'map'; // Redirects user to the /map route when 'create_map' is finished
}
fetch('create_map').then(navigate); // Performing 'create_map' and then calls navigate() function, declared above
</script>
Then, add a loading wheel div to your <body>
section:
<body>
<div ></div>
</body>
If it's still not clear for you - please check my example at the end of the answer
Explanation
In the <script>
section we have to declare navigate()
function. It just redirects the user to the desired reference, /map
in the current case.
fetch()
is the analog to jQuery.ajax()
- read more. It's just fetching the app route to /create_map
, awaits it to be done in the "backround", and then performs action in the .then()
block - navigate()
function in our case.
So the workflow is:
- User opens the page,
@app.route('/')
function is performed, which is loading page. - Loading page fetching
@app.route('/create_map')
and runs its functions in the background. - When
create_map()
function is completed - user is being redirected to the@app.route('/map')
which function is rendering all-donemap.html
template.
Few recommendations from me
- If you want your loading page to have an icon, just add the following tab to your
<head>
section ofloading.html
:
<link rel="icon" href="/static/<icon_file>">
Pay attention, Flask is searching for media in the
/static
folder. Put your media in it. Otherwise, media will not be rendered!
- If you want your loading page to have a nice CSS loader, consider visiting this page: loading.io. I really enjoyed it.
Finally, here's code snippet as an example of loader:
.loader {
position: absolute;
top: 50%;
left: 50%;
margin: -56px 0 0 -56px;
}
.loader:after {
content: " ";
display: block;
width: 110px;
height: 110px;
border-radius: 50%;
border: 1px solid;
border-color: #0aa13a transparent #47a90e transparent;
animation: ring 1.2s linear infinite;
}
@keyframes ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Loading...</title>
</head>
<body>
<div ></div>
</body>
</html>