VK / dash-lumino-components

This package integrates Lumino Widgets, the basis of JupyterLab, into Plotly's Dash.
MIT License
27 stars 1 forks source link

[Bug] Issues when using dash-lumino-components with multipage #37

Open JFKraasch opened 7 months ago

JFKraasch commented 7 months ago

I brought up the usage of the components in multipage dash apps before. A workaround was proposed, but I am still running into issues.

When interacting with the widgets an error appears and the widget no longer spans the entire height of the page. Also it is impossible to move back from page2 to page1

import dash_lumino_components as dlc
from dash import Dash
from dash import Input, Output, ALL, html

# common style
external_stylesheets = [
    'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css',
    'https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css'
]

menus = [
    dlc.Menu([
        dlc.Command(id={"type": "changePage", "url": "/page1"}, label="Page 1", icon="fa fa-1"),
        dlc.Command(id={"type": "changePage", "url": "/page2"}, label="Page 2", icon="fa fa-2"),
    ], id="openMenu", title="Widgets")]

# Create first Dash app
app = Dash(__name__, url_base_pathname='/page1/', external_stylesheets=external_stylesheets)
app.layout = html.Div([
    dlc.MenuBar(menus, 'menuBar'),
    dlc.BoxPanel([
        dlc.DockPanel([
            dlc.Widget(
                html.Div([
                    html.H1('This is our Home page'),
                    html.Div('This is our Home page content.'),
                ]),
                id="homeWidget1",
                title="Home Widget 1",
                closable=True
            ),
            dlc.Widget(
                html.Div([
                    html.H1('This is our Viz page'),
                    html.Div('This is our Viz page content.'),
                ]),
                id="vizWidget1",
                title="Viz Widget 1",
                closable=True
            ),
        ], id="dockPanel"),
    ],
        id="boxPanel", addToDom=True)
])

# Create second Dash app
app2 = Dash(__name__, server=app.server, url_base_pathname='/page2/', external_stylesheets=external_stylesheets)
app2.layout = html.Div([
    dlc.MenuBar(menus, 'menuBar'),
    html.H1('This is our Viz page'),
])

for a in [app, app2]:
    a.clientside_callback(
        """
        function(buttons) {

            ctx = window.dash_clientside.callback_context;

            try {
                // Extract the triggered input
                triggered_input = JSON.parse(ctx.triggered[0].prop_id.replace(".n_called", "")).url;

                console.log(triggered_input);
                window.location.href = triggered_input;
            } catch (error) {}

            return [];
        }
        """,
        Output('boxPanel', 'children'),
        Input({"type": "changePage", "url": ALL}, "n_called")
    )

# Run the Flask server
if __name__ == '__main__':
    app.run_server(debug=True)

Dashand15morepages-Work-MicrosoftEdge2024-02-1515-09-39-ezgif com-video-to-gif-converter

VK commented 7 months ago

Thank you for bringing the bug in the example to my attention. After checking the issue, it appears that the problem stems from the direct output to the boxPanel children in the callback. It's better to use

    a.clientside_callback(
        """
        function(buttons) {

            ctx = window.dash_clientside.callback_context;

            try {
                // Extract the triggered input
                triggered_input = JSON.parse(ctx.triggered[0].prop_id.replace(".n_called", "")).url;

                console.log(triggered_input);
                window.location.href = triggered_input;
            } catch (error) {}

            return window.dash_clientside.no_update;
        }
        """,
        Output('dummy', 'children'),
        Input({"type": "changePage", "url": ALL}, "n_called"),
        initial_callback=False
    )

I am not aware of a callback without output. To address this, I use a dummy element in the callback, which should also be present on the second page. This adjustment should help resolve the issue and ensure smoother functionality across pages.

JFKraasch commented 7 months ago

That makes sense. One thing I was wondering is if there is a way of incorporating the components in a form like: https://community.plotly.com/t/multipage-persistence/10682/2

Since I would like to be able to keep some persistence between pages, e.g. my end goal is having a data uploading page as a home page and then opening a Dashboard with lumino components, and users being able to go back to the data loading and that not resetting either the data loaded or the dashboard