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)





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





@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.

Comments

Popular posts from this blog

paramiko-expect timeout is happening after executing the command

Opening a url is failing in Swift

Export result set on Dbeaver to CSV