Hi,
I’m trying to create a DataTable dashboard with 1) multiple widget filters, 2) updates to the data maybe once per minute.
Problem is that widget filters don’t work as expected after data is updated.
Below is a minimal working example of what I’m trying to do, with a button for manually updating the data (update in demo is just incrementing counter on one row). Filters work as expected before data is updated, but after first update they stop working properly.
My guess is that the data used as source in the DataTable and the same data that is used in the CustomJS as original_source go out of sync when data is updated, but I haven’t been able to figure out just what to do about that. Any help would be appreciated!
My Bokeh version is 2.4.1 and I’m running the app on Bokeh server.
from bokeh.models.widgets import Select
from bokeh.models import Button, CustomJS, ColumnDataSource,DataTable,TableColumn
from bokeh.io import curdoc
from bokeh.layouts import column, row
data_source = {
'name': ["Bob", "John", "Mark"],
'size': ["S", "M", "L"],
'country':["US", "Canada", "US"],
'counter':[1,2,3]
}
source = ColumnDataSource(data=data_source)
original_source = ColumnDataSource(data=data_source)
columns = [TableColumn(field=col) for col in data_source]
data_table = DataTable(source=source, columns=columns)
size_list = ['ALL', 'S', 'M', 'L', 'XL']
size_select = Select(title="Size:", value=size_list[0], options=size_list)
country_list = ['ALL', 'US', 'Canada']
country_select = Select(title="Country:", value=country_list[0], options=country_list)
callback_code = """
var data = source.data;
var original_data = original_source.data;
var size_select = size_select_obj.value;
var country_select = country_select_obj.value;
for (var key in original_data) {
data[key]=[]
for (var i = 0; i < original_data['name'].length; ++i) {
if (((original_data['size'][i]==size_select || size_select=='ALL') && (original_data['country'][i]==country_select || country_select=='ALL'))) {
data[key].push(original_data[key][i]);
}
}
}
source.change.emit();
target_obj.change.emit();
"""
callback = CustomJS(
args=dict(source=source,
original_source=original_source,
size_select_obj=size_select,
country_select_obj=country_select,
target_obj=data_table),
code=callback_code
)
size_select.js_on_change('value', callback)
country_select.js_on_change('value', callback)
def update_data() -> None:
data_source['counter'][0]+=1
data_table.source=ColumnDataSource(data=data_source)
button = Button(label="Update", button_type="success")
button.on_click(update_data)
curdoc().add_root(column(button, row(size_select, country_select), data_table))