Dynamic databindings with unknown column headers

What I’m trying to do:
How to display a table with Labels and Textboxes, when I don’t know the number of columns, or what the column names will be?

E.g.
A user specifies any set of animals categories (dog/cat/parrot/anything), and there are specific types of each animal:
dog = [d1, d2, d3, d4]
cat = [c1, c2, c3, c4]
mouse = [m1, m2, m3, m4]

My program then generates combinations (e.g. [d1, c3, m4]) and the user specifies how to evaluate it (e.g. ‘ranking’). So a label for each component (d1, c3, m3) should be created programatically and displayed under the correct column, while the ranking should be a textbox that writes back to the ‘combinations’ datatable.

Currently, the column headers and column names are not aligned, and the text box does not write back to my datatable.

What I’ve tried and what’s not working:
Read the docs, but this only works if I know the column name: Anvil Docs | Data Bindings

Dynamic data bindings post, but here the components (e.g. labels) were added with the drag-and-drop, not programatically: About data binding with custom components

Generate text boxes with code, I tried this but couldn’t get the headers to align with the data input textbox: Want to be able to generate x number of TextBoxes

Code Sample:


class ItemTemplate1(ItemTemplate1Template):
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
    
    data = self.item # a dictionary
    
    # Dynamically create components for each key-value pair
    for key, value in data.items():
      if key == 'ranking': # will do this check differently in my actual code
        # Create a TextBox for editable 'c5205'
        textbox = anvil.TextBox(text=value)
        # Optionally, bind the TextBox's lost_focus event to save changes
        textbox.tag = key  # Use tag to identify the column
        # textbox.set_event_handler('lost_focus', self.textbox_lost_focus)
        self.flow_panel_1.add_component(textbox)
        
      else:
        # For non-editable fields, just display the value
        label = anvil.Label(text=f"{key}: {value}")
        self.flow_panel_1.add_component(label)

    def textbox_lost_focus(self, sender, **event_args):
      edited_value = sender.text
      column_name = sender.tag
      print(f"Edited {column_name}: {edited_value}")

Clone link: Anvil | Login

Took just a quick skim, but you’re adding to flow_panel_1 instead of repeating_panel_1.

Also see the docs about creating your matching Data Grid columns on the fly: Manipulating Data Grids using code

2 Likes

That was helpful, I am creating the data grid fully programatically now, and the columns are correctly aligned.

However, all the data is displayed as a label. In the example in the docs, to add a textbox, it suggests this:

python

row = DataRowPanel()
textbox = TextBox(text="Alice")
# Add the textbox to the column with id "A"
row.add_component(textbox, column="A")
grid.add_component(row)

But this will add a new row underneath my data with a text box. How do I add a text box in line with the rest of my data (on the same row)?

Here you can see how I have done it. I have used Link rather than TextBox, but it should be similar: DataGridJson - A DataGrid with quick simple setup

  1. Add the TextBox to your repeating_panel_1, instead.
  2. When you do, use the same column id that you gave the column when you added the column to your grid’s columns.

To drag-and-drop a textbox from the editor and changing the column id will only work if I know the column id ahead of time, right? What I’m trying to do is add a text box if I don’t know the column ids ahead of time, so I need an approach only with code.

Since you are creating the columns (see "grid.columns = "… in Manipulating Data Grids using code, you get to assign whatever ids you want. Just be sure to use those same ids when adding to the Repeating Panel.

Ohh right, I just read the docs again, so looks like I can loop over each row, and as I’m adding each component using the id, I can specify whether it should be added as a label or as a textbox? I’ll give that a try and revert back in a few days. Thank you!

That looks cool, thank you for sharing! I wanted to try achieving this with built in functions first, but if I can’t I’ll definitely give your package a try, thanks!