破折号多页“存储组件”值

FBr*_*esi 5 python plotly-dash

我正在尝试实现一个多页面 Dash Web 应用程序,但在跨页面来回切换时,我很难“存储”组件中的更改。也就是说,每次移动到新页面时,布局都会刷新。

下面有一个(最小?)用于重现性目的的代码示例,其中第 1 页有一个 html 表格,第一行允许用户输入和添加按钮,然后也可以删除插入的行。第2页只是一个用于来回切换的占位符。

我想一种策略可能是使用不同的dcc.Stores 并记录发生的情况,并在切换时从中填充页面。但我希望有更好的策略:)

索引.py

import dash
from dash import html, dcc
import dash_bootstrap_components as dbc

# Dash app
app = dash.Dash(
    __name__,
    external_stylesheets=[
        dbc.themes.FLATLY,
        dbc.icons.BOOTSTRAP,
    ],
    use_pages=True,
)

sidebar = dbc.Col(
    id="sidebar",
    children=[
        dbc.Nav(
            id="sidebar-nav",
            children=[
                dbc.NavLink(children="page1", id="page1", href="/page1", active="exact"),
                dbc.NavLink(children="page2", id="page2", href="/page2", active="exact")
            ],
        ),
    ],
)

main_content = dbc.Col(
    id="main-content",
    className="main-content-expanded",
    children=dash.page_container,
)

page = dbc.Row(
    id="page",
    children=[
        sidebar,
        main_content,
    ],
)

url = dcc.Location(id="url", refresh=False)

# App layout
app.layout = html.Div(
    id="layout",
    children=[page, url],
)

if __name__ == "__main__":
     app.run_server(
        debug=True,
        host=0.0.0.0,
        port=8080,
    )
Run Code Online (Sandbox Code Playgroud)

页面/page1.py

import dash
import dash_bootstrap_components as dbc
from dash import MATCH, Input, Output, Patch, State, callback, html, dcc
from dash.exceptions import PreventUpdate


dash.register_page(__name__, path="/page1", name="Page1")


def make_filled_row(button_clicked: int, name: str) -> html.Tr:
    """
    Creates a filled row in the table, with dynamic id using the button_clicked
    parameter.
    """
    return html.Tr(
        [
            html.Td(name),
            html.Td(
                html.Button(
                    "Delete",
                    id={"index": button_clicked, "type": "delete"},
                    className="delete-user-btn",
                )
            ),
        ],
        id={"index": button_clicked, "type": "table-row"},
    )


header = [
    html.Thead(
        html.Tr(
            [
                html.Th("First Name"),
                html.Th(""),
            ]
        ),
        id="table-header",
    )
]


add_row = html.Tr([
    html.Td(dcc.Input(id="name-input")),
    html.Td(html.Button("Add", id="add-user-btn",)),
])

body = [html.Tbody([add_row], id="table-body")]

table = html.Table(header + body)


# Layout of the page
layout = dbc.Container([
    dbc.Row([dbc.Col([table])]),
])

# List of callbacks for the page
@callback(
    Output("table-body", "children", allow_duplicate=True),
    Output("name-input", "value"),
    Input("add-user-btn", "n_clicks"),
    State("name-input", "value"),
    prevent_initial_call=True,
)
def on_add(n_clicks: int, name: str):
    """Adds a new row to the table, and clears the input fields"""
    table_body = Patch()
    table_body.append(make_filled_row(n_clicks, name))

    return table_body, ""


@callback(
    Output({"index": MATCH, "type": "table-row"}, "children"),
    Input({"index": MATCH, "type": "delete"}, "n_clicks"),
    prevent_initial_call=True,
)
def on_delete(n_clicks: int) -> html.Tr:
    """Deletes the row from the table"""
    if not n_clicks:
        raise PreventUpdate

    return html.Tr([])
Run Code Online (Sandbox Code Playgroud)

页面/page2.py

import dash
import dash_bootstrap_components as dbc
from dash import html
dash.register_page(__name__, path="/page2", name="Page2")

# Layout of the page
layout = dbc.Container([
    dbc.Row([dbc.Col(html.Div("You are on page 2"))]),
])
Run Code Online (Sandbox Code Playgroud)

小智 0

dcc.Store使用全局变量来存储页面数据是一个好方法:

索引.py

只需添加商店:

# App layout
app.layout = html.Div(
    id="layout",
    children=[
        # add this:
        dcc.Store(
            id='store',
            storage_type='local',
            data={},
        ),
        page,
        url,
    ],
)
Run Code Online (Sandbox Code Playgroud)

page1.py

一种存储数据的工作方法on_add

import dash
import dash_bootstrap_components as dbc
from dash import MATCH, Input, Output, Patch, State, callback, html, dcc
from dash.exceptions import PreventUpdate

dash.register_page(__name__, path="/page1", name="Page1")


def make_filled_row(button_clicked: int, name: str) -> html.Tr:
    """
    Creates a filled row in the table, with dynamic id using the button_clicked
    parameter.
    """
    if not button_clicked:
        raise PreventUpdate
    return html.Tr(
        [
            html.Td(name),
            html.Td(
                html.Button(
                    "Delete",
                    id={"index": button_clicked, "type": "delete"},
                    className="delete-user-btn",
                )
            ),
        ],
        id={"index": button_clicked, "type": "table-row"},
    )


header = [
    html.Thead(
        html.Tr(
            [
                html.Th("First Name"),
                html.Th(""),
            ]
        ),
        id="table-header",
    )
]

add_row = html.Tr([
    html.Td(dcc.Input(id="name-input")),
    html.Td(html.Button("Add", id="add-user-btn", )),
])

body = [html.Tbody([add_row], id="table-body")]

table = html.Table(header + body)

# Layout of the page
layout = dbc.Container([
    dbc.Row([dbc.Col([table])]),
])


# List of callbacks for the page
@callback(
    # Output("table-body", "children", allow_duplicate=True),
    Output("store", "data"),
    Output("name-input", "value"),
    Input("add-user-btn", "n_clicks"),
    State("name-input", "value"),
    State("store", "data"),
    prevent_initial_call=True,
)
def on_add(n_clicks: int, name: str, store: dict):
    """Adds a new row to the store, and clears the input fields"""

    if not n_clicks:
        raise PreventUpdate

    store = {
        'data': [*store.get('data', []), name]
    }

    return store, ""


@callback(
    Output({"index": MATCH, "type": "table-row"}, "children"),
    Input({"index": MATCH, "type": "delete"}, "n_clicks"),
    prevent_initial_call=True,
)
def on_delete(n_clicks: int) -> html.Tr:
    """Deletes the row from the table"""
    if not n_clicks:
        raise PreventUpdate

    return html.Tr([])


@callback(
    Output("table-body", "children"),
    Input('store', 'modified_timestamp'),
    State('store', 'data'),
)
def on_data(ts, store):
    if ts is None:
        raise PreventUpdate

    table_body = Patch()
    table_body.clear()
    table_body.append(html.Tr([
        html.Td(dcc.Input(id="name-input")),
        html.Td(html.Button("Add", id="add-user-btn", )),
    ]))

    for i, name in enumerate(store.get("data", [])):
        table_body.append(make_filled_row(i + 1, name))

    return table_body

Run Code Online (Sandbox Code Playgroud)

on_delete当回调触发时,您可以使用相同的方法从商店中删除项目。