Reputation: 665
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?
Upvotes: 1
Views: 1064
Reputation: 71461
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
:
students_and_classes.html
:
<div class='student_class'>
{%for student in lecture%}
<span class='student'>Name: {{student.name}}</span>
{%endfor%}
{%if lecture.has_previous%}
<button id='previous_{{lecture.previous}}'>Previous</button>
{%endif%}
{%if lecture.has_next%}
<button id='next_{{lecture.next}}'>Next</button>
{%endif%}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(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('<p>Opps, something when wrong</p>');
}
});
});
});
</script>
</div>
Second, the page to display the full student pagination, along with the jquery
and ajax
:
main.html
:
<html>
<body>
<div id='pagination_results'>
<div class='student_class'>
{%for student in lecture%}
<span class='student'>Name: {{student.name}}</span>
{%endfor%}
{%if lecture.has_previous%}
<button id='previous_{{lecture.previous}}'>Previous</button>
{%endif%}
{%if lecture.has_next%}
<button id='next_{{lecture.next}}'>Next</button>
{%endif%}
</div>
</div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(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('<p>Opps, something when wrong</p>');
}
});
});
});
</script>
</html>
Lastly, in the desired route (in this case '/'), the route to serve main.html
can be created:
@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:
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
in Pagination
can be replaced with a database query, etc., should that be the case in your projectStudentList
is used for cleaner rendering of the template should all list values be the basic datatypes. In your example, it is not necessary, as you mention your lists already store custom class objects, and yield i
can be substituted for yield StudentList(i)
.Upvotes: 1
Reputation: 1507
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.
Upvotes: 0