I have a table of users. and I need to be able to filter them by 'Group'. So when user selects option from drop-down list, I want not the whole page but only users table to reload and display users from that group
This is twig for users table
Filter by group
<select class="filter-users">
<option selected></option>
{% for group in groups %}
<option>{{ group.name }}</option>
{% endfor %}
</select>
<table id="dataTable">
<tbody>
{% for user in users %}
<tr>
<td>{{user.id}}</td>
<td>{{user.fullName}}</td>
</tr>
{% endfor %}
</tbody>
</table>
This action to get users by group name
/**
* #Route("/filter", name="users_filter")
*/
public function filterAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$group = $request->request->get('group');
$users = $em->getRepository('AppBundle:User')->findBy(['group' => group]);
return $this->render('AppBundle:User:list.html.twig', [
'users' => $users
]);
}
If you want only the table to be reloaded you could do the following.
First create a template only with the table.
table.html.twig
<table id="dataTable">
<tbody>
{% for user in users %}
<tr>
<td>{{user.id}}</td>
<td>{{user.fullName}}</td>
</tr>
{% endfor %}
</tbody>
</table>
In controller
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$group = $request->request->get('group');
$users = $em->getRepository('AppBundle:User')->findBy(['group' => group]
return new Response($this->renderView('table.html.twig', [
'users' => $users,
]));
}
Then create an ajax call
$('#select-button-div').on('change','#select-button',function() {
reloadData();
});
function reloadData() {
let data = {};
let selected = $('#select-button').find(':selected').text();
let usersTable = $('#entries');
usersTable.empty();
data['selectedVal'] = selected.val();
let url = Routing.generate('ToYourRoute');
table.load(url, data);
}
Related
I have create a test website using Flask that displays data from a postgres database, ordered by category, product, month and date. All the table rows below each category are hidden. Clicking on a row will expand/collapse 1 level of rows beneath it. So, clicking on a category will show all the products. Clicking on a product will show all the months with data.
Currently, as you can see in the code, all the records are retrieved when the page is loaded.
The test website is a simplification of a website that displays thousands of of records. I would like to use the website for a different db with millions of records.
Unfortunately, the page would take far to long to load and expand/collapse. Instead of collapsing/expanding the rows when clicked, I would like to query the db using Flask each time a category, product or month is expanded and add 1 level of rows to the table with the data returned by a query.
Can this be done with Flask, jinja2 and js?
html template with jinja2 code
<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type=text/javascript src="{{url_for('static', filename='js/test.js') }}"></script>
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles/test.css') }}">
<title>Test Table</title>
<body>
<table class = "products">
<thead>
<tr>
<th>Product</th>
<th>Month</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{% for category in types %}
<tr class="category" data-level="0" vis_id = "{{ category.category}}">
<td colspan = 3 class="nosides">
<label>{{ category.category}}</label>
</td
</tr>
{% for product in category.products %}
<tr class="parent" data-level="1" vis_id = "{{ product.product }}" par_vis_id = "{{ category.category}}">
<td colspan = 3 class="nosides">
<label>{{ product.product }}</label>
</td>
</tr>
{% for month in product.months %}
<tr class="parent" data-level="2" vis_id = "{{ product.product }}_{{ month.month }}" par_vis_id = "{{ product.product }}">
<td colspan=1 class="noright"></td>
<td colspan=2 class="nosides">
<label>{{ month.month }}</label>
</td>
</tr>
{% for date in month.date %}
<tr class="child" data-level="3" vis_id = "{{ date.date }}" par_vis_id = "{{ product.product }}_{{ month.month }}">
<td colspan = 2></td>
<td>{{ date.date }}</td>
</tr>
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
</tbody>
</table>
</body>
</html>
Python Flask code
import sys, os
from flask import Flask, render_template
import pandas as pd
import pandas.io.sql as psql
#app.route('/test_table')
def test_table():
con = set_con('db')
sys_list = []
#select all the records
df = pd.read_sql("select category, product, TO_CHAR(date, 'Mon YYYY') as month, TO_CHAR(date, 'YYYY-MM-DD HH24:MI:SS') as date \
from db order by category, product, date desc", con)
query = "Select distinct product from db order by product"
#get a list of categories, products and months, then loop over each.
df_products = pd.read_sql(query, con)
query = "Select distinct category from products"
df_sys = pd.read_sql(query,con)
for category in df_sys['category'].values:
products_list = []
df_sys_tmp = df[df['category']==category]
if df_sys_tmp.empty:
continue
for product in df_products['product'].values:
df_product_tmp = df_sys_tmp[df_sys_tmp['product']==product]
if df_product_tmp.empty:
continue
months = df_product_tmp['month'].unique()
tmp_list_months = []
for month in months:
#loop over all the records for this particular category, product, and month.
df_recs = df_product_tmp[df_product_tmp['month']==month]
if df_recs.empty:
continue
tmp_list_recs = []
for i, row in df_recs.iterrows():
tmp_list_recs.append({'date':row['date']})
tmp_list_months.append({'month':month, 'date':tmp_list_recs})
products_list.append({'product':product, 'months':tmp_list_months})
sys_list.append({'category':category, 'products':products_list})
return render_template(test.html',types=sys_list)
if __name__ == '__main__':
main()
js
function toggleIf(element, condition) {
if (condition ===true || condition === 'true') { element.show(); }
else { element.hide(); }
};
$(document).ready(function() {
function getChildren($row) {
var children = [], level = $row.attr('data-level');
while($row.next().attr('data-level') > level) {
children.push($row.next());
$row = $row.next();
};
return children.sort((a, b) => parseFloat(a.attr('data-level')) - parseFloat(b.attr('data-level')));;
};
$('.parent').on('click', function() {
var children = getChildren($(this));
var datalevel = $(this).attr('data-level')
var vis_id = $(this).attr('vis_id')
$.each(children, function() {
par_vis_id = $(this).attr('par_vis_id')
if (datalevel==1){
if ($(this).attr('data-level')==2){
$(this).toggle();}
else{
if ($(this).is(":visible")){
$(this).toggle();}}}
else{$(this).toggle();}
sessvisible = $(this).is(":visible")
if (sessvisible) {
sessionStorage.setItem(par_vis_id, 'true')
}
else{
try {
sessionStorage.removeItem(par_vis_id);}
catch { };};});
});
$('.product').on('click', function() {
var children = getChildren($(this));
var vis_id = $(this).attr('vis_id')
$.each(children, function() {
item_visible = $(this).is(":visible")
if ($(this).attr('data-level')==1){
$(this).toggle();
product_visible = $(this).is(":visible")
if (!product_visible) {
try {
sessionStorage.removeItem($(this).attr('vis_id'));}
catch { };}} else{
if (item_visible){
$(this).toggle();}
try {
sessionStorage.removeItem($(this).attr('vis_id'));}
catch { };
}})
if (product_visible) {
sessionStorage.setItem(vis_id, 'true')
}
else{
try {
sessionStorage.removeItem(vis_id);}
catch { };};
});
$('.parent').on('custom', function() {
var children = getChildren($(this));
var datalevel = $(this).attr('data-level')
var vis_id = $(this).attr('vis_id')
$.each(children, function() {
if (datalevel==1){
if ($(this).attr('data-level')==2){
$(this).toggle();}
else{
if ($(this).is(":visible")){
$(this).toggle();}}}
else{$(this).toggle();}
});
});
$('.product').on('custom', function() {
var children = getChildren($(this));
var vis_id = $(this).attr('vis_id')
$.each(children, function() {
item_visible = $(this).is(":visible")
if ($(this).attr('data-level')==1){
$(this).toggle();}
else{
if (item_visible){
$(this).toggle();}}
});
});
$('.product').each(function(i, tr) {
var isvisible = $(this).is(":visible")
var children = getChildren($(this));
var vis_id = $(this).attr('vis_id')
if (sessionStorage.getItem(vis_id) !== null){
$(this).trigger('custom');
}
$.each(children, function() {
var datalevel = $(this).attr('data-level')
if (datalevel !== '3'){
var vis_id = $(this).attr('vis_id')
if (sessionStorage.getItem(vis_id) !== null){
$(this).trigger('custom');};};
});
});
});
I was able to do this using XMLHTTPRequests with Flask and JS.
I developed a web project where I can upload two files and django server execute a function to return the lines that don't match with other in a pandas dataframe which is renderized in a html template. Is returned a two columns table, file1 and file2. I would like to have another column with the text and text difference 'highlighted'. I have no knowledge about javascript programing, but I don't know how to apply the function on each table row.
My views.py
def index(request):
form = UploadFileForm(request.POST, request.FILES)
if request.method == 'POST':
if form.is_valid():
check = CheckFiles(form.cleaned_data["arquivo1"], form.cleaned_data["arquivo2"]).verifica_tamanho()
if type(check) == str:
return HttpResponse('The file are the same!')
#return HttpResponse(check.to_html())
return render(request, 'index2.html', {'check': check})
else:
print('Invalid Form!')
return render(request, 'index.html', {'form': form})
and in my index2.html:
<table class="table" border="1px">
<thead class="thead-dark">
<tr>
<th scope="col">Original File</th>
<th scope="col">Secondary File</th>
</tr>
</thead>
<tr>
{% for _, record in check.iterrows %}
<tr>
{% for value in record %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</tr>
</table>
and the highlight javascript function is:
highlight($("#new"), $("#old"));
function highlight(newElem, oldElem){
var oldText = oldElem.text(),
text = '';
newElem.text().split('').forEach(function(val, i){
if (val != oldText.charAt(i))
text += "<span class='highlight'>"+val+"</span>";
else
text += val;
});
newElem.html(text);
}
.highlight {background-color: #B4D5FF}
So, I need some help to understand how to apply this javascript function on each table row.
Thank you, have a nice day.
I can see you also use jquery so:
$(.table tr).each(function(index){
$(this).addClass("highlight");
});
that way you can apply highlight to every row.
It seems like your current highlight function "highlights" chars that have changed in the string but I have no idea what it has to do with rows... What is $("#new") ... what is $("#old")...
**UPDATE **
$("table tr").each(function(index){
a = $(this).find("td");
first_td = a.eq(0);
second_td = a.eq(1);
highlight(second_td, first_td);
});
I'm new at python, but no at programing. I'm using Django to create a webapp and in some part, I have to create products. A product has a name, brand and type, but this product can have many "inventories". For example: The product Iphone has a brand Apple, type Phone and since I have 3 in my stock, there is another table that saves 3 individual codes for them (and the price and state as well).
For this solution I have used formsets, and it was working fine without the JS. What I mean is that the process is ok, but when I want to dinamically add a new row to add a new Inventory, is added visually but then when I tried to save it, it just saves the last row. I have looked into the debug console of chrome and it seems that the problem is that the name and ID, of the new row, remains the same as the original one (the prefix I believe), but I don't know how to change it. Here is my code: Note in this image that the prefix of the second row is 0
views.py:
class ProductoInline(InlineFormSetFactory):
model= Inventario
fields = ['codigo', 'Producto', 'Precio', 'Estado']
factory_kwargs = {'extra':1 , 'max_num': 10,
'can_order': False, 'can_delete': False}
class InventarioCreateView(LoginRequiredMixin, CreateWithInlinesView):
model = Producto
inlines = [ProductoInline]
fields = '__all__'
factory_kwargs = {'extra': 3, 'max_num': 10,'can_order': False,
'can_delete': False}
template_name = 'ventas/inventario_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
inventario_form.html:
{%extends "ventas/base.html" %}
{% load staticfiles %}
{% load crispy_forms_tags %}
{%block content %}
<section class="card">
<header class="card-header">Crea/actualiza producto</header>
<div class="card-body">
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
{% for formset in inlines %}
<table class="table table-bordered table-responsive-md table-striped text-center">
<tr>
<th class="text-center">Código</th>
<th class="text-center">Precio</th>
<th class="text-center">Estado</th>
<th class="text-center">Eliminar</th>
</tr>
{{formset.management_form}}
<tbody>
{% for form2 in formset %}
<tr id="{{ form2.prefix }}-row" class="dynamic-form">
<td>{{ form2.codigo }}</td>
<td>{{ form2.Precio }}</td>
<td>{{ form2.Estado }}</td>
<td><a id="remove-{{ form2.prefix }}-row" href="javascript:void(0)" class="delete-row">Eliminar</a></td>
</tr>
{% endfor %}
<tr>
<td colspan="4">Añadir producto</td>
</tr>
</tbody>
</table>
{% endfor %}
<button type="submit">Crear producto</button>
</form>
</div>
{%endblock content %}
formset.js:
function updateElementIndex(el, prefix, ndx) {
var id_regex = new RegExp('(' + prefix + '-\\d+)');
var replacement = prefix + '-' + ndx;
if ($(el).attr("for")) $(el).attr("for",
$(el).attr("for").replace(id_regex, replacement));
if (el.id) el.id = el.id.replace(id_regex, replacement);
if (el.name) el.name = el.name.replace(id_regex, replacement);
}
function addForm(btn, prefix) {
var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
var row = $('.dynamic-form:first').clone(true).get(0);
$(row).removeAttr('id').insertAfter($('.dynamic-form:last')).children('.hidden').removeClass('hidden');
$(row).children().not(':last').children().each(function() {
updateElementIndex(this, prefix, formCount);
$(this).val('');
});
$(row).find('.delete-row').click(function() {
deleteForm(this, prefix);
});
$('#id_' + prefix + '-TOTAL_FORMS').val(formCount + 1);
return false;
}
function deleteForm(btn, prefix) {
$(btn).parents('.dynamic-form').remove();
var forms = $('.dynamic-form');
$('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
for (var i=0, formCount=forms.length; i<formCount; i++) {
$(forms.get(i)).children().not(':last').children().each(function() {
updateElementIndex(this, prefix, i);
});
}
return false;
}
first of all, even if i want to display a message after click on the table, nothing happened. i didn't understand what is missing for this first step.
At the end, i want to confirm the delete after click on the button delete.
There is my code for each files html.twig and js
main.js:
var produits = document.getElementById('produits');
if (produits) {
produits.addEventListener('click', e => {
// if (e.target.className === 'btn btn-danger') {
// if (confirm('Are you sure?')) {
// const id = e.target.getAttribute('data-id');
//
// fetch(`/Delete/${id}`, {
//
// }).then(res => window.location.reload());
//}
// }
alert(2);
});
}
Affiche.html.twig:
<table class="table table-striped table-bordered" id="produits">
<tr>
<th>Nom</th>
<th>Desctiption</th>
<th>Image</th>
<th>Operation</th>
<th>Actions</th>
</tr>
{% for produit in produits %}
<tr>
<td>{{ produit.nomProduit }}</td>
<td>{{ produit.descriptionProduit }}</td>
<td><img alt="" src="{{ asset('uploads/images/'~produit.imageProduit) }}"></td>
<td>{{ produit.operationProduit }}</td>
<td>
Show
Edit
Delete
</td>
</tr>
{% endfor %}
</table>
An ID is by definition unique, here it seems you can have more than one product so you should set a class to target them all. And moreover you set a "data-id" and not an id. I used "produits-alerte" in my example.
Select products by class and loop over them to add an onClick event, so you can both trigger dialog and get the specific id of the product clicked.
So it leads to :
for (var i = 0, len = produits_alerte.length; i < len; i++) {
produits_alerte[i].onclick = function() {
if (confirm("Are you sure?")) {
console.log('Product ID Clicked : ' + this.getAttribute("data-id"));
//delete product with id above
} else {
//or don't
}
}
}
Here is a working solution using only vanilla JavaScript.
create a function:
<script>
function getvalue() {
alert("Hi");
return;
}
</script>
Link it to the button with 'onClick':
Delete
For some reason, I cannot obtain the text/html from the 2nd TD as specified below
The template tags are Django.
{% block content %}
{% if host.host_scripts %}
<table class="table">
<button class="btn btn-info add" id="{{forloop.counter}}">Add to session</button>
{% for script in host.host_scripts %}
<tr>
<td id="script-name-{{forloop.counter}}">{{script.scriptName}}</td>
<td id="script-output-{{forloop.counter}}">{{script.scriptOutput|linebreaks}}</td>
</tr>
{% endfor %}
</table>
{% endif %}
<script>
$(".add").click(function(e) {
var id = $(this).attr('id');
scriptList = []
e.preventDefault();
$("#myTable-"+id+" tr ").each(function() {
var name = $(this).find('#script-name-'+id).html();
var output = $(this).find('#script-output-'+id).html();
var myList = [name, output];
console.log('Name -> ' + name);
console.log('Output -> ' + output);
scriptList.push(myList);
});
});
</script>
{% endblock %}
I can get the name variable just fine. For some reason I cannot get the scriptOutput variable.
I assumed this was due to |linebreaks but removing it has no influence on the behavior.
Desired behavior:
Per tr in the table, get the contents of the td's and add to the list.
Current behavior:
It only get's the name variable, skips the scriptOutput variable for some reason.
I managed to get 'some' output, now I get the same value returned for output for every scriptName. I'm baffled as I can't see what's wrong with the ID's.
{% block content %}
{% if host.host_scripts %}
<table class="table myTable">
<button class="btn btn-info add" id="{{forloop.counter}}">Add to session</button>
{% for script in host.host_scripts %}
<tr>
<td id="script-name-{{forloop.counter}}">{{script.scriptName}}</td>
<td id="script-output-{{forloop.counter}}">{{script.scriptOutput|linebreaks}}</td>
</tr>
{% endfor %}
</table>
{% endif %}
<script>
var scriptList = []
$("#myTable-"+id+" tr").each(function() {
var name = $(this).find('td:first').html();
var output = $('#script_output-'+id).html();
var script = [name, output];
scriptList.push(script);
console.log('Text: ' + output);
});
console.log(scriptList);
</script>
I am aware I should probably expand my ID to something of sorts:
<td id="script-output-{{forloop.parentloop.counter}}-{{forloop.counter}}">{{script.scriptOutput|linebreaks}}</td>
Yet I can't seem to figure it out, what am I doing wrong??
Try this instead:
<script>
$(".add").click(function(e) {
var id = $(this).attr('id');
scriptList = []
e.preventDefault();
$("#myTable-"+id+" tr ").each(function(index, el) {
var name = $(el).find('#script-name-'+id).html();
var output = $(el).find('#script-output-'+id).html();
var myList = [name, output];
console.log('Name -> ' + name);
console.log('Output -> ' + output);
scriptList.push(myList);
});
});
</script>