Change value of a dropdown without triggering any further chain callbacks


#1

Hi all,
new to Dash and trying to experiment with a few things.
I have a situation where on dropdown should be linked to a second dropdown, but the second one had a callback attached to it. I need to change the value of the second dropdown based on the value of the first. I would like the latter not to trigger the callback attached to the 2nd dropdown. This second callback should be triggered only if the user clicked and changed the value. Is there any way to ‘silently’ change the value in the first instance without triggering any events?

How can I change the value of the second dropdown programmatically but not trigger the callback attached to it?

Many thanks!

#1st callback in chain. Here I want to change the value but not trigger the next callback in chain
@app.callback(
dash.dependencies.Output(‘dropDownID2’, ‘value’),
[dash.dependencies.Input(‘dropDownID1’, ‘value’)])
def change_the_value_of_2nd_dropdown_somehow(value):
pass

#2nd callback
@app.callback(
dash.dependencies.Output(‘SomethingElse’, ‘children’),
[dash.dependencies.Input(‘dropDownID2’, ‘value’)])
def change_something_based_on_value_of_2nd_dropdown_somehow(value):
pass


#2

I guess the ‘value’ property of the second dropdown will be empty after its values have been updated, you could add a

from dash.exceptions import PreventUpdate

if len(value) == 0:
    raise PreventUpdate

#3

Hi jarjar - many thanks for your response.
Actually both dropdowns are populated from the beginning (with lists of key/value pairs).
When I choose something in DD1 I want (and I do that sucessfully) to change the shown value in the second DS, e.g. when I choose item X in DD1, then the value of the second DD2 is set to say iten 2. My problem is that this calls the second callback below, which I want to use only when the user clicks directly on DD2 and chooses a value there. Thoughts?


#4

I changed a bit my title to reflect better my problem. It is not the already answered in the forum “how I ignore one of the inputs innthe callback” :slight_smile: I guess coming from a QT environment I am looking to change a property of a conponent without firing the callback


#5

@jarjarbins has the right idea. You want to work out whether the user changed the value in the first callback, and if not, then raise PreventUpdate. Doing this will prevent any subsequent callbacks from being triggered.


#6

@nedned @jajarbins thanks both a lot. Yes, indeed I need to differentiate if the user changed the value on the second DD or another callback changed programmatically the value on that second DD. However, even if I try to decouple the DD’s callbacks with hidden buttons to keep the state and try to figure out which button was called (i.e. indirectly trying to figure out who changed the dropdown), essentially the Input is always Input(‘DD2’, ‘value’), so I don’t see any sensible way to differentiate the two routes here.


#7

Hello

to be sure, you want to differentiate when your callback 2nd is triged :

  • because you change the value of the first DD.
  • Or because you selected an item from your second DD.

for the first case, the length of the value property of your second DD will be 0 while in the second case, it will be (at least) 1.

so

app.callback(Output('foo_fighter', 'bar_ometer'))
    [Input(..., ...)]
def callback_for_second_DD(DD2_value)
    if len(DD2_value) == 0:
        print('trigged by app launch or by selecting an item in DD1)
        raise PreventUpdate
    print(trigged by choosing a value in DD2)
    ...

this should do it


#8

Hi @jajarbins , thanks for the response! It doesn’t seem to work though, I tried it. DD2_value is going to be 0/None only in initialisation.

DD2 is something like that:

dcc.Dropdown(id='DD2',  options=label_values, value=a_from_the_values)

So DD2 always going to have a value specified (except from initialisation I guess).

Then the first callback above does something likes this:

@app.callback(dash.dependencies.Output(‘DD2’, ‘value’), [dash.dependencies.Input(‘DD1’, ‘value’)])
 def change_the_value_of_2nd_dropdown_somehow(value):
     if value:
         #do something that will define the value of DD1, let's say:
         # dd1_value= 3 #to show the 3d option, assuming I initialised e.g. the values as [1,2.. etc]
         return dd1_value

this works find and changes the value shown in DD2. But for DD2 my case is that I need to have a ‘real’ callback, i.e. callback 2nd above, where if the user clicks on an option I need to change the children of another div, but this change shouldn’t happen if the DD2 value is just changed from the 1st callback, i.e.:

#2nd callback
@app.callback(
dash.dependencies.Output(‘SomethingElse_a_DIV’, ‘children’),
[dash.dependencies.Input(‘DD2’, ‘value’)])
def change_something_based_on_value_of_2nd_dropdown_somehow(value):
      if user_triggered_value_change_by_selecting_DD2():
           return [html.Div('dada')]
      else:
         raise PreventUpdate

I think you got the gist of my problem right but I am not sure how to implement this intermediate user_triggered_value_change_by_selecting_DD2() condition. What I have tried is to have two hidden button whose n_click_timestamp will change every time either the 1st or 2nd callabacks above are changed. But again essentially when the 1st cb is triggered it will change 1st button’s n_click_timestamp, but the it wll cause the 2nd button’s n_click_timestamp to change too as a chained event.


#9

the easiest way to solve this would be to remove the default value of DD2…

Also, I don’t find a way right now, but it should be possible to do what you want with adding a store component and storing the number of time the values of your DD2 have been chosed.
then add this dcc.Store component to your callback as a State and raise a preventUpdate for all your default value if they have been chosed only once (I guess the default value won’t be the same depending on DD1 choice).
something like this…

Also, I don’t know how you’ve put a hidden button (would you share your layout?) on top of a DD, but if your two buttons are “trigged” when choosing a value a DD1, you could consider adding this kind of if statement in your 2nd callback:

app.callback(output(...),
    [Input(DD2, value)],
    [State(btn1, n_click_timestamp),
    State(btn1, n_click_timestamp)])
def yolo(dd2_value, n_click_timestamp_btn1, n_click_timestamp_btn2)
    if abs(n_click_timestamp_btn1 - n_click_timestamp_btn2) < some_millisecond:
        raise PreventUpdate
    ....

#10

@jajarbins many thanks for the help.

For time being I bypassed this with decoupling the functionality of the two drop downs (i.e. I just don’t do it). Re the buttons approach, no I didn;t manage to have hidden btns over the DDs really, I just tried to have a hidden div somewhere and manually (programmatically) update the n_click_timestamp’s values but wasn’t successful either. That was towards the line you describe in your yolo func but I didn’t manage to make it work either.

In any case thanks for the time and help!