Black Lives Matter. Please consider donating to Black Girls Code today.
Dash HoloViews is now available! Check out the docs.

dcc.Graph: How to force figure's layout to refresh after the class of its container was updated (bootstrap + sidebar + responsive)

Hello,

I believe my issue is very similar to the one raised here : How to force dcc.Graph to resize?

That is, a button is used to show/hide a sidebar (right side of the page, if you run the below code). When this sidebar is displayed, it is 3 col wide, and the main content of my page is 9 col wide. When the sidebar is not displayed, the main content is 12 col wide. (The code uses more classes than col-3, col-9 and col-12 though, because of the 5 possible screen sizes xs/sm/md/lg/xl)

Problem is : in the main content, I have a figure whose layout is not refreshed when I click on this button. As a result, it keeps the “12 col width”, despite the container in which this figure is, is properly resized (see the div with the text “some content some content…”)

If I manually resize the window (browser), making it just 1px larger, the figure is immediately refreshed and perfectly fits the container. So, how could I force the figure to adjust immediately, when I click on the button?

I tried the workaround proposed in the above post, but either I do not implement it correctly, or it doesnt work.

Does anyone have a solution?

My code is:

import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
from dash.dependencies import Input, Output, State

PLOTLY_LOGO = "https://images.plot.ly/logo/new-branding/plotly-logomark.png"
FONT_AWESOME = "https://use.fontawesome.com/releases/v5.7.2/css/all.css"


app = dash.Dash(__name__, 
                external_stylesheets=[dbc.themes.BOOTSTRAP, FONT_AWESOME], 
                meta_tags=[
                        {'name':'viewport','content':'width=device-width, user-scalable=1'}
                        ]
                )

app.config['suppress_callback_exceptions']=True


logo = dbc.Navbar(
    dbc.Container(
        [   
            html.Button(
                    html.Span(className="fas fa-cog fa-lg"),
                    id="sidebar-toggler",
                    className="navbar-toggler",
                    n_clicks=0,
                    n_clicks_timestamp=0
            ),
                
            html.A(
                # Use row and col to control vertical alignment of logo / brand
                dbc.Row(
                    [
                        dbc.Col(html.Img(src=PLOTLY_LOGO, height="30px")),
                        dbc.Col(dbc.NavbarBrand("MyTitle", className="ml-2")),
                    ],
                    align="center",
                    no_gutters=True,
                ),

            ),
            dbc.NavbarToggler(id="navbar-toggler2"),

            dbc.Collapse(
                dbc.Nav(
                    [
                        dbc.NavItem(dbc.NavLink("Page1", href="/PageOne", id="LinkOne", n_clicks=0)),
                        dbc.NavItem(dbc.NavLink("Page2", href="/PageTwo", id="LinkTwo", n_clicks=0)),
                        dbc.NavItem(dbc.NavLink("Page3", href="/PageThree", id="LinkThree", n_clicks=0)),
                        dbc.NavItem(dbc.NavLink("Page4", href="/PageFour", id="LinkFour", n_clicks=0)),
                        dbc.NavItem(dbc.NavLink("Page5", href="/PageFive", id="LinkFive", n_clicks=0)),
                    ], 
                    className="ml-auto", 
                    navbar=True, 
                    pills=False,
                    horizontal="center",
                    style={"text-align":"right"}
                ),
                id="navbar-collapse2",
                navbar=True,
            ),
        ]
    ),
    color="dark",
    sticky="top",
    dark=True,
    className="mb-5",
)            


