Announcing Dash Bio 1.0.0 🎉 : a one-stop-shop for bioinformatics and drug development visualizations.

Use dynamic cards instead of Dash Table - how to output data frame values to cards?

For purely aesthetic reasons, I want to output table contents to different components in a card. Each of these components have an id, and each card is generated dynamically based on the number of rows in a Data Frame through a callback. I want to be able to get the Data Frame values and output it to the card components.

I’m thinking of using a MATCH callback, but I’m not sure how to properly implement it. I think my problem is finding an appropriate input to run the callback? Essentially, I’m not sure how to get the callback to output data values for every card that has been generated.

Hope someone can help, thank you!

Here are the relevant codes. The callback I need help with is at the end:

The card:

def cardentry(i):
    return dbc.Card(
        dbc.CardBody(
            [
                dbc.Row(
                    [                    
                        dbc.Col(
                            html.P(
                                [                                
                                    html.B(style={"font-weight": "bold", "font-size":"1.1rem"},
                                            id={
                                                'type':'card-knw-name',
                                                'index':i
                                            }
                                    )
                                ]
                            ),
                            className="col-auto"
                        )
                    ],                
                ),
                dbc.Row(
                    [
                        dbc.Col(
                            [                            
                                html.P([html.B("Equipment: "),
                                html.Span(id={
                                                'type':'card-knw-eqpt',
                                                'index':i
                                            }
                                )])
                            ],                        
                                className="col-auto"
                        ),
                        dbc.Col(
                            [                            
                                html.P([html.B("Process: "), 
                                html.Span(id={
                                                'type':'card-knw-prc',
                                                'index':i
                                            }
                                
                                )])                            
                            ],
                            className="col-auto"
                        )
                    ]
                ),
                dbc.Row(
                    [
                        dbc.Col(
                            className="col-2 align-self-end",
                            id={
                                'type':'card-edit-btn',
                                'index':i
                            }
                        )
                    ]
                )
            ]
        ),
        className="card-entry hvr-side",
        style={"margin-bottom":"1.5rem"},
        id={
            'type':'card',
            'index':i
        }
    )

Generating the card:

@app.callback(
    [
        Output('knw-card-container','children')
    ],
    Input("url", "pathname"),
    [
        State('knw-card-container','children')
    ]
)
def elements(pathname, card):
    if pathname == '/directory/knowledge':
    
        sql = """ SELECT knw_id, knw_name, knw_target
                FROM knowledge knw
                WHERE knw_delete_ind = false
                """
        values = []
        cols = ['Knowledge ID','Knowledge Name', 'Knowledge Target']
        df = db.querydatafromdatabase(sql, values, cols)

        totalRows = df.shape[0]

        for i in range(totalRows):
            card.append(cardentry(i))
        return [card]
    else:
        raise PreventUpdate

My callback:

@app.callback(
    [Output({'type':'card-knw-name','index':MATCH},'children'),
    Output({'type':'card-knw-eqpt','index':MATCH},'children'),
    Output({'type':'card-knw-prc','index':MATCH},'children'),],
    Input('url','pathname'),
    State({'type':'card','index':MATCH},'children')
    
)
def cardknwname(pathname, card):
    if pathname == '/directory/knowledge':
        sql = """
            SELECT k.knw_name, e.eqpt_name, p.prc_name
            FROM knowledge k
                INNER JOIN equipmentknowledgeentry eqke ON eqke.knw_id = k.knw_id
                INNER JOIN equipment e ON e.eqpt_id = eqke.eqpt_id
                INNER JOIN processknowledgeentry pke ON pke.knw_id = k.knw_id
                INNER JOIN process p ON p.prc_id = pke.prc_id
            WHERE knw_delete_ind = False
        """
        values = []
        cols = ['knw','eqpt', 'prc']
        df = db.querydatafromdatabase(sql, values, cols)

        knw = []
        eqpt = []
        prc = []

        for index, row in df.iterrows():
            print('row')
            knw[index] = row['knw']
            eqpt[index] = row['eqpt']
            prc[index] = row['prc']

        return [knw, eqpt, prc]
    else:
        raise PreventUpdate

Hi,

If the dataframe is entirely defined by pathname, i. e. you have one specific dataframe fetched from a DB per page, then I think you might be overthinking how to create the cards.

If I were you, I would modify cardentry to accept as parameters [‘knw’,‘eqpt’, ‘prc’], then use df.iterrows in the elements callback to create all the cards at once. In other words, you have currently two callbacks: one to create the right number of cards and one to fill it with information according to the row. You can combine them in one that creates the cards with the right values.