5

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 thing that 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 the values reset to default 'x'. 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 functionalities. 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. Whole thing is to update value=manager.api_key, and value=manager.api_secret, in layout variable. I was thinking to get access of the values like from normal list but it is not possible since it is not nested as regular list. Is there any way to access the values for example layout.getid("primary-key").value or something like this? How to clean this code?

1 Answer 1

7

I have figured out how to solve this problem. As my app us rendering app.layout everytime I select a tab, so it always render the layout that was assigned during first import. Firstly I wanted to override layout everytime in a callback function but it has no sense to write layout= 2 times. So I found in dash docs a dash core component named dcc.Store (it stores json in hidden div). I used it like this:

html.Div([
        dcc.Store(id='storage')
    ],
            id="hiddendata",
            style={"display": "none"},
    ),

This div is placed directly after 'Tabs' div and is there as a 'cache' memory for tabs to remember previous state. And finally I have updated callbacks to change data in dcc.Store everytime I send new keys so when I leave the tab, other tabs know what is there:

@app.callback(
    Output('storage', 'data'),
    [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)
    lcheck=0
    return values

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

@app.callback(
    Output('private-key', 'value'),
    [Input('storage', 'data')],
    [State('storage', 'data')])
def update_values(values, value):
    if values:
        return value['value2']
1
  • Why do you call storage the way you did: [Input('storage', 'data')], [State('storage', 'data')]) ? If the storage changes the callback will be called and you can access data via Input. why do you need State in addition? I think this is redundant.
    – dl.meteo
    Commented May 29, 2020 at 16:40

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.