myContent=dbc.Container([
    dbc.Row([
        dbc.Col([
            html.Button(
                    #html.Span([html.I(className="fas fa-plus-circle ml-2")]),
                    
                    [html.Span(className="fas fa-cog fa-2x")],
                    id="buttonModel50",
                    className="navbar-toggler d-none d-md-block bg-dark btn-secondary",
                    style={"position":"fixed", "top":"50vh","right":"0","z-index":"1050", "padding-left":"4px", "padding-right":"4px"},
                    #data-activates="slide-out",
                    n_clicks=0,
                    n_clicks_timestamp=0
            ),                
        ])
    ]),
    dbc.Row([
        dbc.Col([
            

            html.Div([
                html.H2("some content")
            ], style={"position":"fixed", "top":"70px", "background-color":"azure"})
                
        #], className="col", syle={"-ms-flex":"0 0 230px", "flex":"0 0 230px", "background-color":"greenyellow"}),
        ], id="sidebarForTest", className="col-md-3 d-none d-md-block d-lg-block d-xl-block bg-dark", style={"height":"90vh", "position":"fixed", "top":"50px"}),
        
        dbc.Col([
            html.P("some content some content some content some content some content some content some content some content some content some content some content some content some content some content some content some content some content some content some content some content"),
            dcc.Graph(
                id='example-graph2',
                figure={
                    'data': [
                        {'x': [1, 2, 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60], 'y': [4,1,2,4,1,2,4,5,4,1,2,4,1,2,4,1,2,4,1,2,4,1,3,5,4,1,2,4,1,2,4,1,2,4,1,2,4,5,4,1,2,4,1,2,4,1,2,4,1,2,4,1,3,5,4,1,2,4,1,2], 'type': 'bar', 'name': 'SF'},
                        {'x': [1, 2, 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60], 'y': [2,4,5,4,1,2,4,1,2,4,1,2,4,1,22,4,5,4,1,2,4,1,2,5,4,1,2,4,1,2,4,1,2,4,1,2,4,5,4,1,2,4,1,2,4,1,2,4,1,2,4,1,3,5,4,1,2,4,1,2], 'type': 'bar', 'name': u'TownAN'},
                    ],


                    'layout':go.Layout(
                        title='Coverage - Summary',
                        margin=go.layout.Margin(l=0, r=20, t=35, b=10),
                        #height=400,
                        titlefont=dict(
                            size=11,
                            #color='white'
                            color='#212122'
                        ),
                     )       
                },
                style={"height":"65vh", "width":"100%"}
                
            ),              
            #some_other_content,
            
            html.Div([
            ], style={"height":"500px"})
            
        ], id="myMain", className="col-md-9")
    ]) #End of Row
], fluid=True) #End of Container        


app.layout = html.Div(

    [logo, myContent]

)    


@app.callback([
        Output('sidebarForTest', 'className'), Output('sidebarForTest', 'style'),
        Output('myMain', 'className')
        ], [
        Input('buttonModel50', 'n_clicks'), Input('buttonModel50', 'n_clicks_timestamp'), 
        Input('sidebar-toggler', 'n_clicks'), Input('sidebar-toggler', 'n_clicks_timestamp')
        ])
def update_stylesidebar(n_clicks_A, timestamp_A, n_clicks_B, timestamp_B):
    if n_clicks_A%2==0 and int(timestamp_A)>=int(timestamp_B):
        return 'd-none bg-dark', {"height":"90vh", "position":"fixed!important", "top":"56px"}, 'col-12' 
    elif n_clicks_A%2==1 and int(timestamp_A)>=int(timestamp_B):
        return 'col-sm-3 d-none d-md-block d-lg-block d-xl-block bg-dark', {"height":"90vh", "position":"fixed", "top":"56px"}, 'col-sm-9 offset-sm-3' 
    elif n_clicks_B%2==0 and int(timestamp_B)>int(timestamp_A):
        return 'd-none bg-dark', {"height":"90vh", "position":"fixed", "top":"56px","width":"250px", "z-index":"1050"}, 'col-md-12' 
    elif n_clicks_B%2==1 and int(timestamp_B)>int(timestamp_A):
        return 'd-block d-sm-block d-md-none bg-dark', {"height":"90vh", "position":"fixed", "top":"56px", "width":"250px", "z-index":"1050"}, 'col-md-12' 
    else:
        return              


@app.callback(
        Output(f"navbar-collapse2", "is_open"),
        [Input(f"navbar-toggler2", "n_clicks")],
        [State(f"navbar-collapse2", "is_open")],
    )
def toggle_navbar_collapse(n, is_open):
    if n:
        return not is_open
    return is_open

@app.callback(
        Output(f"sidebar-collapse", "is_open"),
        [Input(f"sidebar-toggler", "n_clicks")],
        [State(f"sidebar-collapse", "is_open")],
    )
def toggle_sidebar_collapse(n, is_open):
    if n:
        return not is_open
    return is_open


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

I tried this How to force dcc.Graph to resize?
this Plotly resize bug using html.Details / hidden Div and width=100%
checked this Is it possible to update just `layout`, not whole `figure` of Graph in callback?
found this https://github.com/plotly/plotly.js/issues/2769
But im still struggling with this layout which doesnt want to update the width of the figure correctly

I still have this issue and I dont know how to fix it. Everythime I change a className on the fly, Div are resized, but graph in these Div keeps the width of the previous class’ width :confused:

I just read about clientsideFunction 📣 Dash 0.41.0 released

Does it fix this issue? How to use it?

Solution here : Dynamic layout does not propagate resized graph dimensions until window is resized

1 Like