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)