Closed wuyuanyi135 closed 1 year ago
I have a workaround here. Instead of using DashBlueprint.layout
, the DashBlueprint._layout_value()
will invoke the transformation for the layout. This seems like an ugly hack and I am not sure if this should be used.
from dash_extensions.enrich import Output, DashProxy, Input, html, PrefixIdTransform, DashBlueprint
bp = DashBlueprint(transforms=[PrefixIdTransform(prefix="some_prefix")])
bp.layout = html.Div([html.Button("Click me", id="btn"), html.Div(id="log")])
@bp.callback(Output("log", "children"), Input("btn", "id"))
def func(btn_id): # argument is omitted from the function
return f"The button id is {btn_id}"
if __name__ == "__main__":
app = DashProxy(__name__)
app.layout = html.Div(
[
html.H1("Test"),
bp._layout_value()
]
)
bp.register_callbacks(app)
app.run_server(debug=True)
Ah, yes, this is a bug in the documentation. With the current code, the correct way to embed the layout is indeed,
bp._layout_value()
I guess what is needed here is a more proper syntax, i.e. an alias for _layout_value()
; maybe an embed
function or property? So the code would be,
bp.embed()
What do you think? Which syntax would you prefer?
Hi, @emilhe, thanks for the confirmation. Yes, I agree having a proper alias is helpful. Is it possible to make use of the layout
property instead of creating a new one? It seems like an easy mistake and it fails sliently, making it a bit hard to debug. Maybe raise a warning when transformation is not applied?
@wuyuanyi135 since the blueprint was designed to mirror Dash
object itself, the intention of the layout
property is to represent the unmodified layout, i.e. without any transforms applied. Hence, I would prefer to use a different property/function for the layout including modifications. What about a function that registers the callbacks and returns the resulting layout? I.e. your example would be,
from dash_extensions.enrich import Output, DashProxy, Input, html, PrefixIdTransform, DashBlueprint
bp = DashBlueprint(transforms=[PrefixIdTransform(prefix="some_prefix")])
bp.layout = html.Div([html.Button("Click me", id="btn"), html.Div(id="log")])
@bp.callback(Output("log", "children"), Input("btn", "id"))
def func(btn_id): # argument is omitted from the function
return f"The button id is {btn_id}"
if __name__ == "__main__":
app = DashProxy(__name__)
app.layout = html.Div(
[
html.H1("Test"),
bp.embed(app)
]
)
app.run_server(debug=True)
@emilhe I see. I agree having an embed
function handling both layout and callback registration will be very useful!
I have made the first draft of an implementation and pushed an rc version,
pip install dash-extensions==0.1.12rc1
which enables code like the example posted above. Could you test if it works for your case(s) as well?
@emilhe Sorry for the late response. I have tested 0.1.12rc1
and 0.1.12
. The proposed embed
works well when using eager-loaded layout: (app.layout = ....
). However, if lazy-loaded layout is used (app.layout=lambda : .....
) The embed
did not properly register the callbacks.
MWE with your example:
from dash_extensions.enrich import Output, DashProxy, Input, html, PrefixIdTransform, DashBlueprint
bp = DashBlueprint(transforms=[PrefixIdTransform(prefix="some_prefix")])
bp.layout = html.Div([html.Button("Click me", id="btn"), html.Div(id="log")])
@bp.callback(Output("log", "children"), Input("btn", "id"))
def func(btn_id): # argument is omitted from the function
return f"The button id is {btn_id}"
if __name__ == "__main__":
app = DashProxy(__name__)
app.layout = lambda: html.Div(
[
html.H1("Test"),
bp.embed(app)
]
)
app.run_server(debug=True)
For this case, the only way to make it work is to register the callback first then use _layout_value()
in the layout function.
Ah, yes, I didn't test for function layouts. I have made an initial implementation that targets this issue,
pip install dash-extensions==0.1.13rc1
I still need to do a bit more testing before merging the fix into master. Does it solve your issues?
@emilhe Thanks. For my simple tests, this version works fine now! Closing this issue.
Please see the code modified from the PrefixIdTransform and embedding. I want to use the transform to prevent id collision when using a
DashBlueprint
as a layout component. However it only transforms the callback, not the components, hence giving errorID not found in layout
.Is this an expected behavior?