Cycle through 2D list with Jinja
Cycle through 2D list with Jinja
For simplicity, I will make a hypothetical which is equivalent to my current situation. I am using Flask as a backend to render Jinja templates for the frontend. Suppose I have a list of classes, each class being a list of students (Python classes/nodes with attributes and all). I want to render a single "class of students" at a time, and have buttons to cycle to the next group of students. Here is an example of how that looks:
app.py
@app.route('/', methods=['GET', 'POST'])
def get_students():
groups = None
# some calculations and algorithms
groups = [['foo', 'bar'], ['baz', 'boo']]
return render_template('index.html', groups=groups, index=0)
index.html
{% if groups %}
{% set display_group = groups[index] %}
<ul id="students" class="">
{% for student in display_group %}
<li class="student">{{ student.name }}</li>
{% endfor %}
</ul>
{% endif %}
<button onclick="next_group()">Next Group</button>
<button onclick="prev_group()">Previous Group</button>
I would like these buttons to re-render this list as if the index incremented/decremented respectively. I do not want this to be done through URL parameters (e.g. page_url/number). How can I achieve this affect, and if possible, without a page refresh?
2 Answers
2
This is similar to pagination. First, you can create a simple pagination object to better assist with the proper page lookup for a new index, while also controlling the indices for the next page and previous page:
import typing
class StudentList(typing.NamedTuple):
name:str
class Pagination:
def __init__(self, _num = 1):
self.num = _num
self.data = [['foo', 'bar'], ['baz', 'boo'], ['first', 'last']]
@property
def has_next(self):
return self.num < len(self.data)
@property
def has_previous(self):
return self.num > 0
@property
def next(self):
return self.num + 1
@property
def previous(self):
return self.num - 1
def __iter__(self):
for i in self.data[self.num-1]:
yield StudentList(i)
Next, in order to create a dynamic lookup, two pieces of html
are needed: the main page, with javascript to control the button clicks and communicate with the backend, and the html
returned as part of the query to the backend by ajax
. First, create the query html
:
html
html
ajax
html
students_and_classes.html
:
students_and_classes.html
{%for student in lecture%}
Name: {{student.name}}
{%endfor%}
{%if lecture.has_previous%}
Previous
{%endif%}
{%if lecture.has_next%}
Next
{%endif%}
https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js
$(document).ready(function() {
$('button').click(function(event) {
var result = event.target.id;
$.ajax({
url: "/update_page",
type: "get",
data: {results: result},
success: function(response) {
$("#pagination_results").html(response.html);
},
error: function(xhr) {
$("#pagination_results").html('Opps, something when wrong
');
}
});
});
});
Second, the page to display the full student pagination, along with the jquery
and ajax
:
jquery
ajax
main.html
:
main.html
<html>
<body>
{%for student in lecture%}
Name: {{student.name}}
{%endfor%}
{%if lecture.has_previous%}
Previous
{%endif%}
{%if lecture.has_next%}
Next
{%endif%}
</div>
</body>
https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js
$(document).ready(function() {
$('button').click(function(event) {
var result = event.target.id;
$.ajax({
url: "/update_page",
type: "get",
data: {results: result},
success: function(response) {
$("#pagination_results").html(response.html);
},
error: function(xhr) {
$("#pagination_results").html('Opps, something when wrong
');
}
});
});
});
</html>
Lastly, in the desired route (in this case '/'), the route to serve main.html
can be created:
main.html
@app.route('/', methods = ['GET'])
def home():
return flask.render_template('main.html', lecture=Pagination())
Then, the route to receive the data from the ajax
GET
method needs to be created:
ajax
GET
import re
@app.route('/update_page')
def update_page():
_r = flask.request.args.get('results')
_l = Pagination(int(re.findall('d+$', _r)[0]))
return flask.jsonify({'html':flask.render_template('students_and_classes.html', lecture=_l)})
Notes:
self.data
Pagination
StudentList
yield i
yield StudentList(i)
@adapap My apologies, I forgot to include the javascript in between
in students_and_classes.html
, not just main.html
. It currently works for me when I test it. Please see my recent edit.– Ajax1234
Jun 29 at 23:37
students_and_classes.html
main.html
That is a job for javascript, not jinja templating system, when you send template back to the client its pre formatted according to the variables composing it. You should have an endpoint to be invoked via ajax calls in java script, not to return the render_template but to return the json of your groups, and manipulate DOM via JS, jinja won't be able to do that, though you may send javascript formmated via Jinja templating system.
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 for your answer. I got this working with my setup. I ended up serializing my objects so that I could retrieve the data through cookies (instead of database query). The problem I am having is a bit strange. The buttons will only work once in either direction, and then fail to operate again. The script event listener is not even called and I'm not sure why.
– adapap
Jun 29 at 23:10