Black Lives Matter. Please consider donating to Black Girls Code today.

Which component triggered the callback

Hi,

Is it possible to know which component was the one that triggered some particular callback?

Thanks,
Yossi

1 Like

It isn’t right now, but this PR https://github.com/plotly/dash/pull/140 is a proposition to make this possible.

Fore more, see this community thread: Input two or more button - How to tell which button is pressed?

I would like something similar, and what may work is to have hidden elements which record a timestamp of the last modification, and them from these timestamps, pick the control which was touched last. Potential problems:

  • Initial state is nondeterministic… but have all your controls agree at the start, and hopefully this isn’t a problem?
  • Sequence of user actions not guaranteed to match the order the requests reach the server.
  • Clock skew on the backend, if talking to multiple servers.

There might be other problems I haven’t thought of. Here’s an example:

import time
import json
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
from dash import Dash

def update_timestamp(input):
    return time.time()

def choose_latest(*timestamps_and_states):
    assert len(timestamps_and_states) % 2 == 0
    midpoint = len(timestamps_and_states) // 2
    timestamps = timestamps_and_states[:midpoint]
    states = timestamps_and_states[midpoint:]
    most_recent = states[timestamps.index(max(timestamps))]
    return json.dumps(most_recent)

app = Dash()

app.layout = html.Div([
    dcc.Input(id='input-a', type="text"),
    dcc.Input(id='input-b', type="text"),
    dcc.Input(id='input-c', type="text"),
    html.Div(
        [
            'Timestamps:', html.Br(),
            'A: ', html.Span(id='input-a-timestamp'), html.Br(),
            'B: ', html.Span(id='input-b-timestamp'), html.Br(),
            'C: ', html.Span(id='input-c-timestamp'), html.Br(),
        ],
        style={'display': 'block'}  # Switch this on or off for debugging.
    ),
    html.Span(['and the latest value is: ']),
    html.Span(id='latest-value')
])

app.callback(
    Output('input-a-timestamp', 'children'), [Input('input-a', 'value')]
)(update_timestamp)
app.callback(
    Output('input-b-timestamp', 'children'), [Input('input-b', 'value')]
)(update_timestamp)
app.callback(
    Output('input-c-timestamp', 'children'), [Input('input-c', 'value')]
)(update_timestamp)

app.callback(
    Output('latest-value', 'children'),
    [Input('input-a-timestamp', 'children'),
     Input('input-b-timestamp', 'children'),
     Input('input-c-timestamp', 'children')],
    [State('input-a', 'value'),
     State('input-b', 'value'),
     State('input-c', 'value')]
)(choose_latest)

app.run_server()
1 Like

Thanks Mccalluc, Great idea.
I implemented your method and I thought id show a simplified example of it.

Please note all the potential problems Mccalluc pointed out.

You can add as many html/dcc components as you want, following the same pattern with the timekeeper divs.

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import time

app = dash.Dash()

app.layout = html.Div([

    # hidden div for holding timestamp of any DCC or html component
    html.Div(id='dcc-timekeeper', children='0', style={'display': 'none'}),

    # Any DCC or html item you want to use for ordered trigger
    dcc.Dropdown(id='dcc-item',
                 options=[
                     {'label': 'Chriddyp', 'value': 'Hr'},
                     {'label': 'Please notice me', 'value': 'Day'}
                 ],
                 value='Hr'
                 ),

    # 3 buttons to compare to for ease (these can use n_clicks_timestamp like in previous examples)
    html.Button(id='btn1',
                children='btn1',
                n_clicks_timestamp='0'),

    html.Button(id='btn2',
                children='btn2',
                n_clicks_timestamp='0'),

    html.Button(id='btn3',
                children='btn3',
                n_clicks_timestamp='0'),

    html.Div(id='results')

])


# use your DCC or html item to trigger a callback to update the dcc-timekeeper div
@app.callback(Output('dcc-timekeeper', 'children'),
              [Input('dcc-item', 'value')])
def update_timebase_nclick(value):
    return '{}'.format(int(time.time() * 1000))


# use multiple inputs and figure out which one was the trigger as such:
@app.callback(Output('results', 'children'),
              [Input('btn1', 'n_clicks_timestamp'),
               Input('btn2', 'n_clicks_timestamp'),
               Input('btn3', 'n_clicks_timestamp'),
               Input('dcc-timekeeper', 'children'),
               Input('dcc-item', 'value')])
def slider_buttons(btn1, btn2, btn3, btn4, dccvalue):
    if int(btn1) > int(btn2) and int(btn1) > int(btn3) and int(btn1) > int(btn4):
        val = 'btn1'
    elif int(btn2) > int(btn1) and int(btn2) > int(btn3) and int(btn2) > int(btn4):
        val = 'btn2'
    elif int(btn3) > int(btn1) and int(btn3) > int(btn2) and int(btn3) > int(btn4):
        val = 'btn3'
    elif int(btn4) > int(btn1) and int(btn4) > int(btn2) and int(btn4) > int(btn3):
        val = 'Dcc item'
    return '{}'.format(val)


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