Black Lives Matter. Please consider donating to Black Girls Code today.

How to update/reload layout

I have app that imports layout from second module then renders it in tab as returning app.layout to this tab content. I have problem with thin gthat layout is imported with default values of inputs and when I have inputs filled with keys and change between tabs then when I click back to the inputs tab I have resetted values to default. I managed to solve this problem by declaring layout second time in a callback but it doesn’t look good and would require to write layout 2 times for further functions. There is my code:

if 'manager' not in vars():
    manager = Recognition('xx', 'xx')
    print('defining manager')

lcheck = 0
while lcheck == 0:
    layout = [
        html.Div([
            html.Div(
                [
                    html.H3("Primary API key"),
                    dcc.Input(
                        id='primary-key',
                        value=manager.api_key,
                        placeholder='Enter a value...',
                        type='text',
                        style = {"width":"50%"}
                    ),
                    html.H3("Private API key"),
                    dcc.Input(
                        id='private-key',
                        value=manager.api_secret,
                        placeholder='Enter a value...',
                        type='text',
                        style = {"width":"50%"}
                    ),

                    
                ],
            ),
            html.Button('Submit', id='button', className='btn btn-primary'),
            html.Div(id='output-hidden')
        ])
    ]
    lcheck=1


@app.callback(
    Output('primary-key', 'value'),
    [Input('button', 'n_clicks')],
    [State('primary-key', 'value'), 
     State('private-key', 'value')]
)
def update_output(n_clicks, value1, value2):
    values = {'value1':value1, 'value2':value2}
    global manager
    global layout
    manager.update(value1, value2)
    layout = [
        html.Div([
            html.Div(
                [
                    html.H3("Primary API key"),
                    dcc.Input(
                        id='primary-key',
                        value=manager.api_key,
                        placeholder='Enter a value...',
                        type='text',
                        style = {"width":"50%"}
                    ),
                    html.H3("Private API key"),
                    dcc.Input(
                        id='private-key',
                        value=manager.api_secret,
                        placeholder='Enter a value...',
                        type='text',
                        style = {"width":"50%"}
                    ),

                    
                ],
            ),
            html.Button('Submit', id='button', className='btn btn-primary'),
            html.Div(id='output-hidden')
        ])
    ]
    
    lcheck=0
    return values['value1']

As you can see it saves new layout everytime I send new data with button but this solution looks terrible I would like to find better solution without chaos in code.

Alright, because you CREATE the layout each time your tab changes, you will automatically lose the current data. What you could do is store the data OUTSIDE of the tab (dcc.Storage or json in hidden Div) when your user interacts with it. You can then include it in the callback as State. If “your_existing_data” exists, then change the initial value in your layout. That’d be one way to do it.

On my app, I use Tabs/Tab in 2 different ways.

Case 1:
The content of my webpage is OUTSIDE of the tabs. Its a big “html.Div” that updates based on the selected tab. So everytime you click on a tab, I completely recreate the layout of the page, and the previous data is not saved. That’s the example you see in the guide.

Case 2:
I use “normal” tabs, meaning the content is WITHIN the dcc.Tab element. Tab elements are kind of like a Div, if you click on a tab, you see its contents (without callbacks). That way, each Tab has its own layout that is loaded at the beginning. Changing tab does not impact its values (unless you have specific callbacks from one tab to another).
To give you an actual example:

  • In one tab I load a CSV
  • Which creates graphs and other dcc elements in the other tabs
  • I can then switch to any tab to view its content (without callbacks) because the layout is INSIDE the tab and clicking on a tab automatically shows its content (that’s included in the normal tab behaviour, not related to callbacks)

@Jordan Thanks for your reply. I am using this way of selecting tabs:

@app.callback(Output("tab_content", "children"), [Input("tabs", "value")])
def render_content(tab):
    if tab == "analysis_tab":
        return analysis.layout
    elif tab == "settings_tab":
        return settings.layout
    else:
        return analysis.layout

so probably i need to use hidden divs. Is dcc.Store the option? Where should be the hidden div placed then?

You can use dcc.Store component (https://dash.plot.ly/dash-core-components/store) OR a hidden html.Div element (https://dash.plot.ly/sharing-data-between-callbacks)

Either way, you’d need to store the data OUTSIDE of the div “where the tab content is displayed” and so that it is always available. Then you’d need additional callbacks to update data in your store/hidden-div component, and change your existing callbacks to use the stored-values (if not empty) when you generate your tab contents.

I haven’t done it yet. That’s how I would do it… but there might be complications I have not foreseen. As mentioned in my other post, I switch between the 2 types of “tab management” based on what I need.

@Jordan

@app.callback(
    Output('primary-key', 'value'),
    [Input('storage', 'data')],
    [State('storage', 'data')])
def update_values(values, value):
    if values:
        return value['value1'] 

This callback works! Thanks. Storage is id of dcc.Store. Also is it only possible to have single output for a callback?

Yes, the rules are:

  • A callback can have only ONE output
  • An element can only be used ONCE as output (you can’t have two callbacks using the same output)

For example, if you need to change the text of 3 different elements, you might need 3 callbacks. I’m currently looking into “loop over callbacks” for when I need to do the exact same action on several elements of the page. I’ve bookmarked topics of interests:

I’ll probably work on it in a few weeks