It seems to me that it is currently not possible to properly update components that were created dynamically using set_props.
I don’t have a problem updating a statically added component using either Output (Case 1 in the MRE) or set_props (Case 2 in the MRE).
I don’t have a problem to update the component via set_props when it was dynamically added to the layout using the standard Output of a callback (Case 3 in the MRE).
However, adding the component using set_props seems to miss registering the component in the tree/layout in such a way, that it can later be discovered by an another call to set_props (Case 4 in the MRE).
Is this supposed to be like this? Any ideas?
Dash version is v3.2.0, but I also tried v2.18.2
Error message is: ID running component not found in layout. Component defined in running keyword not found in layout. Component id: …
from dash import Dash, Input, Output, callback, html, set_props
from dash.exceptions import PreventUpdate
# using suppress_callback_exceptions=True would suppress the error in case 4, but the update won't work
app = Dash(suppress_callback_exceptions=False)
app.layout = html.Div(
[
html.Div(
[
html.H2("Case 1 - Layout: static & Update: output"),
html.Div("The result element is created statically during initial layout. Update is done via an Output element of a callback."),
html.Button("Create", id="static-output-create-button"),
html.Button("Update", id="static-output-update-button", disabled=True),
html.Div(id="static-output-container", children=[
html.H3(id="static-output-result", children="Clicks: ?")
])
],
),
html.Div(
[
html.H2("Case 2 - Layout: static & Update: set_props"),
html.Div("The result element is created statically during initial layout. Update is done via a call to set_props."),
html.Button("Create", id="static-setprops-create-button"),
html.Button("Update", id="static-setprops-update-button", disabled=True),
html.Div(id="static-setprops-container", children=[
html.H3(id="static-setprops-result", children="Clicks: ?")
])
],
),
html.Div(
[
html.H2("Case 3 - Layout: dynamic via output & Update: set_props"),
html.Div("The result element is created dynamically via an Output element of a callback. Update is done via a call to set_props."),
html.Button("Create", id="output-setprops-create-button"),
html.Button("Update", id="output-setprops-update-button", disabled=True),
html.Div(id="output-setprops-container")
],
),
html.Div(
[
html.H2("Case 4 - Layout: dynamic via set_props & Update: set_props"),
html.Div("The result element is created dynamically via a call to set_props. Update is done via a call to set_props."),
html.Div("The update does not work in this case."),
html.Button("Create", id="setprops-setprops-create-button"),
html.Button("Update", id="setprops-setprops-update-button", disabled=True),
html.Div(id="setprops-setprops-container")
],
),
]
)
# Case 1 - Layout: static & Update: output
@callback(
Output("static-output-update-button", "disabled"),
Output("static-output-result", "children"),
Input("static-output-create-button", "n_clicks"),
)
def static_output_create_title(n_clicks: int) -> tuple[bool, str]:
if n_clicks is None:
raise PreventUpdate()
return False, "Clicks: 0"
@callback(
Output("static-output-result", "children", allow_duplicate=True),
Input("static-output-update-button", "n_clicks"),
prevent_initial_call=True,
)
def static_output_update_title(n_clicks: int) -> str:
if n_clicks is None:
raise PreventUpdate()
return f"Clicks: {n_clicks}"
# Case 2 - Layout: static & Update: set_props
@callback(
Output("static-setprops-update-button", "disabled"),
Input("static-setprops-create-button", "n_clicks"),
)
def static_setprops_create_title(n_clicks: int) -> bool:
if n_clicks is None:
raise PreventUpdate()
set_props(component_id="static-setprops-result", props={"children": "Clicks: 0"})
return False
@callback(
Input("static-setprops-update-button", "n_clicks"),
)
def static_setprops_update_title(n_clicks: int) -> None:
if n_clicks is None:
raise PreventUpdate()
set_props(component_id="static-setprops-result", props={"children": f"Clicks: {n_clicks}"})
# Case 3 - Layout: dynamic via output & Update: set_props
@callback(
Output("output-setprops-update-button", "disabled"),
Output("output-setprops-container", "children"),
Input("output-setprops-create-button", "n_clicks"),
)
def output_setprops_create_title(n_clicks: int) -> tuple[bool, html.H3]:
if n_clicks is None:
raise PreventUpdate()
return False, html.H3(id="output-setprops-result", children="Clicks: 0")
@callback(
Input("output-setprops-update-button", "n_clicks"),
)
def output_setprops_update_title(n_clicks: int) -> None:
if n_clicks is None:
raise PreventUpdate()
set_props(component_id="output-setprops-result", props={"children": f"Clicks: {n_clicks}"})
# Case 4 - Layout: dynamic via set_props & Update: set_props
@callback(
Output("setprops-setprops-update-button", "disabled"),
Input("setprops-setprops-create-button", "n_clicks"),
)
def setprops_setprops_create_title(n_clicks: int) -> bool:
if n_clicks is None:
raise PreventUpdate()
set_props("setprops-setprops-container", {"children": html.H3(id="setprops-setprops-result", children="Clicks: 0")})
return False
@callback(
Input("setprops-setprops-update-button", "n_clicks"),
)
def setprops_setprops_update_title(n_clicks: int) -> None:
if n_clicks is None:
raise PreventUpdate()
set_props(component_id="setprops-setprops-result", props={"children": f"Clicks: {n_clicks}"})
if __name__ == "__main__":
app.run(debug=True)