0

I want to create a list of words that updates dynamically in a Dash application. For that, I would like to use ListGroup and ListGroupItem from the dash_bootstrap_components library.

Now, my callback bellow would work if the children attribute of the ListGroup component would be have the same type on input as well as on output. However, even if my output is a list, which is clearly an accepted type for the children of ListGroup, what I am reading in from the function is a dictionary. of the type: {'props': {'children': 'sample_word'}, 'type': 'ListGroupItem', 'namespace': 'dash_bootstrap_components'}.

The question then is, how do I get as input from the ListGroup component the list of ListGroupItem components I am returning in the callback?

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html

app.layout = html.Div(children=[dbc.Row(dbc.ListGroup(id='list-group-items'))])

@app.callback([Output('list-group-items','children')],
    [Input('input-domain-specific-words','value'),
     Input('add-button','n_clicks'),
     Input('delete-button','n_clicks'),
     Input('reset-button','n_clicks')],
    [State('list-group-items','children')])

def update_list(word,n_clicks_add,n_clicks_delete,n_clicks_reset,listChildren):
    ctx = dash.callback_context
    if not ctx.triggered:
        raise dash.exceptions.PreventUpdate
    else:
        button_id = ctx.triggered[0]['prop_id'].split('.')[0]

    if button_id not in ['add-button','delete-button','reset_button']:
        raise dash.exceptions.PreventUpdate
    else:
        
        if button_id == 'delete-button':
            for item in listChildren:
                if item.children == word:
                    listChildren.remove(item)
                
        elif button_id == 'add-button':
            listChildren.append(dbc.ListGroupItem(word))

        elif button_id == 'reset-button':
            listChildren = []
    return [listChildren]

1 Answers1

0

I expect this might be useful later on, even if only for myself. The answer is that the ListGroup component does return a list of ListGroupItem components that are written in JSON when collected in the callback. Therefore, whenever you append a ListGroupItem component to a ListGroup it will get added as a ListGroupItem() element, however, when you get it back it will read like a JSON. Bellow, I am showing an example from my code: string1 and string2 have been previously added to the ListGroup component, however, string3 was added in the current callback, therefore it appears as a ListGroupItem element rather than a JSON.

[{'props': {'children': 'string1'}, 'type': 'ListGroupItem', 'namespace': 'dash_bootstrap_components'}, {'props': {'children': 'string2'}, 'type': 'ListGroupItem', 'namespace': 'dash_bootstrap_components'}, ListGroupItem('string3')]

My final version for my specific callback is:

@app.callback(
    [Output('list-group-items','children')],
    [Input('input-domain-specific-words','value'),
     Input('add-button','n_clicks'),
     Input('delete-button','n_clicks'),
     Input('reset-button','n_clicks')],
     [State('list-group-items','children')],
     )

def updateWordList(word,n_clicks_add,n_clicks_delete,n_clicks_reset,listChildren):

    ctx = dash.callback_context
    if not ctx.triggered:
        raise dash.exceptions.PreventUpdate
    else:
        button_id = ctx.triggered[0]['prop_id'].split('.')[0]

    if button_id not in ['add-button','delete-button','reset-button']:
        raise dash.exceptions.PreventUpdate
    else:
        if not listChildren:
            listChildren = []

        if button_id == 'delete-button':
            for item in listChildren:
                if item['props']['children'] == word:
                    listChildren.remove(item)
                
        elif button_id == 'add-button':
            listChildren.append(dbc.ListGroupItem(word))

        elif button_id == 'reset-button':
            print('pressed reset')
            return [[]]
    
    return [listChildren]