I’d like to allow the user to use a Dropdown widget to load a file. Rather than designing a custom widget, I figured an easy way to do this would be to have a hidden FileInput widget which gets “clicked” via a CustomJS callback when the user clicks the Dropdown widget. I can then use the FileInput widget as normal.
Unfortunately I can’t figure out how to trigger the “click” event on the FileInput widget in the CustomJS callback. Here’s what I tried:
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import Dropdown, FileInput, CustomJS
# Set up widgets
dropdown = Dropdown(label='File', menu=['Open'])
fileinput = FileInput(visible=False)
# Set up callback for Dropdown widget
dropdown.js_on_click(CustomJS(args=dict(FileInput=fileinput),
code="""
if (this.item == 'Open')
FileInput.click();
"""))
# Set up callback for hidden FileInput widget
def loadfile(attr, old, new):
print(fileinput.filename)
fileinput.on_change('filename', loadfile)
# Set up layouts and add to document
curdoc().add_root(column(dropdown, fileinput))
The FileInput.click() code is not doing anything though…I just get an error in the console that FileInput.click is not a function.
I’ve realized that the FileInput variable in the JavaScript code above is the Bokeh model rather than the DOM element ID. If I open a web page showing a Bokeh document with a FileInput widget, right-click the widget and click “inspect”, that DOM element can be accessed as $0. So I can write in the Console: $0.click() and the FileInput widget is triggered.
So the question becomes how to get the DOM element ID of a widget created by Bokeh? It looks like widgets created by Bokeh don’t generally have DOM element IDs…when I select a widget and type in the console $0.id it just shows an empty string.
One can get the DOM element ID via document.getElementById(), followed by querySelector('input[type=file]') if the element is embedded in the application’s Jinja template. Like so:
main.py:
from bokeh.io import curdoc
from bokeh.models import Dropdown, FileInput, CustomJS
from bokeh.events import ButtonClick
# Set up widgets
dropdown = Dropdown(label='File', menu=['Open'], name='Dropdown', tags=['hi'])
fileinput = FileInput(visible=False, name='FileInput')
# Set up callback for Dropdown widget
dropdown.js_on_click(CustomJS(code="""
if (this.item == 'Open') {
document.getElementById('FileInputElement').querySelector('input[type=file]').dispatchEvent(new MouseEvent('click'))
}
"""))
# Set up callback for hidden FileInput widget
def loadfile(attr, old, new):
print(fileinput.filename)
fileinput.on_change('filename', loadfile)
# Set up layouts and add to document
curdoc().add_root(dropdown)
curdoc().add_root(fileinput)