Second Callback is fired when the input in first callback is triggered!

When I click the button with the id ‘button_click’ the second callback gets triggered too.

I want the second callback to be triggered with the second input button.

@app.callback(
    Output(component_id='add_stations', component_property='children'),
    Input(component_id='button_click', component_property='n_clicks'),
    State(component_id='no_of_stations', component_property='value'),
    prevent_initial_call=True
)
def update_output(n_clicks, no_of_stations):
        if n_clicks is None or n_clicks == 0: # Output works when clicked on button create
            raise PreventUpdate

        if no_of_stations < 2: # Stations cannot be less than 1
            return(dbc.Row(dbc.Col(dbc.Alert('Error: Enter a Value Greater than 2!', color='danger')))) # Error if staions are less than 2
        else:
            stations = Patch()
            stations.clear()
            # Creates the inputs for station name and chainage depending on the number of stations
            stations.append(html.H3('Enter Station and Chainage Details:'))
            for i in range(no_of_stations):
                stations.append(
                    dbc.Row([
                        dbc.Col(dbc.Label('Station Name'), width='2', style={'margin-top': '0.5rem', 'text-align': 'right'}),
                        dbc.Col(dbc.Input(type='text', placeholder=f'Enter Station Name {i+1}', id={'type': 'station_name_value', 'index': i}), width='4'),
                        dbc.Col(dbc.Label('Chainage'), width='2', style={'margin-top': '0.5rem', 'text-align': 'right'}),
                        dbc.Col(dbc.Input(type='number', placeholder=f'Enter Chainage {i+1} (In Km)', id={'type': 'chainage_num_value', 'index': i}), width='4'), 
                    ], style={'margin-bottom': '0.5rem'}),
                    )
            stations.append(html.Br())

            stations.append(dbc.Button("Generate Graph", color='primary', id={'type': 'create_button', 'index': 1}, style={'margin-left': '50%'})) # Button to generate graph

            return stations
                

@app.callback(
     Output('graph', 'src'),
     Input({'type': 'create_button', 'index': ALL}, 'n_clicks'),
     [State('headway', 'value'),
     State('dwell_time', 'value'),
     State({'type': 'station_name_value', 'index': ALL}, 'value'),
     State({'type': 'chainage_num_value', 'index': ALL}, 'value')],
     prevent_initial_call = True,
)
def plot_graph(n_clicks, headway, dwell_time, station_name, station_dis):
    if n_clicks is not None:
        fig_bytes = generate_matplotlib_graph(headway, dwell_time, station_name, station_dis)
        fig_base64 = base64.b64encode(fig_bytes).decode('utf-8')
        print(ctx.triggered_id)
        print(ctx.inputs)
        print(n_clicks)
        return f'data:img/png;base64, {fig_base64}'

Hi @Armaan1Gohil welcome to the community!

this check always returns True if using pattern matching callbacks.

Since n_clicks is a list of len(number of buttons) the list is never empty. You have to check, if any item of n_clicks exists, for example:

if any(n_clicks):
    ...
    ...
else:
    dash.no_update

MRE:

import dash
from dash import html, Input, Output, ALL

app = dash.Dash(__name__)

app.layout = html.Div([
    html.Button(id='btn', children='Create Buttons'),
    html.Div(id='container'),
    html.Div(id='callback_container'),

])


@app.callback(
    Output('container', 'children'),
    Input('btn', 'n_clicks'),
    prevent_initial_call=True
)
def create_buttons(_):
    return [
        html.Button(
            id={'type': 'cb_button', 'index': i},
            children=f'Button_{i}')
        for i in range(5)
    ]


@app.callback(
    Output('callback_container', 'children'),
    Input({'type': 'cb_button', 'index': ALL}, 'n_clicks'),
    prevent_initial_call=True
)
def triggerd_by_cb_buttons(n_clicks):
    if any(n_clicks):
        return 'triggered'

    return dash.no_update


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

mred pmcb

Thank you @AIMPED , it worked!

Hi @AIMPED can you explain why ANY worke?

If ANY check for a true value in the len of n_cliks. Second callback should be trigger on clicking the first button?

Hi @Armaan1Gohil.

The check with any is just to prevent the callback to execute the full callback but rather stop it because it was triggered just because you added pattern matching content (buttons) dynamically.

After generating the buttons, the callback will be triggered, n_clicks will look something like this:

n_clicks = [None, None, None, None]

any just checks each item an returns True if any of those items has a value. In this case it returns False the code execution will be stopped.

After the first click on a button, n_clicks will look like this:

n_clicks = [1, None, None, None]

any will return True all code of the callback will be executed.

I hope this helps.