✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
⚡️ Concerned about the grid? Kyle Baranko teaches how to predicting peak loads using XGBoost. Register for the August webinar!

List of clickable Elements. I'm definitely doing it wrong!

Hi,
I’m new to dash and developing web layouts, but not to python.
My goal is to have a list of clickable elements which all have basically the same callback, namely to display further info and to execute some data driven code.
I’ve tried to do it with pattern-matching callbacks, since the list is not fixed and might change, but I find it hard to believe that I’m doing it efficiently. Here’s some of my code:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, MATCH, ALL
import datasets
import html_generators
import re

app = dash.Dash(__name__)


sentences, embeddings = datasets.dataset_from_csv('sentences')
categories = set(sentence['category'] for sentence in sentences if sentence['category'])




app.layout = html.Div([
    html.Div(
        className="app-header",
        children=[
            html.Div('Plotly Dash', className="app-header--title")
        ]
    ),
    html.Div(
        className="arg-list-pane",
        children=[
            html.Div(className="arg-list-box", children=[
                html.Div('Header', className="arg-list-header"),
                html.Ul(children=[
                    html.Div(
                        sentence['sentence'],
                        id={'type': 'argument-container', 'index': sentence['id']},
                        className="arg-container") for sentence in sentences]
            )]
            ),
            html.Div('Argument Details', id="argument-detail-box")
        ]
    ),
    html.Div(
        className="category-pane",
        children=[
            html.Div(className="category-box", children=[
                html.Div(
                    className="category-header",
                    children=[
                        dcc.Input(id='cat-input', value='Add Category..', type='text'),
                        html.Button(id='submit-cat-button', n_clicks=0, children='Add')
                    ]),
                html.Ul(
                    id='cat_list',
                    children=[
                        html.Div(category, {'type': 'category-item', 'index': index},
                                 className="category-container")
                        for index, category in enumerate(categories)
                    ])
                ]
            ),
            html.Div("algorithm or something, idk, I'm not a web designer", id='algo-box')
        ]
    )
])


@app.callback(
    Output('cat_list', 'children'),
              Input('submit-cat-button', 'n_clicks'),
              State('cat-input', 'value'),
              State('cat_list', 'children')
)
def add_category(n_clicks, cat_input,children):
    if n_clicks > 0:
        new_cat = html.Div(cat_input,
                           id={'type': 'category-item', 'index': len(children)},
                           className="category-container")
        children.append(new_cat)
    return children


@app.callback(
    Output('argument-detail-box', 'children'),
    Output('algo-box', 'children'),
    Input({'type': 'argument-container', 'index': ALL}, 'n_clicks')
)
def handle_arg_click(n_clicks):
    """handle click on a list item.
    When a list item is clicked, the n_clicks property will update, the callback
    will be triggered and the responsible list element will be stored in
    `dash.callable_context.triggered`. From that, the index/id of the sentence
    is known. With that information, the algorithm can be fed and the info box
    can be changed.
    """
    if not dash.callback_context.triggered[0]['value']:
        raise dash.exceptions.PreventUpdate


    sentence_id = int(re.findall('(\d+)',dash.callback_context.triggered[0]['prop_id'])[0])
    print(dash.callback_context.triggered)


    sentence_data = sentences[sentence_id]
    details_table = html_generators.create_details_table(sentence_data, header='argument details')
    return details_table, 'Algorithm output here'


if __name__ == '__main__':
    app.run_server(debug=True)

This is very slow, I assume due tot the fact that I dynamically create a callback for each output and match them all. I tried to just match the one input that was actually changed with Input({'type': 'argument-container', 'index': MATCH}, 'n_clicks'), however, since the output is not generated for a generic ID, that didn’t work.

Also, getting the correct ID by regexing for the number in the stringified dict in [‘prop_id’] seems pretty fishy.

So here’s my question:
How do I make clickable list elements which are dynamically created after loading some data. Basically, I want to have a list of sentences and by clicking these list elements, I want the details box to display details in a table, and the algorithm box to display the results of a chosen algorithm as a list of sentences (which also should be clickable).

Any help would be greatly appreciated, I suspect the answer to be relatively simple, I just can’t find it.

Kind Regards,

ZBN