CustomJS accepts arguments that are passed to javascript during callback.
Though, I observed that if I use a variable as argument to a CustomJS callback, then modify the value of the variable before triggering the callback, the callback uses an outdated value of the variable.
This is different from python callbacks, in which the current value of inputs variables are used.
Here is a short example comparing the behavior of Python and CustomJS callbacks, in which I also introduced what I see as a workaround for CustomJS to use an up to date argument, using a paragraph object to store the input (launch with bokeh serve --show):
from bokeh.io import curdoc
from bokeh.models import Button, Paragraph, CustomJS
from bokeh.layouts import column
js_code = """
alert("Input is: " + input +
", Input from paragraph is: " + paragraph.text)
"""
button_py = Button(label="Start python-callback")
button_js = Button(label="Start js-callback")
input = 1
paragraph = Paragraph(text=str(input))
def py_callback():
print(f"Input is {input}")
print(f"Input from paragraph is {paragraph.text}")
# Callbacks:
button_py.on_click(py_callback)
button_js.js_on_click(CustomJS(args=dict(input=input, paragraph=paragraph), code=js_code))
input = 2
paragraph.text = str(input)
curdoc().add_root(column(button_py, button_js))
The Python callback leads as expected to the print log:
“Input is 2
Input from paragraph is 2”
The CustomJS callback leads to “Input is: 1, Input from paragraph is: 2”, I had expected “Input is: 2, Input from paragraph is: 2”.
Is there another more straightforward way to dynamically update the arguments of CustomJS callbacks than passing them to a bokeh model?
This is standard Python behavior, where variables are just names for values, and not references to memory:
>>> a = 10
>>> b = a
>>> b
10
>>> a = 20
>>> b # still 10
10
For what you are looking for, your best is probably to create a DataModel subclass. A DataModel is a very lightweight user-defined model, created with only a small amount of declarative Python, that can be synchronized across the Python ↔ JS boundary like any standard Bokeh object:
class Params(DataModel):
foo = Float(default=0.1)
bar = Int(default=10)
If you pass an instance of a Params to a CustomJS it will not be “evaluated” until right before it is serialized to generate the output. There is a complete example here: