Issues when using callbacks to passback a slider

Hi All,

I am trying to do something that should be very simple, yet I get an error loading layout. I have narrowed it down to a problem which manifests when a callback is used to generate a slider, the value of that slider is then used in a callback to look up and display data with that value as an index.

I have made a simplified code to illustrate the issue:

This code creates a dropdown which offers a choice of two timebases (hour and day). It then creates a slider which is produced through a callback with timebase as an input. The slider will either have the range of the hour timebase, or the day timebase. That alone works. However, when a separate div is created to display what value has been chosen by the slider (when using the value of the slider as an index to look up a date in the data frame), the app breaks and says “error loading layout”.

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

app = dash.Dash()
application = app.server

app.config.supress_callback_exceptions = True  # to passback callbacks

# two data sets with different timebases
idx_rangeh = [i for i in range(1000)]
hours = pd.DataFrame(pd.date_range(datetime(2017, 5, 1), periods=1000, freq='H'), index=idx_rangeh, columns=['date'])

idx_ranged = [i for i in range(100)]
days = pd.DataFrame(pd.date_range(datetime(2017, 5, 1), periods=100, freq='D'), index=idx_ranged, columns=['date'])



# make map layout
app.layout = html.Div([

    # timebase dropdown

    dcc.Dropdown(id='timebase',
                 options=[
                     {'label': 'Hourly', 'value': 'Hr'},
                     {'label': 'Daily', 'value': 'Day'}
                 ],
                 value='Hr',
                 clearable=False
                 ),

    html.Div(id='slider-keeper'),

    html.H4(id='slider-value', style={'fontSize': 24, 'display': 'inline-block', 'margin-top': '25px'})

],style={'width': '80%', 'margin-left': 'auto',
              'margin-right': 'auto','textAlign': 'center','backgroundColor': '#b3b3b3'})



@app.callback(Output('slider-keeper', 'children'),
              [Input('timebase', 'value')])
def update_slider(timebase):
    if timebase == 'Hr':
        maxval = hours.shape[0] - 1
    elif timebase == 'Day':
        maxval = days.shape[0] - 1

    return dcc.Slider(
        id='map-slider',
        min=0,
        max=maxval,
        step=1,
        value=0,
        updatemode='drag'
    )


@app.callback(Output('slider-value', 'children'),
              [Input('timebase', 'value'),
               Input('slider', 'value')])
def SliderText(timebase, idx):
    if timebase == 'Hr':
        val = (days['date'][idx]).strftime('%Y-%m-%d %H:%M')
    elif timebase == 'Day':
        val = (hours['date'][idx]).strftime('%Y-%m-%d %H:%M')
    return '{} which is idx {}'.format(val, idx)  # idx


if __name__ == '__main__':
    application.run(debug=True, port=8080)

Do you guys see anything that might be wrong about this? If so, please explain what is breaking here, and what a better approach may be.

Thanks,
Troy

You seem to be using:

But I cannot see any component with the Id ‘slider’. The Slider that you return in your first callback has the id ‘map-slider’.

If you hit F12 (using chrome) and look at the javascript console you will probably see some sort of helpful error in there.

Hi Mike,
Thanks for your response, and the catch - that was a typo when trying to simplify things. “map-slider” should have been "slider’ in the “update_slider” callback. Even after fixing that, the same problem still persists. Here is an updated code with typo fixed:
The error is “Error loading dependencies” … yikes!

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

app = dash.Dash()
application = app.server

app.config.supress_callback_exceptions = True  # to passback callbacks

# two data sets with different timebases
idx_rangeh = [i for i in range(1000)]
hours = pd.DataFrame(pd.date_range(datetime(2017, 5, 1), periods=1000, freq='H'), index=idx_rangeh, columns=['date'])

idx_ranged = [i for i in range(100)]
days = pd.DataFrame(pd.date_range(datetime(2017, 5, 1), periods=100, freq='D'), index=idx_ranged, columns=['date'])



# make map layout
app.layout = html.Div([

    # timebase dropdown

    dcc.Dropdown(id='timebase',
                 options=[
                     {'label': 'Hourly', 'value': 'Hr'},
                     {'label': 'Daily', 'value': 'Day'}
                 ],
                 value='Hr',
                 clearable=False
                 ),

    html.Div(id='slider-keeper'),

    html.H4(id='slider-value', style={'fontSize': 24, 'display': 'inline-block', 'margin-top': '25px'})

],style={'width': '80%', 'margin-left': 'auto',
              'margin-right': 'auto','textAlign': 'center','backgroundColor': '#b3b3b3'})



@app.callback(Output('slider-keeper', 'children'),
              [Input('timebase', 'value')])
