Enabling tabs to hold its state with button clicks

Problem Statement :

I have a problem that I wanted some ideas to solve. I have built a dashboard to process some factory data I get in my work.

My layout is such that I have a button, on whose clicks a new html.Div gets created… this Div has two two tabs, each of which has a graph inside (one tab is for box plot, one tab is for histogram). This works great as it is on the default button state.

My layout is like this.
→ Button (has n_clicks)
—-> html.Div
--------> Tab-1 (plots box plot)
--------> Tab-2 (plots histogram)

However, my problem starts with the state of these graphs.

My default value of tab is box plot (tab-1).

Current state (n_click = 0):
Let’s say, my dashboard is currently displaying histogram (tab-2).

Next state (n_clicks = 1):
A new html.Div gets created and shows the two tabs, and displays box plot by default.

However, with this button click, the state of the plot in the first div also changes. So the plot in the first div is now automatically changed to box since that is the default.

So, basically no matter in which Div I click the tab, it is affecting all Divs.

I understand there is no n_clicks option for tabs but I still need to retain the tab state somehow of each Div, corresponding to the button clicks. How do I get around this problem ?

Sample Code :

This is the part where I define the Div, tabs and the graph… I have included the commented out code that tried hoping it would work, basically it is adding n_clicks to the dcc.Tabs section… but I get the following error when I enable it - “A nonexistent object was used in an Input of a Dash callback”.

@app.callback(
Output('tabs_content', 'children'),
[Input('add_chart', 'n_clicks')],
[State('tabs_content', 'children')]
)
def display_graphs(n_clicks, div_children):
    new_section = html.Div(
        children = [ 
            html.Label(['Select Config'], style={'color': 'black', 'font-weight': 'bold', "text-align": "left", 'paddingleft':"10px"}), 
            dcc.Dropdown( id={'type': 'DUT_config', 'index': n_clicks}, options=[{'label': str(x), 'value': x} for x in sorted(df['Config'].unique())], value='NA', multi=True, disabled=False, clearable=True, searchable=True, placeholder='Choose Config...', style={'width':"99%", 'padding-left':"10px"}, persistence='string',persistence_type='session'), 

            html.Br(), 

            # html.Div(id={'type': 'Div-0', 'index': n_clicks}), 
            # children = [ 
            # dcc.Tabs(id="tabs_inline", value = 'tab-1', 
            # children = [ # dcc.Tab(label = 'Box Plot', value = 'tab-1', selected_style = selected_tab_style, style = tab_style,), 
            # dcc.Tab(label = 'Histogram', value = 'tab-2', selected_style = selected_tab_style, style = tab_style,) 
            # ]), 
            # ]), 

            dcc.Tabs(id="tabs_inline", value = 'tab-1', colors = {"border":'blue', "primary":'gold', "background": 'green'}, 
            # id={'type': 'tabs-inline', 'index': n_clicks}, value = 'tab-1', 
            children = [ 
            dcc.Tab(label = 'Box Plot', value = 'tab-1', selected_style = selected_tab_style, style = tab_style,), 
            dcc.Tab(label = 'Histogram', value = 'tab-2', selected_style = selected_tab_style, style = tab_style,) ]), 
            
            dcc.Graph(id={'type': 'data_graph', 'index': n_clicks}), 
            
            html.Div(id={'type': 'Div-1', 'index': n_clicks}) 
        ]) 

    div_children.append(new_section) 
    
    return div_children

This is where I then define the plot types… I deleted a bit of irrelevant code for this problem that does the calculations for the graph based on dataframe

@app.callback(
    [Output({'type': 'data_graph', 'index': MATCH}, 'figure'),
    Output({'type': 'Div-1', 'index': MATCH}, 'children')],
    [
     # Input({'type': 'tabs_inline', 'index': MATCH}, 'value')
     Input('tabs_inline', 'value')
     # Input({'type': 'Div-0', 'index': MATCH}, 'children')],
     # [State('tabs_inline', 'value')
     ])
def build_graph( tab_sel):

    if tab_sel == 'tab-1':
        fig = px.box(df_testcondition, y="Measured", color="Channel", facet_col="Config", points='all', hover_data=["SN"])

        fig.update_layout(
                        title = {'text':f'<b>{modulation_rb_allocation_sel}, {lte_band_sel}, {bandwidth_sel} - {testitem_sel}, {rb_sel}</b>',
                                'x':0.5, 'xanchor': 'center', 'yanchor': 'bottom'}
                                )


    elif tab_sel == 'tab-2':
        fig = px.histogram(df_testcondition, x="Measured", color="Channel", facet_col="Config")

        fig.update_layout(
                        title = {'text':f'<b>{modulation_rb_allocation_sel}, {lte_band_sel}, {bandwidth_sel} - {testitem_sel}, {rb_sel}</b>',
                                'x':0.5, 'xanchor': 'center', 'yanchor': 'bottom'}
                                )

    return [fig,
            dash_table.DataTable(data=data, columns=columns, merge_duplicate_headers=True, filter_action="native", sort_action="native", sort_mode="multi",
                                style_header = {'fontWeight': 'bold', 'backgroundColor': 'rgb(230, 230, 230)', 'border': '1px solid black'},
                                style_data={'border': '1px solid black'},
                                style_cell={'textAlign': 'center'})]

Is there anything we can do to get the community’s attention to this problem ? @adamschroeder

If you could post a (runnable) MWE demonstrating the problem, it would be a lot easier for people to debug the issue.

1 Like
    if n_clicks is None:
        raise PreventUpdate
    else:
        return your.html()

It should also be in the documentation.

1 Like

Hello @vinay.gh2690

I recommend you follow this guidelines, will help to get a answer.

  1. How to write a Minimal Reproducible Example (MRE)
  2. How to Get your Questions Answered on the Plotly Forum