panel icon indicating copy to clipboard operation
panel copied to clipboard

JS triggering of "loading" css_classes doesn't propagate to Python

Open sdc50 opened this issue 4 years ago • 1 comments

I often want to set the loading state of some element with JavaScript:

btn = pn.widgets.Button(name='Load', button_type='primary')
btn.js_on_click(args={'btn': btn}, code='btn.css_classes.push("pn-loading", "arcs"); btn.properties.css_classes.change.emit();')

but then I want to reset it in Python. I assumed this this would be sufficient:

btn.css_classes = []

But setting css_classes to the empty list doesn't trigger a change, presumably because the change to css_classes wasn't propagated from the JS so Python sees it as still sees it as the empty list.

So I assumed that this would force the JS Bokeh model to reset:

btn.css_classes = []
btn.param.trigger('css_classes')

But it is also ineffective at resetting the loading state.

The only thing that I could get to work is to set css_classes to something else:

btn.css_classes = ['temp']
btn.css_classes = []

Here is a full example:

class Demo(param.Parameterized):
    def load(self):
        sleep(2)
        self.btn.css_classes = ['temp']
        self.btn.css_classes = []
        
    def panel(self):
        self.btn = pn.widgets.Button(name='Load', button_type='primary')
        self.btn.js_on_click(
               args={'btn': self.btn}, 
               code='btn.css_classes.push("pn-loading", "arcs"); btn.properties.css_classes.change.emit();'
        )
        self.btn.on_click(lambda e: self.load())

        return self.btn
    
d = Demo()
d.panel()

panel_loading

sdc50 avatar Jun 23 '21 14:06 sdc50

Fixed in #2467 but there is a wrinkle with your code, for some reason this doesn't work:

btn.css_classes.push("pn-loading", "arcs"); 
btn.properties.css_classes.change.emit()

but this does:

btn.css_classes = ["pn-loading", "arcs"]

philippjfr avatar Jun 30 '21 19:06 philippjfr