Show/hide radio and form elements in a menu

This sort of thing is way out of my area. My app has about 20 radio buttons, dropdown boxes, checkboxes, etc. The actual figure is now beneath the fold, difficult to see without scrolling. Eventually, there will be at least 40 buttons/options which is clearly a UX nightmare but the application needs this fine level of control.

I’m trying to find a neat (react?) solution which hides the div containing all the form elements (radio buttons etc) on click, in an elegant menu. Then another click swipes them back again. Any ideas? I imagine this may come up again…

Cheers

Will

3 Likes

You’ll have to post some screenshots when you’re done :slight_smile:

Would something like this work?

app.layout = html.Div([
    dcc.RadioItems(
        id='toggle',
        options=[{'label': i, 'value': i} for i in ['Show', 'Hide']],
        value='Show'
    ),
    html.Div(id='controls-container', children=[
        # all of your controls
        dcc.Dropdown(...)
        # ...
    ]),
    dcc.Graph(...)
])

@app.callback(Output('controls-container', 'style'), [Input('toggle', 'value')])
def toggle_container(toggle_value):
    if toggle_value == 'Show':
        return {'display': 'none'}
    else:
        return {'display': 'block'}
6 Likes

Oh, simple idea. Ha, thanks Chris. Was expecting it to be way more complicated. Will see if I can expand on that - thanks a lot!

2 Likes

Another way to do this is to use dash_html_components.Details and dash_html_components.Summary. This is native in HTML5, no callbacks required! See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details for details.

html.Details([
    html.Summary('Label of the item'),
    html.Div('Contents')
])

I use this in the dash user guide to hide/show the code blocks

7 Likes

Love it @chriddyp - perfect!

1 Like

This looks ideal; however, the dcc.Dropdown component doesn’t have a ‘style’ attribute. How can we toggle a component’s display without this?

I’m sure I’m overlooking something and it’s a quick fix.

===========================================

I found one resolution. You can bypass using the style attribute and control the display of html elements by returning the children argument in a callback.

Thank you for the solution. I got this convo while searching for tabbing solution. I think it works perfectly.

my graph width was reduced after it was wrapped with html.Detailed. how should i fix it?

html.Div([
html.Details([
dcc.Graph(id=‘corner_indicators’, config={‘displayModeBar’: False})
])
], className=‘row’),

Hi @chriddyp,

I am new to dash, right now stuck in the below code,

As in the code, there are two drop downs,
my requirement is,

a) if (dropdown1==‘A’) and (dropdown2==‘X’):
----> display slider with id=‘a’ (Requirement)

b) if (dropdown1==‘B’) and (dropdown2==‘X’):
----> display slider with id=‘b’ (Requirement)

c) And I need to display the value of selected slider. (Dynamically)

But now, with this code below, on selecting dropdown respective slider is displayed. But the value of the slider is not displayed dynamically.

Could you please help me out?

Thank you!

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input,Output

app=dash.Dash()

app.layout = html.Div([
        html.Div([dcc.Dropdown(id='first-dropdown',options=[{'label': 'All', 'value': 'All'},{'label': 'A', 'value': 'A'},{'label': 'B', 'value': 'B'}, {'label': 'C', 'value': 'C'},{'label': 'D', 'value': 'D'}],value='A')],style={'width':200,'marginLeft':10,'marginTop':10}),
        html.Br(),
        html.Div([dcc.Dropdown(id='second-dropdown',options=[{'label': 'All', 'value': 'All'}, {'label': 'X', 'value': 'X'}, {'label': 'Y', 'value': 'Y'}, {'label': 'Z', 'value': 'Z'}],value='X')],style={'width':200,'marginLeft':10,'marginTop':10}),
        html.Br(),
    html.Div(id='controls-container',children=[dcc.Slider(id='a')]),
    html.Div(id='controls-container1',children=[dcc.Slider(id='b')]),
    html.Br(),html.Br(),
    html.Div(id='disp_values')
])

@app.callback(Output('controls-container', 'children'),[Input('first-dropdown', 'value'),Input('second-dropdown', 'value')])
def toggle_container(first_dropdown,second_dropdown):
    if first_dropdown == 'A' and second_dropdown == 'X':
        sig_var=['a','b','c'] # Output from certain function
        sig_coeff=[0.15555,0.25333,0.05222] # Output from certain function
        if 'a' in sig_var:
            val_var=round(sig_coeff[sig_var.index('a')],4)
            min_var=round(val_var-0.1,4)
            max_var=round(val_var+0.1,4)
            return dcc.Slider(id='a',min=min_var, max=max_var, step=0.001, value=val_var, marks={min_var:str(min_var),val_var:str(val_var),max_var:str(max_var)})
        else:
            return {'display':'none'}
        
@app.callback(Output('controls-container1', 'children'),[Input('first-dropdown', 'value'),Input('second-dropdown', 'value')])
def toggle_container1(first_dropdown,second_dropdown):
    if first_dropdown == 'B' and second_dropdown == 'X':
        sig_var=['a','b','c'] # Output from certain function
        sig_coeff=[0.75555,0.85333,0.08222] # Output from certain function
        if 'b' in sig_var:
            val_var=round(sig_coeff[sig_var.index('b')],4)
            min_var=round(val_var-0.1,4)
            max_var=round(val_var+0.1,4)
            return dcc.Slider(id='b',min=min_var, max=max_var, step=0.001, value=val_var, marks={min_var:str(min_var),val_var:str(val_var),max_var:str(max_var)})
        else:
            return {'display':'none'}
    
@app.callback(Output('disp_values', 'children'),[Input('a', 'value'),Input('b', 'value')])
def toggle_container2(val_a,val_b):
    print(val_a,val_b)
    return html.Label([val_a,val_b])

app.css.append_css({'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'})


if __name__ == '__main__':
    app.run_server(debug=False,port=8000)

@zhsee @chriddyp

I have the same behavior. My chart is smaller than without using html.Details().
I’ve also noticed that browser size change (normal, maximize) causing revert to original chart size.
Have you already solved that issue?

Best Regards

1 Like

Very nice and important solution in Dash!
It actually is turning the options around (hiding when it needs to be shown :wink: , The last part needs to be:

    if toggle_value == 'Hide':
        return {'display': 'none'}
    else:
        return {'display': 'block'}
2 Likes

This was a life saver ! I was trying to get dbc NavbarSimple to work with custom styling but just could not get it to work. Using html.Details achieved the same result.

I can’t follow your idea. Can you give an example for your solution?