Update secondary axis from sliders

I have succesfully added a secondary axis and I can control the range of y values on the plot by a slider, which nicely also update the values shown in the main y-axis laber.
Unfortunately, the axis on the right, the seconday one, does not update its labels.

I have the impression it has to do with the JS I wrote, but the JS console output is greek to me. FYI I am running this standalone HTML, not bokeh server.

How do I make my secondary axis follow the changes of the left axis?

    # Making the y-range sliders
    y_center_slider = Slider(start=_data[ydata].min(), end= _data[ydata].max(), value=_data[ydata].mean(), step=(_data[ydata].max()-_data[ydata].min())/200,height_policy='min',orientation='horizontal', title="Central Value") 
    y_extent_slider = Slider(start=0, end=(_data[ydata].max()-_data[ydata].min()), value=(_data[ydata].max()-_data[ydata].min()), step=1,height_policy='min',orientation='horizontal', title="Excursion")
   
    yrange_callback = CustomJS(args=dict(y_range=p.y_range\
                                         ,extra_y_ranges=p.extra_y_ranges\
                                         ,y_extent_slider=y_extent_slider,y_center_slider=y_center_slider), 
    code="""
        var center = y_center_slider.value
        var delta = y_extent_slider.value
        
        y_range.setv({"start": center-delta, "end": center+delta})
        extra_y_ranges.setv( {"second_axis": { "start": (center-delta)/center, "end": (center+delta)/center }})

    """)

    center=y_start_slider.value
    delta=y_extent_slider.value  
    
    
    p.extra_y_ranges = {"second_axis": Range1d(start=(center-delta)/center , end=(center+delta)/center )}
    
    p.add_layout(LinearAxis(y_range_name="second_axis",axis_label="% w.r.t Central Value"), 'right')
    
    y_start_slider.js_on_change('value', yrange_callback)
    y_extent_slider.js_on_change('value', yrange_callback)

Instead of passing the whole extra_y_ranges to the callback:

  • Extract the "second_axis" range into its own variable
  • Pass that variable both to CustomJS and to p.extra_y_ranges = {"second_axis": ...}

This works, thanks to the usual awesome tips you give. But can you also please explain what is the logic behind this? I have found myself making this mistake (of passing the wrong object) already a few times.

What is the rule? Passing the objects as deep as possible (i.e. pass p.y_range and not p or in this case pass p.extra_y_ranges["second_axis"] and not p.extra_y_ranges). I thought that, at least JSON-wise, the setv I was trying was making sense…

When you use

extra_y_ranges.setv( {"second_axis": { "start": (center-delta)/center, "end": (center+delta)/center }})

you say “set the value of second_axis to this plain JavaScript object”. But it has to be an instance of Range1d.

BTW I forgot to mention - I think setv is an implementation detail, so try not to use it. Instead, just assign the values directly:

y_range.start = center - delta;
y_range.end = center + delta;

Thanks for the explanation. I maybe start to get it. I just found myself with the same issue, I had passed loc=span.location but it turned out I needed to pass span=span and then assign span.location=3.
Will keep elaborating the mechanics of customJS …