What I’m trying to do:
I am trying to make a data grid component have filterable and sortable columns.
To make the filtering work I need to show and hide components from the repeating panel rows dynamically (if the column_id of the component is not in the columns of the repeating panel then it should be hidden other wise it should be visible.)
So far I have got sorting working and the filtering working for the repeating panel. The problem is if a component in a row was nested in a column that is now hidden, the component basically gets set to full width.
My solution is to loop through the components in the row and see if the column of the component is in an ‘active_columns’ list.
So how do I get the column_id of each component programmatically?
What I’ve tried and what’s not working:
I have tried component.column and component.column_id but neither of these are attributes of the components.
I have also tried some introspection but my skills are not leet enough to get what I need.
Code Sample:
class RowTemplate1(RowTemplate1Template):
def __init__(self, **properties):
# Set Form properties and Data Bindings.
self.init_components(**properties)
self.add_event_handler('x-filter-columns', self.filter_columns)
def filter_columns(self, active_columns, **event_args):
for c in self.get_components():
try:
if c.column in active_columns:
c.visible = True
else:
c.visible = False
except Exception as err:
print(err)
# prints 'DropDown' object has no attribute 'column'
I don’t know how to get the column ids of existing columns, but it’s easy to get the column ids of columns created from code. That’s what I did in the DataGridJson component, which includes sortable columns.
Hiding columns is done by modifying the Data Grid’s columns property. This is a list of columns; each column is represented as a dictionary. So to hide a column, remove a column from the list. Add it back to the list to show it again.
To make the change live, you must run the assignment (=) operator for the columns property.
Here’s how you hide a column:
# Filter the column with title 'Stock Price' out of the columns list.
column = [c for c in self.data_grid_1.columns if c['title'] == 'Stock Price'][0]
# Remember the details of the hidden column
self.hidden_columns.append(column)
# Remove it from the Data Grid's column list
self.data_grid_stocks.columns.remove(column)
# Make the change live
self.data_grid_stocks.columns = self.data_grid_stocks.columns
And to show it again, you do the reverse:
# Filter the column with title 'Stock Price' out of the hidden columns list.
column = [c for c in self.hidden_columns if c['title'] == 'Stock Price'][0]
# Remove it from the hidden columns list
self.hidden_columns.remove(column)
# Add it to the Data Grid's column list
self.data_grid_stocks.columns.append(column)
# Make the change live
self.data_grid_stocks.columns = self.data_grid_stocks.columns
Thanks @stefano.menci but unfortunately even though I know the ID of the column, I need to check whether each of the components in the data grid row have a matching column ID. If they don’t then they just sit at the bottom of the row in full width.
Thank you for the response @jonathan but I have this section working already, its hiding the components of the columns in each row I am struggling with - the docs don’t deal with this situation but rather a key for a dict (not a component like a Button)
If so then you can achieve this using tags I believe. Are the components added from the designer or via code?
My current project is a universal Data Table Viewer and Editor for use inside any Anvil App. Having to use lots of tags and such to find out what and where the component belongs to.
If I remember you can add a component to a column of the DataGrid (and the component sits in that column) or under all the columns (and this can be used for example for a little report form or a nested DataGrid).
If I understand your problem, when some columns are removed from self.datagrid.columns, their components are still visible and slide to the under-all-the-columns area.
One solution could be to hide those components with data binding: make them invisible when their columns are invisible.
You could create a global variable on a module imported by both the main form and the template forms:
visible_columns = {0, 1, 4, 5}
Then set the visible property of each component, for example, for the component in the third column:
2 in visible_columns
Another solution could be to add the components from code, just like the DataGridJson does. But this would make sense only if you reuse this technique. I do use the DataGridJson in most of my apps, and I upgrade it whenever I need it to manage a new component or I need a new feature.
No they are all dynamic - the idea is to produce a component you can use with any data grid hence the need for finding the Column ID dynamically. It must be stored somewhere, I just don’t know where.
Mmm that could work but I am trying to keep it as low touch as possible - I would like as little as possible modification of the data grid as possible so that it is easy to implement. In this instance adding components from code will be too cumbersome unfortunately.
The DataGridJson solution is the most low touch: you don’t even add components to the columns. You already have two lists of dictionaries, one for the columns and one for the data. All you need to do is add a little more details into them, and forget managing the DataGrid from the IDE.
The data binding solution is low impact too, assuming that you already have a global module. And even if you don’t have it yet, adding a global module sounds easier than disentangling tags and ids that are not available. Even assuming that you figure out the column id thing, you would still need to do something with it, and it’s unlikely to have a smaller impact than setting a data binding.
The only problem would be if hiding them via the visible property with data binding would still preserve some unwanted padding or gaps.
This is a great question. Here’s one way of doing it:
The piece of information each component is missing is what column it is in. We need to add that information to the component when the row is initialised.
Here’s what I changed in your example:
Storing which column a component is in
I added the self.components_by_column dictionary to the row template, to store which column each component is in. The key matches the Data Grid column’s data_key property. When you add a component to the row template, you’ll need to add that component to the dictionary in the relevant column.
Matching the keys sent by the parent form with the row template
In the Form1’s Data Grid, I replaced column ID in active_columns with the column’s data_key to match the key I used in the self.components_by_column dictionary. (You could still use the column ID but the data_key is both available in the Anvil editor and more stable than the user-visible title - you’ll probably edit the column title in the designer more often than the data_key.)
Updating the filter function to use the components_by_column dictionary
Lastly, I updated the filter_columns function in the row template to iterate through each component in the components_by_column dictionary, changing it’s visibility.
I hope that makes sense. Please let us know if you have any questions.
Are you looking for a universal Table Viewer/Editor? If so, I just now am putting the finishing touches on two custom components I have built entirely in Anvil to do just that.