I have a python flask app and I'm wondering how to have the html page to show the selected parameters from dropdown list, text box, multi-selection etc.
A toy example of my current-working setup looks like this:
app.py
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html",
selection1=["1","2"],
selection2=["1","2"],
)
@app.route("/get-values")
def get_values():
dummy = [
["123", "123", "123"],
["456", "456", "456"],
["789", "789", "789"],
]
result = {
'sessions': dummy,
'total_sessions': 3,
'total_pages': 1,
'current_page': 1
}
return jsonify(result)
if __name__ == '__main__':
app.run(debug=True)
The step is basically:
index.html
contains the page where user can update parameters to have the table showing different informationwhen you open the page, it shows the result from default selections
whenever you update any of the field, the result will be updated automatically (done by using
$('#selection1-field').on('change', getFirstPageValues);
the update is done by calling
/get-values
inapp.py
and sending the new paramters.
What I want to do now is to be able to save (bookmark in browser) the url with updated parameters. Currently it is only saving http://localhost:5000/
which is the home page as well as where the result is shown, but I want it to be able to save url with updated parameters so that next time when I open the bookmarked page, it has the parameters applied already, something like: http://localhost:5000/get-values?c1=v1&c2=v2&c3=v3
.
I think I will need to have something like getUrlParam (How to get the value from the GET parameters?) but I'm very new to js and I don't know how to do it. Also probably I need to make changes on the python flask end as well?
Another problem is that since the main url is the index (/
) but I'm calling a different endpoint (/get-values
) to get data for table, if I were to use something like http://localhost:5000/get-values?c1=v1&c2=v2&c3=v3
, then according to the current setup, I'm basically just saving the json output rather than the page with table display of the json output result. Ideally it should be http://localhost:5000/c1=v1&c2=v2&c3=v3
but I don't know how to make this work and I was not able to find any references.
The corresponding index.html
is below. You should be able to put it under /templates
and make the app work. It has function displayResults(result)
which is used to display the table.
<!doctype html>
<html>
<head>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'>
<link rel='stylesheet' href='//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.css' />
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.4/css/bootstrap-select.min.css'>
<style type='text/css'>
.CSSTableGenerator {
margin: 50px;
padding: 0px;
width: 95%;
border: 1px solid #000000;
-moz-border-radius-bottomleft: 0px;
-webkit-border-bottom-left-radius: 0px;
border-bottom-left-radius: 0px;
-moz-border-radius-bottomright: 0px;
-webkit-border-bottom-right-radius: 0px;
border-bottom-right-radius: 0px;
-moz-border-radius-topright: 0px;
-webkit-border-top-right-radius: 0px;
border-top-right-radius: 0px;
-moz-border-radius-topleft: 0px;
-webkit-border-top-left-radius: 0px;
border-top-left-radius: 0px;
}
.CSSTableGenerator table {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
}
.CSSTableGenerator tr:last-child td:last-child {
-moz-border-radius-bottomright: 0px;
-webkit-border-bottom-right-radius: 0px;
border-bottom-right-radius: 0px;
}
.CSSTableGenerator table tr td {
-moz-border-radius-topleft: 0px;
-webkit-border-top-left-radius: 0px;
border-top-left-radius: 0px;
}
.CSSTableGenerator table tr:first-child td:last-child {
-moz-border-radius-topright: 0px;
-webkit-border-top-right-radius: 0px;
border-top-right-radius: 0px;
}
.CSSTableGenerator tr:last-child td:first-child {
-moz-border-radius-bottomleft: 0px;
-webkit-border-bottom-left-radius: 0px;
border-bottom-left-radius: 0px;
}
.CSSTableGenerator tr:hover td {
}
/*.CSSTableGenerator tr:nth-child(odd) {
background-color: #aad4ff
}
.CSSTableGenerator tr:nth-child(even) {
background-color: #ffffff
}*/
.CSSTableGenerator td {
vertical-align: middle;
border: 1px solid #000000;
border-width: 0px 1px 0px 0px;
text-align: left;
padding: 7px;
font-size: 13px;
font-family: Arial;
font-weight: normal;
color: #000000;
}
.CSSTableGenerator tr:last-child td {
border-width: 0px 1px 0px 0px
}
.CSSTableGenerator tr:last-child td:last-child {
border-width: 0px 0px 0px 0px
}
.CSSTableGenerator tr:first-child th {
background: -o-linear-gradient(bottom, #005fbf 5%, #003f7f 100%);
background: -webkit-gradient( linear, left top, left bottom, color-stop(0.05, #005fbf), color-stop(1, #003f7f) );
background: -moz-linear-gradient( center top, #005fbf 5%, #003f7f 100% );
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#005fbf", endColorstr="#003f7f");
background: -o-linear-gradient(top,#005fbf,003f7f);
background-color: #005fbf;
border: 0px solid #000000;
text-align: center;
border-width: 0px 0px 1px 1px;
font-size: 14px;
font-family: Arial;
font-weight: bold;
color: #ffffff;
}
.CSSTableGenerator tr:first-child td:first-child {
border-width: 0px 1px 1px 0px
}
.CSSTableGenerator tr:first-child td:last-child {
border-width: 0px 0px 1px 1px
}
#radial-center {
/* fallback */
background-color: #2F2727;
background-position: center center;
background-repeat: no-repeat;
/* Safari 4-5, Chrome 1-9 */
/* Can't specify a percentage size? Laaaaaame. */
background: -webkit-gradient(radial, center center, 0, center center, 460, from(#1a82f7), to(#2F2727));
/* Safari 5.1+, Chrome 10+ */
background: -webkit-radial-gradient(circle, #1a82f7, #2F2727);
/* Firefox 3.6+ */
background: -moz-radial-gradient(circle, #1a82f7, #2F2727);
/* IE 10 */
background: -ms-radial-gradient(circle, #1a82f7, #2F2727);
/* Opera couldn't do radial gradients, then at some point they started supporting the -webkit- syntax, how it kinda does but it's kinda broken (doesn't do sizing) */
}
td {
vertical-align: top;
}
.content {
width: 650px;
}
.sidebar {
width: 300px;
}
.leftNavButton {
width: 190px;
line-height: 1;
}
/* Start by setting display:none to make this hidden.
Then we position it in relation to the viewport window
with position:fixed. Width, height, top and left speak
speak for themselves. Background we set to 80% white with
our animation centered, and no-repeating */
/* When the body has the loading class, we turn
the scrollbar off with overflow:hidden */
body.isloading {
overflow: hidden;
}
/* Anytime the body has the loading class, our
modal element will be visible */
body.isloading .mymodal {
display: block;
}
.pace .pace-progress {
background: red;
position: fixed;
z-index: 2000;
top: 0;
left: 0;
height: 5px;
-webkit-transition: width 1s;
-moz-transition: width 1s;
-o-transition: width 1s;
transition: width 1s;
}
.pace-inactive {
display: none;
}
.ignoreDetailsRow {
cursor: pointer
}
.mistagDetailsRow {
cursor: pointer
}
.fixDetailsRow {
cursor: pointer
}
.ignoreSummaryRow {
cursor: pointer
}
.fixSummaryRow {
cursor: pointer
}
.mistagSummaryRow {
cursor: pointer
}
pre {
outline: 1px solid #ccc;
padding: 5px;
margin: 5px;
background-color: #000;
}
.string {
color: white;
}
.number {
color: darkorange;
}
.boolean {
color: blue;
}
.null {
color: magenta;
}
.key {
color: gold;
}
/* shirokov additions */
* {
-webkit-border-radius: 3 !important;
-moz-border-radius: 3 !important;
border-radius: 3 !important;
}
.container {
width: 95%;
}
.bootstrap-select > .dropdown-toggle {
width: 100%;
padding-right: 25px;
}
</style>
<title>Test</title>
</head>
<body>
<div id='homepage-container' class='container body-container' style='width: 1500px;'>
<div id='title-div' class='row' style='margin-bottom: 30px;'>
<h1 class='title' style='text-align: center; margin-top: 30px;'>Test</h1>
</div><!-- title-div -->
<div id='form-div' class='row' style='margin-bottom: 30px;'>
<div class='form-holder'>
<form id='query-form'>
<div class='col-lg-1 col-md-1'></div>
<div class='col-lg-2 col-md-2'>
<label id='date-range'>Date</label>
<div id='reportrange' class='pull-right' style='background: #fff; cursor: pointer; padding: 5px 8px; border: 1px solid #ccc; width: 100%'>
<span id='date-field'></span> <b class='caret'></b>
</div>
</div>
<div class='col-lg-1 col-md-1'>
<label>Selection1</label>
<select class='form-control' id='selection1-field'>
{% for d in selection1 %}
<option value="{{ d[0] }}">{{ d[1] }}</option>
{% endfor %}
</select>
</div>
<div class='col-lg-2 col-md-2'>
<label>Selection2</label>
<select id='selection2-field' class='selectpicker form-control' name='selection2' title='All' multiple data-live-search='true' style='width: 100%;'>
{% for d in selection2 %}
<option value='{{ d }}'> {{ d }}</option>
{% endfor %}
</select>
</div>
<div class='col-lg-2 col-md-2'>
<label>Query Search</label>
<input type="text" class="form-control" id="query-search-field">
</div>
</form>
</div><!-- form-holder -->
</div><!-- form-div -->
<div id='result-div' class='row' style='margin-bottom: 30px;'>
<p class='result-num' id='fetching' style='margin-left: 50px;'></p>
<table border="0" cellpadding="0" width="100%" id='result-table'> </table>
<p class='result-num' style='margin-left: 50px;'></p>
</div><!-- result-div -->
</div><!-- homepage-container -->
<script src='https://code.jquery.com/jquery.js'></script>
<script src='//cdn.jsdelivr.net/momentjs/latest/moment.min.js'></script>
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>
<script src='//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.4/js/bootstrap-select.min.js'></script>
<script type='text/javascript'>
$(function() {
var start = moment().year(2019).month(10).day(1);
var end = moment().year(2019).month(10).day(5);
function cb(start, end) {
$('#reportrange span').html(start.format('MMMM DD, YYYY') + ' - ' + end.format('MMMM DD, YYYY'));
getFirstPageValues();
}
$('#reportrange').daterangepicker({
startDate: start,
endDate: end,
alwaysShowCalendars: true,
opens: 'right',
ranges: {
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
'This Month': [moment().startOf('month'), moment().endOf('month')],
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
}
}, cb);
cb(start, end);
});
function displayResults(result) {
$('.result-num').empty();
$('#result-table').empty();
if ((result == 'ERROR: memory exceeded') || (result == 'ERROR: no results found for given parameters') || (result == 'ERROR: invalid date range')) {
$('#result-table').append(result);
} else {
var sessions = result['sessions'];
var totalSessions = result['total_sessions'];
var totalPages = result['total_pages'];
var currentPage = result['current_page'];
console.log(sessions[0]);
// setup table
var table = $("<table id=\'result-table\' />").addClass('CSSTableGenerator');
var row1 = $("<tr/>");
row1.append($("<td/>").text("C1"));
row1.append($("<td/>").text("C2"));
row1.append($("<td/>").text("C3"));
table.append(row1);
for (var i=0, len=sessions.length; i<len; i++) {
for (var j=0, len2=sessions[i].length; j<len2; j++) {
var c1 = sessions[i][j][0];
var c2 = sessions[i][j][1];
var c3 = sessions[i][j][2];
row = $("<tr/>").css("border-top", "1px solid #d6d6d6");
row.append($("<td/>").text(c1));
row.append($("<td/>").text(c2));
row.append($("<td/>").text(c3));
table.append(row);
}
}
$('.result-num').append('Total sessions: ' + totalSessions.toString() + '<br/>');
$('.result-num').append('Showing page ' + currentPage.toString() + ' out of ' + totalPages.toString() + '<br/>');
if (currentPage == 1) {
$('.result-num').append('<a href="" id="nextpage-btn" class="clickable" data-page="' + currentPage + '" data-action="next">>> Next Page</a>');
} else if (currentPage == totalPages) {
$('.result-num').append('<a href="" id="prevpage-btn" class="clickable" data-page="' + currentPage + '" data-action="prev"><< Prev Page</a>');
} else {
$('.result-num').append('<a href="" id="prevpage-btn" class="clickable" data-page="' + currentPage + '" data-action="prev">Prev Page <<</a> <a href="" id="nextpage-btn" class="clickable" data-page="' + currentPage + '" data-action="next">>> Next Page</a>');
}
$('#result-table').append(table);
var nextPageBtn = document.querySelectorAll('#nextpage-btn');
for (var i = 0; i < nextPageBtn.length; i++) {
nextPageBtn[i].addEventListener('click', function(evt) {
goToNextPage(evt);
});
}
var prevPageBtn = document.querySelectorAll('#prevpage-btn');
for (var i = 0; i < prevPageBtn.length; i++) {
prevPageBtn[i].addEventListener('click', function(evt) {
goToPrevPage(evt);
});
}
}
}
function getFirstPageValues() {
$('.result-num').empty();
$('#result-table').empty();
$('#fetching').append('Fetching results...');
var formInputs = {
'date': $('#date-field').html(),
'selection1': $('#selection1-field').val(),
'selection2': JSON.stringify($('#selection2-field').val()),
'querypattern': $('#query-search-field').val(),
'page_num': 1
};
$.get('/get-values',
formInputs,
displayResults
);
}
$('#selection1-field').on('change', getFirstPageValues);
$('#selection2-field').on('change', getFirstPageValues);
$('#query-search-field').on('change', getFirstPageValues);
function goToNextPage(evt) {
evt.preventDefault();
var formInputs = {
'date': $('#date-field').html(),
'selection1': $('#selection1-field').val(),
'selection2': JSON.stringify($('#selection2-field').val()),
'querypattern': $('#query-search-field').val(),
'page_num': parseInt(document.getElementById('nextpage-btn').dataset.page) + 1
};
$.get('/get-values-ns',
formInputs,
displayResults
);
}
function goToPrevPage(evt) {
evt.preventDefault();
var formInputs = {
'date': $('#date-field').html(),
'selection1': $('#selection1-field').val(),
'selection2': JSON.stringify($('#selection2-field').val()),
'querypattern': $('#query-search-field').val(),
'page_num': parseInt(document.getElementById('prevpage-btn').dataset.page) - 1
};
$.get('/get-values',
formInputs,
displayResults
);
}
</script>
</body>
</html>