CDS.js_on_change not called, but CDS.on_change is

Hi,
I have a CDS which fires a python callback, but not the JS callback. I am under the impression that both should fire at the same event. In the example below when I modify a cell in the DataTable, the python callback is called, but the JS callback is not.

from bokeh.models import DataTable, ColumnDataSource, CustomJS, TableColumn
from bokeh.plotting import curdoc

source = ColumnDataSource(data = { 'x':[0,1,2,3], 'y':['a','b','c','d']})
columns = [TableColumn(field='x'), TableColumn(field='y')]
data_table = DataTable(source=source,columns= columns, editable=True)

callback_hello_js = CustomJS(code='''
    // just logs to the console
    console.log('hello from js');
    ''')

def callback_hello_py(attr, old, new):
    print('hello from python')

source.on_change('data',callback_hello_py)
source.js_on_change('data',callback_hello_js)

curdoc().add_root(data_table)

Seems like something to discuss and figure out, please file a GitHub issue with all this information.

Normally, events are only fired when the entire value of a property changes, not when a subitem (e.g. when all of .data is re-assigned, not when a single buried inside .data changes changes). So offhand I’m not sure the python property should be triggering a callback here, when a single element in the table column is changed. The best solution might be to make things consistent with the rest of bokeh and making the python property not fire (consistency is good), but adding some other event that can be watched for “subitem” changes since in this specific case of the table (it’s a common need).

Done.
Issue #9613
Yeah, ideally I would like a way to trigger a callback on the “subitem” changes.
Thanks

1 Like

Thanks, I definitely agree there should be some way, I was only stating that, for the sake of API consistency, perhaps it should not be on_change('data', ...) or js_on_change('data', ...)

Hi, apologies for replying to an old thread, but my issue is similar but I don’t think quite the same. I thought that if I completely replace my source.data then it’s not an in-place update, and my js callback should trigger, however it still refuses to trigger. The python callback works fine. Is this the expected behaviour or have I done something silly. Thanks.

edit: latest version of python (2.3), running the code in a bokeh server

cds_counter=ColumnDataSource(data={"counter":[0]})
### dummy button to see if can trigger js callback
button_dummy=Button(label="Test Button_dummy", button_type="success", width=60)
global dummy_int
dummy_int=0
def button_dummy_callback(event):  # button pressed callback, replaces cds "counter" in .data
    print("button has been pressed")
    global dummy_int
    dummy_int +=1
    temp=dummy_int
    cds_counter.data={"counter": [temp]}
    print(cds_counter.data["counter"])
js_counter_callback = CustomJS(args=dict(cds_counter=cds_counter), code=""" #DOES NOT FIRE
            console.log("test test test")
            """)
def py_counter_callback(attr, old, new):             # RUNS WHEN BUTTON PRESSED
    print("callback based on change has run.")
cds_counter.on_change("data", py_counter_callback)   # WORKING
cds_counter.js_on_change("data", js_counter_callback) #NOT WORKING

button_dummy.on_click(button_dummy_callback)

@sirspoon Python callbacks with on_change only function in Bokeh server applications. Are you actually running this as a Bokeh server application (i.e. with bokeh serve app.py and not just python app.py)?

Yep- it’s being run as a bokeh server, I can expand the code if required.

@sirspoon yes please provide a Minimal Reproducible Example but also please make a brand new topic (I only just noticed this resurrected a very old question)