✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
📊 Dash 2.0 is Arriving. Register here.

📣 Dash Labs 0.3.0: Template System API Updates

Hi All,

We just published a new version of Dash Lab (0.3.0) to PyPI that contains a few API updates to the template system. Special thanks to @adamschroeder, @AnnMarieW, and @Emil for their feedback over in 📣 Dash Labs 0.2.0. Here is a summary of the discussion and changes.

The key observation was that using the words “input” and “output” to denote where components should be placed in a template was confusing, because of the overlap with the existing use of Input, State, and Output in callback dependency objects.

To clarify things we decided to make the following changes:

  • The _input and _output suffixes on the template component constructor methods have been removed, and a new_ prefix has been added. So tpl.button_input(...) is now tpl.new_button(...).
  • The term “role” has been replaced by “location”. And templates now define a set of descriptive location values where components can be positioned. So, when using the DbcSidebar template, the use of tpl.new_slider(..., role="input") has been replaced with tpl.new_slider(..., location="sidebar"). For the DbcRow template, this would now be tpl.new_slider(..., location="left"). Templates document all of their supported locations in their constructor docstring.

For more information, see the updated versions of chapters 3 and 4 in the documentation at dash-labs/docs at main · plotly/dash-labs · GitHub. Or, see the PR at Rename "role" to "location" throughout Dash Labs and rename component builder methods by jonmmease · Pull Request #25 · plotly/dash-labs · GitHub.

Thanks!
-Jon

4 Likes

These updates look good!

One further enhancement I would suggest is to change

@app.callback

decorator to

@tpl.callback

so that the templates collect the callback information and to add the template to the app at the end. Then you don’t need to create the app until the last step and components are easier to compose as submodules don’t depend on an “app” object being available.

Hi Jon, thanks a lot for all this work, super excited to see those changes happening.

I just tried out the component plugin system which looks fantastic to “componentise” blocks of code.

For practice, I’m creating a simple SynchronisedSliderPlugin that has a Slider and an Input that stay in sync. Now, I can get the thing to work using .install_callbacks(app) which gives this:

synchronised_slider_1

So far so good.

Now I’m trying to modify the layout of this component so in the plugin I write something like this:

template.add_component(
    dbc.Row([
        dbc.Col(_slider, sm=8, md=9),
        dbc.Col(_input, sm=4, md=3),
    ]),
    "top"
)

Without using install_callbacks on my component I can see that the layout was updated according to the layout:

image

However as soon as I call install_callbacks to actually start synchronising things I get a dash.exceptions.DuplicateIdError, looks like using components in a callback doesn’t check whether it already exists in the template and just adds it a second time?

And a few more questions:

  • Do component plugins have to be used with templates?
  • Could we consider something that would transpyle the function to js to do some simple callbacks like this one on the clientside?
  • Will flexible callbacks be available in clientside_callbacks for Dash 2.0?

Thanks a lot!

Hi @RenaudLN, thanks for giving the component plugin pattern a spin! I just added an example to the documentation of using a component plugin without a template: dash-labs/05-ComponentPlugingPattern.md at main · plotly/dash-labs · GitHub

import plotly.express as px
import dash_labs as dl
import dash_html_components as html
import dash

df = px.data.tips()

app = dash.Dash(__name__, plugins=[dl.plugins.FlexibleCallbacks()])

table_plugin = dl.component_plugins.DataTablePlugin(
    df=df,
    page_size=10,
    sort_mode="single",
    filterable=True,
    serverside=False,
)

table_plugin.install_callback(app)

app.layout = html.Div(children=
    table_plugin.args_components + table_plugin.output_components
)

if __name__ == "__main__":
    app.run_server(debug=True)

Does that help? If not, feel free to share you’re full code sample.

These are good questions about clientside callbacks. I think it could make sense for a component plugin to optionally define callback logic as a clientside callback. I’ll need to think about it a little more. So far, I haven’t dug into what it will take to support the enhanced callback features in clientside callback definitions. JavaScript doesn’t have named keyword arguments the like Python, so that part probably wouldn’t translate. But the tuple/“dict” grouping would.

Thanks!

Yeah thanks it helps heaps!

synchronised_slider_2

I ended up creating a new layout_component property to group the elements in a given layout, instantiated the plugin without a template, then added the object to the template with tpl.add_component and it works like a charm.

3 Likes