Pattern matching callback, input wildcards not in outputs

Hey guys,

I am trying to update the content of my div after a button has been pressed.

@app.callback(
    Output("industry-view", "children"),
    [
        Input({"type": "industry-filter", "index": MATCH}, "n_clicks"),
        State({"type": "industry-filter", "index": MATCH}, "value"),
    ],
    prevent_initial_call=True,
)
def filter_industry_cards(n_clicks, value):
    if n_clicks == 0:
        raise PreventUpdate
    print("callback fired")
    print("industry value", value)
    return value

Currently this throws an error, complaining my Input/State wild cards are not in my Output.

However, when I change my Pattern match to be “All” - this works without any issues, but this isn’t my desired functionality.

Basically, I have a div with an id of “industry-view”, by default this will render some card elements on the page, each card element has a button. Clicking one of the card elements will update the children of “industry-view”

As mentioned before, the callback runs fine when I change the match to “ALL” but it gives me the values of all the buttons, and I would just like the value of the button that is clicked.

Could anyone help me with this?

1 Like

Hi,

Welcome to the community! :slight_smile:

Your intuition is right, however MATCH only works when there is a matching MATCH in the Output (pun intended)… :slight_smile:

The simplest solution is to use ALL as you tried, and then use the callback_context to figure out which one of your buttons were pressed. The callback context syntax has been simplified a lot in the latest release, and you can find a simple example here.

It looks a bit overkill to pass all values in this case, but a solution with MATCH would let your app much more convoluted.

Hope this helps!

2 Likes

Thank you! Exactly what I needed

I came across this problem with pattern matching too and found a simple solution :slight_smile:

If you want to keep html.Div(id="industry-view") position in the layout (for display paragraph, figures, etc), one way to do that without disturbing the layout is to change children of html.Div(id="industry-view") component according to which button you clicked.

A new html.Div component will be created each time you click a button and display the data associate with that button, html.Div(id="industry-view" will be like a wrapper instead. A dcc.Store component is used to activate filter_industry_cards func instead.

from dash import Dash,html,ctx,dcc
from dash.dependencies import Input, Output,State,MATCH,ALL

app = Dash(__name__)

app.layout = html.Div([
    html.Button(children="Button_1",id={"type": "industry-filter", "index": 0},
                n_clicks=0),
    html.Button(children="Button_2",id={"type": "industry-filter", "index": 1},
                n_clicks=0),
    html.Button(children="Button_3",id={"type": "industry-filter", "index": 2},
                n_clicks=0),
    
    dcc.Store(data=0,id="buffer_input"),
    html.Hr(),
    html.P("Paragraph 1."),
    html.Hr(),
    html.Div('Click a button please.',id="industry-view"),
    html.Hr(),
    html.P("Paragraph 2."),
    html.Br(),
])

    
@app.callback(
    [Output("industry-view", "children"),
    Output("buffer_input","data")],
    Input({"type": "industry-filter", "index": ALL}, "n_clicks"),
    State("buffer_input","data"),
    prevent_initial_call=True,
)
def buffer_output(input_btn_click,buffer_stored):
    buffer_idx = ctx.triggered_id
    buffer_idx["type"]="industry-buffer"
    buffer_stored = buffer_stored+1
    
    return [html.Div(id=buffer_idx),buffer_stored]


@app.callback(
    Output({"type": "industry-buffer","index":MATCH}, "children"),
    Input("buffer_input","data"),
    State({"type": "industry-filter", "index": MATCH}, "children"),
    prevent_initial_call=True,
)
def filter_industry_cards(activate,data_value):
    return data_value + " is clicked."

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