def update_slider(timebase):
    if timebase == 'Hr':
        maxval = hours.shape[0] - 1
    elif timebase == 'Day':
        maxval = days.shape[0] - 1

    return dcc.Slider(
        id='slider',
        min=0,
        max=maxval,
        step=1,
        value=0,
        updatemode='drag'
    )


@app.callback(Output('slider-value', 'children'),
              [Input('timebase', 'value'),
               Input('slider', 'value')])
def SliderText(timebase, idx):
    if timebase == 'Hr':
        val = (days['date'][idx]).strftime('%Y-%m-%d %H:%M')
    elif timebase == 'Day':
        val = (hours['date'][idx]).strftime('%Y-%m-%d %H:%M')
    return '{} which is idx {}'.format(val, idx)  # idx


if __name__ == '__main__':
    application.run(debug=True, port=8080)

Thanks,
Troy

It’s probably because your using an Input (‘slider’) which does not exist in the initial layout to modify an element (‘slider-value’) which does exist in the initial layout. Look at the JavaScript console in your browser and reload. It should tell you what’s wrong in there.

You will need to include the slider in your initial layout and just change the maxval in the callback e.g.

app.layout = html.Div([

    # timebase dropdown

    dcc.Dropdown(id='timebase',
                 options=[
                     {'label': 'Hourly', 'value': 'Hr'},
                     {'label': 'Daily', 'value': 'Day'}
                 ],
                 value='Hr',
                 clearable=False
                 ),

    dcc.Slider(
        id='slider',
        min=0,
        step=1,
        value=0,
        updatemode='drag'
    ),

    html.H4(id='slider-value', style={'fontSize': 24, 'display': 'inline-block', 'margin-top': '25px'})

],style={'width': '80%', 'margin-left': 'auto',
              'margin-right': 'auto','textAlign': 'center','backgroundColor': '#b3b3b3'})



@app.callback(Output('slider', 'max'),
              [Input('timebase', 'value')])
def update_slider(timebase):
    if timebase == 'Hr':
        maxval = hours.shape[0] - 1
    elif timebase == 'Day':
        maxval = days.shape[0] - 1

    return maxval

@app.callback(Output('slider-value', 'children'),
              [Input('timebase', 'value'),
               Input('slider', 'value')])
def SliderText(timebase, idx):
    if timebase == 'Hr':
        val = (days['date'][idx]).strftime('%Y-%m-%d %H:%M')
    elif timebase == 'Day':
        val = (hours['date'][idx]).strftime('%Y-%m-%d %H:%M')
    return '{} which is idx {}'.format(val, idx)  # idx


if __name__ == '__main__':
    application.run(debug=True, port=8080)

Hi Mike, Thanks for the response.

Thats unfortunate, because it then seems like there is no solution to the issue I posted here which results from using the method you just mentioned:

That one is real puzzling.

The usage of sliders seems to be pretty limited in Dash. I figured that the only way to solve the issue in the thread I linked was to re-instantiate the slider through callbacks, but if that doesn’t work then I don’t know what will :frowning:

I can’t seem to figure out any way to successfully dynamically update sliders in dash (marks and ranges) without having some weird glitches manifest.

If you have any insight, it would be much appreciated.

Thanks,
Troy

hmm… you should report this issue on the git hub https://github.com/plotly/dash-core-components/issues

Have you tried creating a dummy slider for page load using your original method?

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

app = dash.Dash()
application = app.server

app.config.supress_callback_exceptions = True  # to passback callbacks

# two data sets with different timebases
idx_rangeh = [i for i in range(1000)]
hours = pd.DataFrame(pd.date_range(datetime(2017, 5, 1), periods=1000, freq='H'), index=idx_rangeh, columns=['date'])

idx_ranged = [i for i in range(100)]
days = pd.DataFrame(pd.date_range(datetime(2017, 5, 1), periods=100, freq='D'), index=idx_ranged, columns=['date'])



# make map layout
app.layout = html.Div([

    # timebase dropdown

    dcc.Dropdown(id='timebase',
                 options=[
                     {'label': 'Hourly', 'value': 'Hr'},
                     {'label': 'Daily', 'value': 'Day'}
                 ],
                 value='Hr',
                 clearable=False
                 ),

    html.Div([dcc.Slider(id='slider')], id='slider-keeper'), # dummy slider

    html.H4(id='slider-value', style={'fontSize': 24, 'display': 'inline-block', 'margin-top': '25px'})

],style={'width': '80%', 'margin-left': 'auto',
              'margin-right': 'auto','textAlign': 'center','backgroundColor': '#b3b3b3'})



