Full working example:
import json
import uuid
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
CANDIDATES = [
{'name': 'foobar0', 'category': 'Computers'},
{'name': 'foobar1', 'category': 'Computers/Programming'},
{'name': 'foobar2', 'category': 'Computers/Programming/Resources'},
{'name': 'foobar3', 'category': 'Computers/News_and_Media'},
{'name': 'foobar4', 'category': 'Computers/News_and_Media'},
]
CATEGORIES = {
'Computers': {
'parent': None,
'id': 0,
'children': {
'Computers/Programming': {
'parent': 0,
'id': 1,
'children': {
'Computers/Programming/Resources': {
'parent': 1,
'id': 2,
'children': [4]
}
}
},
'Computers/News_and_Media': {
'parent': 0,
'id': 3,
'children': {}
},
}
}
}
app = dash.Dash()
app.config['suppress_callback_exceptions'] = True
app.layout = html.Div([
# top controls
html.Div(
[
html.Div(
dcc.Dropdown(
id="root_category_dropdown",
options=[{'label': 'Computers', 'value': 'Computers'}]
),
className="two columns"
),
html.Div(
dcc.Dropdown(
id="sub_category_dropdown_1",
),
className="two columns",
style={'display': 'none'},
id="container_sub_category_dropdown_1"
),
html.Div(
dcc.Dropdown(
id="sub_category_dropdown_2",
),
className="two columns",
style={'display': 'none'},
id="container_sub_category_dropdown_2"
),
],
className="row",
style={"marginBottom": "10"},
),
# Index table
html.Div(
id="candidates_table",
className="row"
),
# Hidden divs to store shared data
html.Div(
id='selected_category',
style={'display': 'none'}),
html.Div(
id='available_candidates',
style={'display': 'none'}),
html.Div(
id='available_metrics',
style={'display': 'none'}),
# Style
html.Link(href="https://use.fontawesome.com/releases/v5.2.0/css/all.css",rel="stylesheet"),
html.Link(href="https://cdn.rawgit.com/plotly/dash-app-stylesheets/2d266c578d2a6e8850ebce48fdb52759b2aef506/stylesheet-oil-and-gas.css",rel="stylesheet"),
html.Link(href="https://fonts.googleapis.com/css?family=Dosis", rel="stylesheet"),
html.Link(href="https://fonts.googleapis.com/css?family=Open+Sans", rel="stylesheet"),
html.Link(href="https://fonts.googleapis.com/css?family=Ubuntu", rel="stylesheet"),
html.Link(href="https://cdn.rawgit.com/amadoukane96/8a8cfdac5d2cecad866952c52a70a50e/raw/cd5a9bf0b30856f4fc7e3812162c74bfc0ebe011/dash_crm.css", rel="stylesheet")
])
# Fill #1 options when root is picked
@app.callback(
Output('sub_category_dropdown_1', 'options'),
[Input('root_category_dropdown', 'value')])
def set_sub_category_dropdown_1_options(root):
if root is None:
return []
category = CATEGORIES[root]
options = [
{'label': child.split('/')[-1], 'value': child}
for child in category['children']
]
return options
@app.callback(
Output('sub_category_dropdown_1', 'value'),
[Input('root_category_dropdown', 'value')])
def empty_sub_category_dropdown_1_value(root):
return ''
# Fill #2 options when #1 is picked
@app.callback(
Output('sub_category_dropdown_2', 'options'),
[Input('root_category_dropdown', 'value'),
Input('sub_category_dropdown_1', 'value'),])
def set_sub_category_dropdown_2_options(root, sub_1):
if not sub_1 or not root:
return []
category = CATEGORIES[root]['children'][sub_1]
return [
{'label': child.split('/')[-1], 'value': child}
for child in category['children']
]
@app.callback(
Output('sub_category_dropdown_2', 'value'),
[Input('root_category_dropdown', 'value'),
Input('sub_category_dropdown_1', 'value'),])
def empty_sub_category_dropdown_2_value(root, sub_1):
return ''
# Show #1 when its options are filled
@app.callback(
Output('container_sub_category_dropdown_1', 'style'),
[Input('sub_category_dropdown_1', 'options')])
def toggle_container_sub_category_dropdown_1(options):
if not options:
return {'display': 'none'}
return {'display': 'block'}
# Show #2 when its options are filled
@app.callback(
Output('container_sub_category_dropdown_2', 'style'),
[Input('sub_category_dropdown_2', 'options')])
def toggle_container_sub_category_dropdown_2(options):
if not options:
return {'display': 'none'}
return {'display': 'block'}
# Set selected category when root is picked
@app.callback(
Output('selected_category', 'children'),
[Input('root_category_dropdown', 'value'),
Input('sub_category_dropdown_1', 'value'),
Input('sub_category_dropdown_2', 'value')])
def set_selected_category(root, sub_cat, sub_sub_cat):
if sub_sub_cat and sub_cat and root:
return sub_sub_cat
elif sub_cat and root:
return sub_cat
elif root:
return root
return 'Computers'
# Set candidates once selected category is set
@app.callback(
Output('available_candidates', 'children'),
[Input('selected_category', 'children')])
def set_available_candidates(selected_category):
if selected_category:
candidates = [c for c in CANDIDATES if c['category'].startswith(selected_category)]
return json.dumps(candidates)
return "{}"
def generate_callback_update_root_when_category_clicked():
def output_callback(_, state):
print("Dynamic callback called")
return state
return output_callback
def register_category_link(link_id):
app.callback(
Output(link_id, 'children'),
[Input(link_id, 'n_clicks')],
[State(link_id, 'children')])(
generate_callback_update_root_when_category_clicked()
)
@app.callback(
Output('static_link_id', 'children'),
[Input('static_link_id', 'n_clicks')],
[State('static_link_id', 'children')])
def test_callback(_, state):
print("Static callback called")
return state
@app.callback(
Output('candidates_table', 'children'),
[Input('available_candidates', 'children')])
def build_index(available_candidates):
candidates = json.loads(available_candidates)
rows = []
for candidate in candidates:
link_id = str(uuid.uuid1())
cells = [
html.Td(candidate['name']),
html.Td(html.Div(id=link_id, children=html.A(children=candidate['category']))),
html.Td(html.Div(id='static_link_id', children=html.A(children=candidate['category']))),
]
# Create dynamic callback
register_category_link(link_id)
rows.append(cells)
columns = ["Name", "Dynamic Callback Category", "Static Callback Category"]
return html.Table(
# Header
[html.Tr([html.Th(col) for col in columns])] +
# Body
[html.Tr(row) for row in rows]
)
if __name__ == '__main__':
app.run_server()
I have a three drop down that represent hierarchical categories and a list of candidates in a table that have the selected category as their category.
I would like the option to update the drop down values when I click on a candidate’s category in the table.
I set a unique dynamic ID to the anchors in my rows in my table and then create a dynamic callback for that anchor.
The issue is that the dynamic callback is never called, if you run the example above, you can click on both links in each row and notice that only the static callback prints in the console when clicked.
What am I missing?
Thanks