Problem with updating graph in different tabs

I tried to use dcc.Tabs to show two graphs. Each graph is based on a unique dataframe. The default tab loaded fine, but when I clicked the other tab I got an error: “Cannot read properties of null (reading ‘layout’)”

Here is the code to reproduce the problem:

import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output, State
import plotly.express as px
import pandas as pd

app = dash.Dash(__name__)
server = app.server

data_grant = {'x':  ["Grant 1", "Grant 2", "Grant 3"],
        'y': [2, 4, 6]}

data_pub = {'x':  ["Publication 1", "Publication 2", "Publication 3"],
        'y': [7, 2, 17]}

app.layout = html.Div([
    html.H1(
        children='test',
        style={
            'textAlign': 'center'
        }
        ),
    
    dcc.Tabs(id="tabs", value='publication', children=[
        dcc.Tab(label='Publication', value='publication', children=[
        html.H3(children='Publication'),
        dcc.Graph(id='graph-pub')
        ]
    ),
        dcc.Tab(label='Grant', value='grant', children=[
        html.H3(children='Grant'),
        dcc.Graph(id='graph-grant')
        ]
    )]
  )
])

@app.callback(
    Output('graph-grant', 'figure'),
    Output('graph-pub', 'figure'),
    Input('tabs', 'value'))

def update_fig(tabs):
    if tabs=='publication':
        fig = px.bar(data_pub, x="x", y="y", title="Publication Results")
        return None, fig
    if tabs=='grant':
        fig = px.bar(data_grant, x="x", y="y", title="Grant Results")
        return fig, None

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

So then I used the Method 1 from here: Tabs | Dash for Python Documentation | Plotly
The problem was solved.

Here is the revised code:

app.layout = html.Div([
    html.H1(
        children='test',
        style={
            'textAlign': 'center'
        }
        ),
    
    dcc.Tabs(id="tabs", value='publication', children=[
        dcc.Tab(label='Publication', value='publication', children=[
        html.H3(children='Publication')
        ]
    ),
        dcc.Tab(label='Grant', value='grant', children=[
        html.H3(children='Grant')
        ]
    )]
  ),
  html.Div(id='graph-output')
])

@app.callback(
    Output('graph-output', 'children'),
    Input('tabs', 'value'))

def update_fig(tabs):
    if tabs=='publication':
        fig = px.bar(data_pub, x="x", y="y", title="Publication Results")
        return html.Div([
        dcc.Graph(figure=fig)
      ]
    )
        
    elif tabs=='grant':
        fig = px.bar(data_grant, x="x", y="y", title="Grant Results")
        return html.Div([
        dcc.Graph(figure=fig)
      ]
    )

Well, whereas the problem seems to be solved, I genuinely want to know what the problem is with the code in my OP. It seems it’s more intuitive coming from coding with just one tab: Set up dcc.Graph in app.layout, then establish the relationship between callback returns and dcc.Graph. Am I missing something in my logic here? Any insights would be greatly appreciated.

Hi,

The error is not super explanatory in this case, but the problem is simple. You can’t pass a None as a “figure” prop. Since you are doing it for the graph not rendered in the view, then the error is raised just when you load the second tab.

The simple alternative is to replace None by dash.no_update or simply split the callback in two. Your approach in the second post is also fine, it all depends if you want the two tabs defined in the layout all the time or not.

Hope this helps! :slight_smile:

1 Like