1

I am using Plotly Dash to create a dashboard. I want to have multiple tabs, and when the user clicks on any particular tab, for relevant callbacks to be triggered.

The main.py file calls a layout.py file to generate the layout. Here is a code snippet of how I'm going about it.

def create_layout(app):
    
    @callback(Output("tab-output", "children"), Input("tab-input", "value"))
    def update_tab(tab):
        if tab == "tabname1":
            return my_individual_script.render(app)  # might work, might not
        elif tab == "tabname2":
            return f"You selected tab {tab}."  # works properly
    
    # 1st alternative (succeeds ✅)
    layout = html.Div(children=[
        dcc.Tabs(id="tabs-input", value="tabname1",
                 children=[dcc.Tab(label="foo", value="tabname1"), dcc.Tab(label="bar", value="tabname2")]),
        my_individual_script.render(app)
    ])
    
    # 2nd alternative (fails ❌)
    layout = html.Div(children=[
        dcc.Tabs(id="tab-input", value="tabname1",
                 children=[dcc.Tab(label="foo", value="tabname1"), dcc.Tab(label="bar", value="tabname2")]),
        html.Div(id="tab-output")
    ])
    return layout

What I expect / what I have tried:

  1. I want update_tab to call the right separate scripts to render all the content for that tab. Hence the use of my_individual_script.
  2. The 1st alternative which succeeds 'statically' calls the render function from my_individual_script with its own callback.
  3. The 2nd alternative which fails only calls my_individual_script IF the tab name is correct. Obviously, this is the desired behavior because I want content to change based on the tab selection. Here, I place an html.Div with the ID tab-output to trigger update_tab. I think it should work because I get the expected behavior from tabname2.
  4. I have tried supplying Input("tab-output", "children") to my_individual_script's callback based on my tenuous understanding of chained callbacks, but I'm not sure if that's applicable here, considering that the callback is triggered based on if conditions in the parent callback. It also doesn't work.

Here is code in my_individual_script.py with its callback that fails to trigger in the 2nd alternative:

def render(app):
    @callback(Output("output-div", "children"), [Input("dropdown", "value")])
    def update_value(value):
        return f"You have selected {value}."

    div = html.Div(children=[
        dcc.Dropdown(["foo", "bar"], "foo", id="dropdown"),
        html.Br(),
        html.Div(id="output-div")
    ])
    return div

In this code, a dropdown menu with two options is initialized and all I want to do is print out what the user has selected in an html.Div below the dropdown. This is ultimately the div I want to show up when I write html.Div(id="tab-output") in the 2nd alternative.

The problem is that the callback in my_individual_script just isn't triggered. Why is that the case SPECIFICALLY when I try access its content through an html.Div with the parent callback's output ID?

Is it to do with my callback being dependent on another callback, so dash doesn't see it when it's initialized?

How can I allow for 'dynamic' loading of scripts based on the tab the user has clicked?


Apologies for any possible syntax errors here. I've had to transcribe this code from another computer, so any errors here do not correspond to syntax errors in the code I'm working with.

I am not getting any error messages so it's difficult to search for answers on StackOverFlow or elsewhere that correspond to my issue.

aarohan
  • 23
  • 4
  • I think defining a callback in the create_layout() function is problematic, why don't you put the callback definition in main.py ? Same for the render function, why do you register a callback on render, why not once in main.py or in a dedicated file callbacks.py ? – EricLavault Mar 13 '23 at 14:10
  • Thank you for your comment; I think it's solved my problem. As to your question, I primarily followed ArjanCodes' video guide for dash (see [here](https://youtu.be/GlRauKqI08Y?t=549)) as this was one of the first larger projects I was designing from the ground up. As for your separate callbacks.py suggestion, I followed it according to [this Stackoverflow answer](https://stackoverflow.com/a/68196559/12079010) and it worked like a charm (just in case anyone in the future has this problem) – aarohan Mar 13 '23 at 18:04

0 Answers0