Datagrids with dynamic columns

Hi
I am trying to build a datagrid where the number and types of columns are know only at runtime, because they’re fetched from DB.
So, the DG is built completely from code following these instructions.
I was able to build each row by adding single DataRowPanels in either way:

  • simple way”: that is, just setting the item property of the DRP, with the drawback that data is displayed only as labels (see code of button_1_click)
  • complex way”: that is by adding components for each column 1 by 1, so to allow more flexibility on data display (see code of button_2_click)

But I really seem unable to figure out how I could do the same setting up a RepeatingPanel and assigning a DataRowPanel as item_template at runtime.
I’ve tried (see code of button_3_click) but couldn’t get over these 2 questions:

  1. when assigning the RP’s items property to a list of dictionaries, I get the error TypeError: '<invalid type>' object is not callable
  2. how could I hook up the Delete Button for each row?

See the GIF for better understanding.

Here’s a clone link:
https://anvil.works/build#clone:4RF24TBSNIRCZIBB=6HVB56OUZCKUQGE7BXVJQ2NJ

In the clone I’ve tried to make things simpler by accessing tables from the client, and I am building data rows in a horrible way, and the code is written in a try-to-be-just-clear-not-too-pythonic mood, but that’s not the point.

The focus is on the three button_x_click event handlers and the way the DataGrid (and its components) are built at runtime.

I think this can be istructive for any Anvil noob like me, since it’s about understanding Anvil’s “foundation objects” like data grids, data rowpanels and repeating panels.

Thanks for any help.

Hi - did you find any solution to this issue ?
I’ve tried building dynamic columns with repeating panel too and it seems that you can only have labels in the repeating panel part, and not other components like Button or Link. But, you are able to set any component in the DataRowPanel header …
Any guidance/help is welcome.

Thanks

Hi both - i’ve taken a look at this and have a solution:

https://anvil.works/build#clone:PNJRSH2B64FS4WMR=CL2GIIGFXQHUBOX63B52WCBX

The main thing to think about here is RepeatingPanel.item_template

You have to set this to:

  • a string - which is a path to a Form "MyApp.RowTemplate1"
  • a component class - e.g. DataRowPanel

In the clone above with option 3 you were setting
self.repeating_panel_1.item_template = DataRowPanel()
which is neither of the options above - you are setting it to an instance rather than a class.

But I see in option 3 why DataRowPanel isn’t going to work because you have no control over how it is created…

What you could do is:

  1. Subclass from DataRowPanel
  2. Mimic a class with a function
    # defined inside self.button_click_3
    form_self = self
    class DataRowSubclass(DataRowPanel):
      def __init__(self, item, **properties):
        super().__init__(item=item, **properties) # DRP needs the item
        # first column for DELETE BUTTON
        # Question #1: how to pass the obj_id to the button?
        # all data_row panels have an item property so use it...
        row = item
        btn_delete = Button(icon='fa:minus-circle',tooltip='Delete this record', 
                            text=row['obj_id'],
                            tag={'obj_id': row['obj_id']},
                            align='center') 
   
        btn_delete.set_event_handler('click', form_self.btn_delete_click)      
        self.add_component(btn_delete, column=str(0))
        # dynamic columns follow
        column_id = 1
        for attribute in attributes:
          self.add_component(
            Label(text=row[attribute['attr_name']]),
            column=column_id
          )
          column_id += 1

    self.repeating_panel_1.item_template = DataRowSubclass


As an alternative - we can mimic this behaviour of a Component Class by writing a function that takes **properties as an argument and returns a component instance…

    def TemplateRowFunc(item, **properties): # we will get an item from RepeatingPanel
        drp = DataRowPanel(item=item, **properties) # pass the item to DRP
        row = item
        btn_delete = Button(icon='fa:minus-circle',tooltip='Delete this record',
                            text=row['obj_id'],
                            tag={'obj_id': row['obj_id']},
                            align='center')   
        btn_delete.set_event_handler('click',self.btn_delete_click)      
        drp.add_component(btn_delete, column=str(0))
        # dynamic columns follow
        column_id = 1
        for attribute in attributes:
          drp.add_component(
            Label(text=row[attribute['attr_name']]),
            column=column_id
          )
          column_id += 1

        return drp

    self.repeating_panel_1.item_template = TemplateRowFunc


Hope that helps

3 Likes

Worked like a charm :clap: :clap: :clap: - thanks a lot !

Thanks for dedicating time to this.
Now apart from cloning and using, I really want to do my homework and study deeply your post… :smiley:
I need to understand this.
Thanks!

1 Like