I’ve discovered an interesting interaction between pattern matching IDs and dynamically generated content that I haven’t seen discussed anywhere. It kind of seems like a bug (or at least unexpected behavior), and I’m hoping someone can explain what’s going on here.
I have a Callback which runs on page load and generates a set of dropdown menus (and labels for each):
@callback(
Output('filter_boxes_left', 'children'),
Output('filter_boxes_right', 'children'),
Input('all_dropdown_choices', 'modified_timestamp'),
State('all_dropdown_choices', 'data'),
State('talent_delta_dropdown_store', 'data'),
State('finance_value_store', 'data')
)
def create_dropdowns(ts, all_dimensions_dict, last_settings, last_finance):
filter_boxes = []
default_finance_value = 'r_o'
finance_filter = dcc.Dropdown(id={"type": "finance-filter-dropdown", "index": 'finance'},
options=finance_plans_dict,
value=default_finance_value,
multi=False,
className="dbc")
finance_label = html.Label('Finance Plan', style={'margin-right': '5em',
'width': '24%',
'color': 'white'})
filter_boxes.append(finance_label)
filter_boxes.append(finance_filter)
for key in all_dimensions_dict:
default_value = all_dimensions_dict[key][0]
single_filter = dcc.Dropdown(id={"type": "dimension-filter-dropdown", "index": key},
options=all_dimensions_dict[key],
value=default_value,
multi=True,
searchable=True,
className="dbc")
single_label = html.Label(f'{PRETTY_NAMES[key]}', style={'margin-right': '5em',
'width': '40%',
'color': 'white'})
filter_boxes.append(single_label)
filter_boxes.append(single_filter)
half = int(len(filter_boxes) / 2)
return filter_boxes[:half], filter_boxes[half:]
For functional reasons, the “finance” filter box is not part of the “dimensional” filter boxes. The interesting thing is when these filters are used in other callbacks. If I try to use the finance filter directly:
@callback(
<snip>
State({"type": "finance-filter-dropdown", "index": 'finance'}, 'value')
)
I get an error stating “A nonexistent object was used in a State
of a Dash callback.” I understand that this is because the finance filter is being created inside another callback, and thus doesn’t exist when the page loads, etc. This is NOT fixed by adding prevent_initial_call=True
to the second callback.
What’s interesting (and I would like to have explained) is that the error DOES go away if we generalize the second callback:
@callback(
<snip>
State({"type": "finance-filter-dropdown", "index": ALL}, 'value')
)
There’s only only object of type ‘finance-filter-dropdown’, one with an index of ‘finance’. Logically, {"type": "finance-filter-dropdown", "index": 'finance'} == {"type": "finance-filter-dropdown", "index": ALL}
, but the first version throws the nonexistent object error while the second does not. I haven’t seen this behavior discussed anywhere else.
Is this documented somewhere?
Why does it happen?
Is it intended to happen this way?
Thanks!
Edit: I should add that the nonexistent object error is also thrown if I don’t use a pattern matching callback at all for the finance filter. My first iteration was to just give it the id='finance-filter'
.