如何在 Dash 中更新侧边栏布局?

Posted

技术标签:

【中文标题】如何在 Dash 中更新侧边栏布局?【英文标题】:How to update sidebar layout in Dash? 【发布时间】:2021-12-24 20:28:47 【问题描述】:

我根据示例制作了一个简单的侧边栏 https://dash-bootstrap-components.opensource.faculty.ai/examples/simple-sidebar/

现在我需要在用户更改页面时更改导航栏内容。我想让它动态和分层。

这意味着我应该在页面更改时更改侧边栏布局。加载普通页面内容时这很容易,但侧边栏组件不会因页面而异。

因此我认为关键问题是如何在用户点击新页面时刷新app.layout,并且脚本分别在下面的link_list中修改(modify_test)?

这是侧边栏代码的基本部分(!不工作!):

link_list = [
            dbc.NavLink("Home", href="/", active="exact"),
            dbc.NavLink("Page 1", href="/page-1", active="exact"),
            dbc.NavLink("Page 2", href="/page-2", active="exact"),
            ]

def modify_test ():
    link_list.append(dbc.NavLink("Page 3", href="/page-3", active="exact"))

sidebar = html.Div(
[
    html.H2("Sidebar", className="display-4"),
    html.Hr(),
    html.P(
        "A simple sidebar layout with navigation links", className="lead"
    ),
    dbc.Nav(
        link_list,
        vertical=True,
        pills=True,
    ),
],
style=SIDEBAR_STYLE,
)

content = html.Div(id="page-content", style=CONTENT_STYLE)

app.layout = html.Div([dcc.Location(id="url"), sidebar, content])


@app.callback(Output("page-content", "children"), [Input("url", "pathname")])
def render_page_content(pathname):
if pathname == "/":
    return html.P("This is the content of the home page!")
elif pathname == "/page-1":
    modify_test ()
    return html.P("This is the content of page 1. Yay!")
elif pathname == "/page-2":
    return html.P("Oh cool, this is page 2!")
# If the user tries to reach a different page, return a 404 message
return dbc.Jumbotron(
    [
        html.H1("404: Not found", className="text-danger"),
        html.Hr(),
        html.P(f"The pathname pathname was not recognised..."),
    ]
)

【问题讨论】:

你能解释一下当页面改变时你想用什么方式改变侧边栏布局?在我看来,您只需在针对侧边栏的回调中添加一个额外的 Output 即可处理它。 @BasvanderLinden 我想让它分层,所以会有曲折和下拉列表。并且列表内容也会根据用户操作而变化。我是 Dash 和 Web 客户端的新手,所以我可能会遗漏一些明显的替代方案。例如。我不知道添加额外输出是什么意思;这应该有什么帮助? 【参考方案1】:

现在我需要在用户更改页面时更改导航栏内容。我想让它动态和分层。

这意味着我应该在页面更改时更改侧边栏布局。加载普通页面内容时这很容易,但侧边栏组件不会因页面而异。

因此我认为关键问题是如何在用户点击新页面时刷新app.layout,并且脚本分别在下面的link_list中修改(modify_test)?

我不知道你到底想做什么,但我认为没有必要刷新整个布局。回调允许我们定位和更新页面上的特定元素,而无需更新整个页面。

由于您已经有一个在页面更改时运行的回调,因此您可以向侧边栏添加一个 id 和一个 Output,通过其 id 定位您的 sidenav 并以某种方式更新 sidenav 的某些属性。

例如可以将您的回调更改为这样的内容

@app.callback(
    Output("sidebar", "style"),
    Output("page-content", "children"),
    Input("url", "pathname"),
)
def render_page_content(pathname):
    if pathname == "/":
        return "backgroundColor": "blue", html.P(
            "This is the content of the home page!"
        )
    elif pathname == "/page-1":
        return "backgroundColor": "green", html.P(
            "This is the content of page 1. Yay!"
        )
    elif pathname == "/page-2":
        return "backgroundColor": "purple", html.P("Oh cool, this is page 2!")
    # If the user tries to reach a different page, return a 404 message
    return "backgroundColor": "red", dbc.Jumbotron(
        [
            html.H1("404: Not found", className="text-danger"),
            html.Hr(),
            html.P(f"The pathname pathname was not recognised..."),
        ]
    )

当页面发生变化时,上面的回调会改变页面内容和侧边栏的样式。

这只是为了让您了解如何解决问题。如果您想在页面更改时更改侧边栏的内容,您可能希望定位到侧边栏的children 属性而不是style

完整版之前的例子

from dash import Dash
import dash_bootstrap_components as dbc
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Output, Input

app = Dash(__name__)

SIDEBAR_STYLE = 
    "position": "fixed",
    "top": 0,
    "left": 0,
    "bottom": 0,
    "width": "16rem",
    "padding": "2rem 1rem",
    "backgroundColor": "#f8f9fa",


CONTENT_STYLE = 
    "marginLeft": "18rem",
    "marginRight": "2rem",
    "padding": "2rem 1rem",


link_list = [
    dbc.NavLink("Home", href="/", active="exact"),
    dbc.NavLink("Page 1", href="/page-1", active="exact"),
    dbc.NavLink("Page 2", href="/page-2", active="exact"),
]

sidebar = html.Div(
    id="sidebar",
    children=[
        html.H2("Sidebar", className="display-4"),
        html.Hr(),
        html.P("A simple sidebar layout with navigation links", className="lead"),
        dbc.Nav(
            link_list,
            vertical=True,
            pills=True,
        ),
    ],
    style=SIDEBAR_STYLE,
)

content = html.Div(id="page-content", style=CONTENT_STYLE)

app.layout = html.Div([dcc.Location(id="url"), sidebar, content])


@app.callback(
    Output("sidebar", "style"),
    Output("page-content", "children"),
    Input("url", "pathname"),
)
def render_page_content(pathname):
    if pathname == "/":
        return "backgroundColor": "blue", html.P(
            "This is the content of the home page!"
        )
    elif pathname == "/page-1":
        return "backgroundColor": "green", html.P(
            "This is the content of page 1. Yay!"
        )
    elif pathname == "/page-2":
        return "backgroundColor": "purple", html.P("Oh cool, this is page 2!")
    # If the user tries to reach a different page, return a 404 message
    return "backgroundColor": "red", dbc.Jumbotron(
        [
            html.H1("404: Not found", className="text-danger"),
            html.Hr(),
            html.P(f"The pathname pathname was not recognised..."),
        ]
    )


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

【讨论】:

看来您已经从示例中删除了 modify_test() 功能。这就是相关侧边栏发生变化的地方:在侧边栏中添加一个新的选择项。没有那个,不幸的是这个例子对我没有用。 重要的部分是这个例子所展示的想法并没有像它到底做了什么。您可以采用此答案中显示的想法并将其应用于您自己的用例。在您的情况下,您可以添加一个 State 来跟踪您的导航项目,并在您的回调中返回一个新的项目列表:当前值列表(状态)附加一个选项。 感谢这个想法。在我对 Dash 事物的研究中还要学习一件事。同时,我绕过了这个问题,让 app.layout 根本没有侧边栏,但每个页面都会加载自己的修改版侧边栏。

以上是关于如何在 Dash 中更新侧边栏布局?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Bootstrap 4 创建固定的侧边栏布局?

响应式更新模块化 Shiny 应用程序中的侧边栏

如何将布局更新到 Python Dash 应用程序中

如何使用 codeigniter 制作基本布局(页眉、页脚、侧边栏)

R Shiny 在后台加载隐藏的侧边栏(使用 bs4Dash 包)

如何在我的项目的每个刀片中包含的侧边栏中注入数据