holoviz / panel

Panel: The powerful data exploration & web app framework for Python
https://panel.holoviz.org
BSD 3-Clause "New" or "Revised" License
4.78k stars 518 forks source link

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

Open sdc50 opened 3 years ago

sdc50 commented 3 years ago

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

philippjfr commented 3 years ago

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"]