Slider not updating on change; however, Bokeh will recognize the slider update when I click a button

I am still trying to learn the nuances of Bokeh and am trying to craft a simple example that allows me to filter data by different methods. In this case I want to click one of two CheckboxButtons that will filter out, or filter back in certain data depending on the value in a specific column. In addition, I want to use a slider to filter out data that is above a certain value. In order to ensure that the execution of one filter does not undo the results of another filter, I am combining all filters into one javascript callback. The code as written (shown below) allows be to filter data by clicking one of the two CheckboxButtons at the instance of clicking the button. However, when I move the slider, the plot does not update on change, even through I am using the js_on_change function. If I move the slider, then click one of the ChecboxButtons, the plot will then correctly update based on both widgets. I am guessing I am missing something simple, any help would be appreciated.

    from bokeh.models import ColumnDataSource, CustomJS, Button
    from bokeh.models import CheckboxButtonGroup, CDSView, GroupFilter, Slider
    from bokeh.plotting import Figure, output_file, show
    from bokeh.layouts import column
    import pandas as pd

    output_file("js_on_change.html")

    x = [1.2, 2.4, 0.8, 5.6, 12.1, 8.8, 12.1, 13.8, 6.5]
    y = [3.4, 1.1, 4.2, 6.6, 1.8, 12.1, 7.8, 3.4, 9.4]
    z = ['Apple', 'Apple', 'Orange', 'Orange', 'Orange',
         'Orange', 'Apple', 'Apple', 'Orange']
    a = ['Water', 'Water', 'Water', 'Land', 'Land',
         'Water', 'Water', 'Land', 'Water']

    source = ColumnDataSource(data = dict(x=x, y=y, z=z, a=a))
    source_copy = ColumnDataSource(data = dict(x=x, y=y, z=z, a=a))

    view = CDSView(source=source, filters=[GroupFilter(column_name='a', group='Water')])

    plot = Figure(plot_width=400, plot_height=400)
    plot.circle('x', 'y', source=source, size=8, line_alpha=0.6)

    LABELS = ["Apple", "Orange"]
    checkbox_button_group = CheckboxButtonGroup(labels=LABELS, active=[0, 1])
    range_slider = Slider(start=0.0, end=15, value=15, step=0.2,
                          title="Max Value")


    callback = CustomJS(args=dict(source=source, source_copy=source_copy,
                                  slider=range_slider), code="""

        // Active buttons ["Apple", "Orange"]
        var active_labels = cb_obj.active.map((item) => cb_obj.labels[item])
        
        var d1 = source.data;
        var d2 = source_copy.data;
        var f = slider.value

        function getAllIndexes(arr, values, y_val, slider_val) {
            var indexes = [], i;
            for(i = 0; i < arr.length; i++)
            if (values.includes(arr[i]) && y_val[i] <= slider_val)
                indexes.push(i);
        return indexes;
        }
        
        // Get indices for columns with specific values
        var indexes = getAllIndexes(d2['z'], active_labels,
                                    d2['y'], f);
        
        d1['x'] = []
        d1['y'] = []
        d1['z'] = []
        d1['a'] = []
        
        // remap dataframes based on results
        for (var i = 0; i < indexes.length; i++) {
            d1['x'].push(d2['x'][indexes[i]])
            d1['y'].push(d2['y'][indexes[i]])
            d1['z'].push(d2['z'][indexes[i]])
            d1['a'].push(d2['a'][indexes[i]])
        }
        
        source.change.emit()

    """)

    checkbox_button_group.js_on_click(callback)
    range_slider.js_on_change('value', callback)

    layout = column(checkbox_button_group, range_slider, plot)

    show(layout)

Hi @webbja123 lease edit your post to use code formatting so that the code is more intelligible (either with the </> icon on the editing toolbar, or triple backtick ``` fences around the code blocks)

My apologies, 90% of edited to be formatted as code; however, for some reason the first 4 lines were missed. That has been corrected.

Thanks, purely as an FYI the triple backticks or </> editor button are usually preferable to just indenting the block, because those methods can add syntax highlighting as well. (I’ve editing the post to use ```)

One immediate issue is this:

var active_labels = cb_obj.active.map((item) => cb_obj.labels[item])

The value of cb_obj is whatever widget triggered the callback. Since you have attached the same callback to two widgets, that means the value of cb_obj is different depending on whether the button or the slider was used. But a slider does not have an active property, so the line above blows up when it was the slider. This can be seen in the browser dev tools:

The solution (to this one issue, at least) is to not use cb_obj in this case, and instead pass both the widgets in the args dict and refer to them explicitly using the separate names you assign them.

Also definitely look in to how to access your browsers JS console, dev tools, etc. Like any programming situation, the main key to debugging these callbacks is to be able to observe program state.

Thank you. I did not fully understand what cb_obj represented. Your description painted a better picture for me.