@app.callback(Output('slider-keeper', 'children'),
              [Input('timebase', 'value')])
def update_slider(timebase):
    if timebase == 'Hr':
        maxval = hours.shape[0] - 1
    elif timebase == 'Day':
        maxval = days.shape[0] - 1

    return dcc.Slider(
        id='map-slider',
        min=0,
        max=maxval,
        step=1,
        value=0,
        updatemode='drag'
    )


@app.callback(Output('slider-value', 'children'),
              [Input('timebase', 'value'),
               Input('slider', 'value')])
def SliderText(timebase, idx):
    if timebase == 'Hr':
        val = (days['date'][idx]).strftime('%Y-%m-%d %H:%M')
    elif timebase == 'Day':
        val = (hours['date'][idx]).strftime('%Y-%m-%d %H:%M')
    return '{} which is idx {}'.format(val, idx)  # idx


if __name__ == '__main__':
    application.run(debug=True, port=8080)

You may need to catch some exception in the bottom callback for page load when idx is None

Nice,

That dummy slider did the trick. I’ll try to apply this in combo with my other post and see if I can get a solution there. I’ll report back

Thanks!
Troy

In combo with the other problem mentioned in the other thread (buggy dynamic slider), the buggy slider issue still persists. Here is my code if you’re interested in seeing them combo’d.

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

app = dash.Dash()
application = app.server

app.config.supress_callback_exceptions = True  # to passback callbacks

# two data sets with different timebases
idx_rangeh = [i for i in range(1000)]
hours = pd.DataFrame(pd.date_range(datetime(2017, 5, 1), periods=1000, freq='H'), index=idx_rangeh, columns=['date'])

idx_ranged = [i for i in range(100)]
days = pd.DataFrame(pd.date_range(datetime(2017, 5, 1), periods=100, freq='D'), index=idx_ranged, columns=['date'])



# make map layout
app.layout = html.Div([

    # dateRange picker
    html.Div([
        html.H3('Date Range:', style={'display': 'inline-block', 'marginRight': '10px'}),

        dcc.DatePickerRange(
            id='date-picker',
            min_date_allowed=datetime(2017, 5, 1),
            max_date_allowed=datetime(2017, 8, 10),
            start_date=datetime(2017, 5, 1),
            end_date=datetime(2017, 8, 10)
        )
    ], style={'display': 'inline-block'}),

    # timebase dropdown

    dcc.Dropdown(id='timebase',
                 options=[
                     {'label': 'Hourly', 'value': 'Hr'},
                     {'label': 'Daily', 'value': 'Day'}
                 ],
                 value='Hr',
                 clearable=False
                 ),

    #html.Div(id='slider-keeper'),
    html.Div([dcc.Slider(id='slider')], id='slider-keeper'),  # dummy slider

    html.H4(id='slider-value', style={'fontSize': 24, 'display': 'inline-block', 'margin-top': '25px'})

],style={'width': '80%', 'margin-left': 'auto',
              'margin-right': 'auto','textAlign': 'center','backgroundColor': '#b3b3b3'})



@app.callback(Output('slider-keeper', 'children'),
              [Input('timebase', 'value'),
               Input('date-picker', 'start_date'),
               Input('date-picker', 'end_date')])
def update_slider(timebase,start,end):
    if timebase == 'Hr':
        #maxval = hours.shape[0] - 1
        maxval = hours[(hours['date']>start) & (hours['date']<end)].shape[0] - 1
    elif timebase == 'Day':
        #maxval = days.shape[0] - 1
        maxval = days[(days['date'] > start) & (days['date'] < end)].shape[0] - 1

    return dcc.Slider(
        id='slider',
        min=0,
        max=maxval,
        step=1,
        value=1,
        updatemode='drag'
    )


@app.callback(Output('slider-value', 'children'),
              [Input('timebase', 'value'),
               Input('slider', 'value'),
               Input('date-picker', 'start_date'),
               Input('date-picker', 'end_date')
               ])
def SliderText(timebase, idx,start,end):
    if timebase == 'Hr':
        #val = (hours['date'][idx]).strftime('%Y-%m-%d %H:%M')
        val = (hours[(hours['date'] > start) & (hours['date'] < end)]['date'][idx]).strftime('%Y-%m-%d %H:%M')
    elif timebase == 'Day':
        val = (days[(days['date'] > start) & (days['date'] < end)]['date'][idx]).strftime('%Y-%m-%d %H:%M')
    return '{} which is idx {}'.format(val, idx)  # idx


if __name__ == '__main__':
    application.run(debug=True, port=8080)

Hope that issue gets solved. I see it as a huge shortcoming to dash.

Best,
Troy