Flask - Update the page content without redirection in Ajax/JQuery

Multi tool use
Flask - Update the page content without redirection in Ajax/JQuery
I am developing a website that allows a user to follow tourist attractions. When a user clicks the follow button, the button will change to unfollow. The page is then refreshed due to redirection. But what I want is to make this page update but not reload. I got some relevant information from the Internet. The solution is to use Ajax or JQuery, but I still have no idea how to solve this problem.
View
@main.route('/place/<tourist_attraction_name>')
def place_detail(tourist_attraction_name):
place = Place.query.filter_by(tourist_attraction_name=tourist_attraction_name.first()
if place is None:
abort(404)
return render_template('main/detail/place_detail.html', place=place)
@main.route('/follow/<tourist_attraction_name>', methods=['GET', 'POST'])
@login_required
def follow(tourist_attraction_name):
place = Place.query.filter_by(tourist_attraction_name=tourist_attraction_name).first()
if place is None:
flash('Invalid place name.')
return redirect(url_for('.index'))
tourist_attraction_name=place.tourist_attraction_name)
current_user.follow(place)
flash('You are now following %s.' % tourist_attraction_name)
return redirect(url_for('.place_detail', tourist_attraction_name=place.tourist_attraction_name)
@main.route('/unfollow/<tourist_attraction_name>', methods=['GET', 'POST'])
@login_required
def follow(tourist_attraction_name):
place = Place.query.filter_by(tourist_attraction_name=tourist_attraction_name).first()
if place is None:
flash('Invalid place name.')
return redirect(url_for('.index'))
tourist_attraction_name=place.tourist_attraction_name)
current_user.unfollow(place)
flash('You are now following %s.' % tourist_attraction_name)
return redirect(url_for('.place_detail', tourist_attraction_name=place.tourist_attraction_name)
place_detail.html
<h3>{{ place.tourist_attraction_name }}</h3>
<p>{% if current_user.is_authenticated %}
{% if not current_user.is_following(place) %}
<a href="{{ url_for('.follow', tourist_attraction_name=tourist_attraction_name) }}"
class="btn btn-primary">Add</a>
{% else %}
<a href="{{ url_for('.unfollow',tourist_attraction_name=tourist_attraction_name) }}"
class="btn btn-default">Remove</a>
{% endif %}
<b><font color="navy"> Followers: </font></b> <span class="badge">{{ place.followers.count() }}</span>
{% endif %}</p>
Additional issue:
{%if condition%} only can be rendered once
<h3>{{ place.tourist_attraction_name }}</h3>
<p>{% if current_user.is_authenticated %}
{% if not current_user.is_following(place) %}
<button id="follow" onclick="ajax_follow('{{place.tourist_attraction_name}}')" class="btn btn-primary">
{% else %}
<button id="unfollow" onclick="ajax_unfollow('{{place.tourist_attraction_name}}')" class="btn btn-primary">
class="btn btn-default">Remove</a>
{% endif %}
<b><font color="navy"> Followers: </font></b> <span class="badge">{{ place.followers.count() }}</span>
{% endif %}</p>
JS
<button id="follow" onclick="ajax_follow('{{place.tourist_attraction_name}}')" class="btn btn-primary">
function ajax_follow(name) {
$.ajax({
url: "/follow/" + name, success: function (result) {
if (result == 'success') {
$("#follow").text("Remove");
}
}
});
}
function ajax_unfollow(name) {
$.ajax({
url: "/unfollow/" + name, success: function (result) {
if (result == 'success') {
$("#unfollow").text("Add");
}
}
});
}
1 Answer
1
You need to change your follow/unfollow routes so that instead of returning a flask redirect object, they return a response to an AJAX request. You'll also need to change how your buttons work so that you are making an AJAX request, rather than pointing to a link.
Let's start with the buttons. Right now they're just links, what you need is for them to initialize a piece of JavaScript which contains your AJAX request, which means changing this:
<a href="{{ url_for('.follow', tourist_attraction_name=tourist_attraction_name) }}"class="btn btn-primary">Add</a>
to something more like this:
<button id="follow" onclick="ajax_follow('{{tourist_attraction_name}}')">Add</button>
and adding the relevant script like this:
function ajax_follow(name) {
$.ajax({url: "/follow/" + name, success: function(result){
if (result == 'success') {
$("#follow").text("Remove");
$("#follow").attr("onclick", "ajax_unfollow('{{tourist_attraction_name}}')"
}
}});
}
This will execute your flask '/follow/' route with tourist_attraction_name as the attraction name and change the content of your button without reloading the page. Note the need to have jQuery imported first for ajax to work properly. You still have a problem with the flask route though, in that it isn't returning 'success', but a flask redirect object. To fix this, change:
return redirect(url_for('.place_detail', tourist_attraction_name=place.tourist_attraction_name)
to:
return "success"
To finish, you should add error handling and do something similar for unfollowing. Id recommend handling errors by returning a non-success descriptive error text if your flask route logic goes bad and then using javascript to alert that error text.
No problem! Good catch on the quotes though. I've updated my answer.
– Logan Bertram
Jul 2 at 16:10
I encountered another issue, it seems that jinja temple only can be rendered once without reloading, because {% if conditions%}{%else%} only worked once. I have added unfollow part, when I clicked follow, it turned to unfollow, but this unfollow button is not available to click again.... we only change the the text - $("#follow").text("Remove")
– HungryBird
Jul 2 at 19:30
Sorry, I added the needed line to the JS. You need to change the onclick attribute as well, and point it to a second JS function like unfollow_ajax which points to the unfollow flask route and reverses the text and onclick attribute changes.
– Logan Bertram
Jul 2 at 19:47
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Thank you so much, it is very helpful !!! ({{tourist_attraction_name}}) should be changed to ('{{tourist_attraction_name}}'), otherwise, error - missing ) after argument list will occur. Anyway, thank you ~
– HungryBird
Jul 2 at 15:48