Closed wuyuanyi135 closed 1 year ago
Following #243, the workaround involves one more step: To make the blueprint work, one should either:
bp.register_callbacks
before the line assigning app.layout
orapp.layout
to a function for deferred evaluation.I guess the client-side layout was not properly generated and was cached. Also, after bp.register_callbacks
the layout did not trigger a layout refresh, leading to transformed callbacks but the dcc.Store
was not injected to the client-side layout.
Same question here: is this the correct way to make a reusable blueprint component?
It seems you are hitting a combination of issues here,
1) The one you reported #243 , i.e. that you must use bp._layout_value()
instead of the layout
property to embed the layout
2) Callbacks must be registered before the blueprint is embedded
As noted in #243, I believe this can be fixed by adding a syntactic alias (+ update of docs accordingly). I guess (2) could be addressed by making it clear in the docs that callback registration must happen prior to embedding. But it would of course be more elegant, if that was not needed - I'll take a look at that :)
Hence, for now, I guess the following code should work,
import dash_mantine_components as dmc
from dash_extensions.enrich import DashBlueprint, Input, Output, ALL, callback_context, no_update, DashProxy, html, \
MultiplexerTransform
from dash_iconify import DashIconify
devices = []
bp = DashBlueprint(transforms=[MultiplexerTransform()])
bp.layout = dmc.Stack(
[
dmc.Button('Test', id='scan-device-btn', variant='outline'),
dmc.Card(
[
dmc.List(
children=None,
id="scanned-device-list"
),
],
withBorder=True
)
]
)
def make_list_item(name: str, idx: int):
return dmc.ListItem(
[
dmc.Group(
[
dmc.Text(name, size="lg"),
(
dmc.ActionIcon(
DashIconify(icon="bi:plus-circle"),
id={"type": "add-device", "index": idx},
variant="subtle"
)
)
]
)
],
icon=DashIconify(icon="bi:wifi", height=24),
)
def make_return_list():
return [
make_list_item(r, i)
for i, r in enumerate(devices)
]
@bp.callback(
Output('scanned-device-list', 'children'),
Input({'type': 'add-device', 'index': ALL}, 'n_clicks'),
prevent_initial_call=True,
)
def add_cb(nc1):
idx = callback_context.triggered_id
el = next(filter(lambda x: x == idx, callback_context.triggered_prop_ids.values()))
if el is None:
return no_update
return make_return_list()
@bp.callback(
Output('scanned-device-list', 'children'),
Input('scan-device-btn', 'n_clicks'),
prevent_initial_call=True,
)
def scan_cb(nc):
if nc is None:
return no_update
devices.append("Test Device Name")
return make_return_list()
app = DashProxy(__name__, suppress_callback_exceptions=True)
bp.register_callbacks(app)
app.layout = html.Div([
html.H2("Test Embedding"),
bp._layout_value()
])
app.run_server(debug=True)
Thanks for the summary @emilhe! Yes, with these two problems addressed, the example works as intended.
The following code requires
dash_mantine_components
anddash_iconify to execute
. I used a blueprint to make a layout that add a new item when the button is clicked. However, when added to the parent app, clicking the button won't even fire a request so the callback will not be fired. Ifadd_cb
was commented out, the callback works fine.Is there any undocumented conflict between blueprint embedding and MultiplexerTransform?