✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🧬 Learn how to build RNA-Seq data apps with Python & Dash. Register for the May 20 Webinar!

Problem creating Tabs dynamically using callback

Hi,
So my project has been using the unofficial Tabs branch for a while and using it, I created a dropdown and upon its selection different tabs would appear. This worked perfectly (code below) but then I decided to upgrade to the official version with the Tab component, and I’ve updated the code below (the init_tabs() method) to return a list of dcc.Tab(...) instead of a dictionary. However, this now doesn’t work anymore :frowning: :frowning: :(.

The callback triggers but the Tabs children don’t change anymore, unless I refresh the page or start off with a different default value for the dropdown. I tried various changes, like declaring the Tabs without children in the layout and only returning the children but that doesn’t work either.

Is this supposed to be like this now, or is there a way around it? Please help.

P.S The code below is an extract of a much larger multi page app so it might not really work…
P.S.2 Dash is one of the best things that I discovered on the internet ever.

layout = html.Div([
html.Div(dcc.Dropdown(id='app-3-dropdown', options=[{'label': 'Below Ground Assets', 'value': 'BG'}, {'label': 'Above Ground Assets', 'value': 'AG'}, {'label': 'Other Assets', 'value': 'OTH'}], value = 'BG', clearable=False), className='three columns'),
    html.Div([
        html.Div([html.Div(id='tt')], className = 'two columns'),
        html.Div([html.Div(id='tab-output')], 
            id='fixed-width', className = 'ten columns', style= {'height': '550px', 'overflow':'scroll'})
        ], id='tabs', className = 'row'))
])

def init_tabs(asset_type = 'bg'):
	if asset_type == 'bg':
		return [{'label': 'H&S Road', 'value': 1}, 
	                    {'label': 'Environmental', 'value': 2},
	                    {'label': 'Interruption', 'value': 3},
	                    {'label': 'Customer Satifaction', 'value': 4},
	                    {'label': 'Repair', 'value': 5}, 
	                    {'label': 'Flooding - Compensation', 'value': 6},
	                    {'label': 'Complex Re-Instatement Cost', 'value': 7}
	                    ]

	else:
		return [{'label': 'Reputational Cost', 'value': 8},
	                    {'label': 'Trafic Disruption', 'value': 9},
	                    {'label': 'Other Regulatory', 'value': 10}]

@app.callback(Output('tt', 'children'), [Input('app-3-dropdown', 'value')])
def display_tabs(value):
    print('hello')
    if value in assets.keys():
        print(value)
        return dcc.Tabs(
                id='tabs',
                children=init_tabs(value),
                vertical=True)

Thanks for reporting and thanks for the kind words :slight_smile:

the new version of Tabs works a little differently now. Instead of a list of dictionaries, you’ll need to pass in a list of dcc.Tab components. Check out the first example here: https://dash.plot.ly/dash-core-components/tabs

Yes, I did that already, but the behaviour is not the same. So now my init_tabs() function is a list of dcc.Tab(). When I have that, the tabs do not update when changing the dropdown value.

I was just wondering if the fact that the tabs can only be defined upfront and not able to be returned/updated by a callback function is intentional.

So this is my new init_tabs now, but it doesn’t work as before.

def init_tabs(asset_type = 'bg'):
	if asset_type == 'bg':
		return [Tab(label='H&S Road', value='hs'),
                            Tab(label='Environmental', value='env'),
                            Tab(label='Interruption', value='int'),
                            Tab(label='Customer Satifaction', value='cs'),
                            Tab(label='Repair', value='rep'),
                            Tab(label='Flooding - Compensation', value='fc'),
                            Tab(label='Trafic Disruption', value='td')]

Definitely not intentional! The children property of the Tabs component should be able to be updated dynamically but this might be a bug.

One thing I notice in your code is that you have two components with the id tabs: one in the parent in app.layout and another in your display_tabs callback. That could be part of the issue as well.

To update tabs dynamically, there would be two options for callbacks:

  1. Update the children property of some div with an entirely new dcc.Tabs instance. This is what it looks like you are doing in your code example and it should work.
  2. Update the children property of a dcc.Tabs component with a new list of dcc.Tab instances. This should also work but I don’t think that we have tested it.

If either of those methods don’t work, then it’s definitely a bug on our end.

@cristina Just a quick update on this one. We’re investigating those two options in https://github.com/plotly/dash-core-components/issues/265. It seems like (1) works but (2) doesn’t. You can track our progress in that issue.
Thanks again for reporting :smiley_cat:

2 Likes

Ah that’s great! I tested the second option too and it wasn’t working and prepared some response here, but got caught up into other (Dash related) work :sweat_smile:

1 Like

Hi, I am still hitting the same issue.

In my app, I want to create different tabs on the fly (on button click event) i.e. each tab will have its own “submit” button which in turn should create (append) a new tab. I define a new tab if the button is clicked. I tried both the options which you suggested:

  1. Update the children property of same div with an entirely new dcc.Tabs instance.
  2. Update the children property of a dcc.Tabs component with a new list of dcc.Tab instances.

Both of them didn’t work.