I have a page where users can vote a post or comment with an empty form. I have written JavaScript to submit the form and update the button with the vote count without refreshing the entire page. Instead of refreshing the button, the JavaScript routes to a new page with the JSON data returned from Flask.
post.html
<form class="d-inline" id="vote_form" action="{{ url_for('main.post', id=post.id) }}" method="post">
<input id="csrf_token" name="csrf_token" type="hidden" value="{{ csrf_token }}">
<button class="px-1 py-0 btn btn-outline-dark btn-sm" id="vote_post_btn" type="submit" name="submit" value="vote_post">⇧ 2</button>
</form>
<form class="d-inline" action="{{ url_for('main.post', id=comment.id) }}" method="post">
<input id="csrf_token" name="csrf_token" type="hidden" value="{{ csrf_token }}">
<button class="px-1 py-0 btn btn-outline-dark btn-sm" id="vote_comment_btn" type="submit" name="submit" value="vote_comment">⇧ 0</button>
</form>
routes.py
@bp.route('/post/<int:id>', methods=['GET', 'POST'])
def post(id):
post = db.first_or_404(sa.select(Post).where(Post.id == id))
if request.method == 'POST':
if request.form['submit'] == 'vote_post':
vote = db.session.scalar(sa.select(PostVote).where((PostVote.user_id == current_user.id) & (PostVote.post_id == id)))
post = db.session.scalar(sa.select(Post).where(Post.id == id))
if vote is None:
vote = PostVote(user_id=current_user.id, post_id=id)
db.session.add(vote)
db.session.commit()
return jsonify({"votes": post.votes_count(), "voted": True})
else:
db.session.delete(vote)
db.session.commit()
return jsonify({"votes": post.votes_count(), "voted": False})
if request.form['submit'] == 'vote_comment':
vote = db.session.scalar(sa.select(CommentVote).where((CommentVote.user_id == current_user.id) & (CommentVote.comment_id == id)))
comment = db.session.scalar(sa.select(Comment).where(Comment.id == id))
if vote is None:
vote = CommentVote(user_id=current_user.id, comment_id=id)
db.session.add(vote)
db.session.commit()
return jsonify({"votes": comment.votes_count(), "voted": True})
else:
db.session.delete(vote)
db.session.commit()
return jsonify({"votes": comment.votes_count(), "voted": False})
javascript
async function sendVote() {
let form = document.getElementById("vote_form");
let formBtn = document.querySelectorAll("#vote_post_btn, #vote_comment_btn");
let url = form.getAttribute("action");
// Associate the FormData object with the form element
const formData = new FormData(form);
try {
const response = await fetch(url, {
method: "POST",
})
.then((response) => response.json())
.then((data) => {
formBtn.innerHTML = data["votes"];
if (data["votes"] === true) {
formBtn.className = "px-1 py-0 btn btn-secondary btn-sm";
}
else {
formBtn.className = "px-1 py-0 btn btn-outline-dark btn-sm";
}
});
console.log(await response.json());
} catch (e) {
console.error(e);
}
}
//Take over form submission
form.addEventListener("submit", (event) => {
event.preventDefault();
sendVote();
});
page rendered
{
"voted": true,
"votes": 1
}
The JavaScript doesn’t update the button class and vote count. It renders JSON on a new page instead.
I used this MDN Advanced forms article and MDN json method to write the javascript.