Execute both Python (server side) and CustomJS code at a callback

I am wondering how you can have a hybrid callback that allows to run both Python and JS code (i.e. when a button is clicked). Essentially I’d like to execute a CustomJS first, then run some Python code and finally execute another CustomJS callback.

There is no way to ensure ordering of JS / Python callbacks on the same property. In practice, a JS callback would almost certainly execute first, since it is “right there”. But if you really want to ensure ordered, I think you would have to:

  • trigger a JS callback on a change to property A
  • in that JS callback: modify a property B, which has a Python callback
  • in the Python callback, modify a property C that has another JS callback

This seems failely clunky but I can’t think of another alternative.

I had something similar in mind but hoped a more elegant solution exists.
Out of curiousity why is not possible to execute a CustomJS directly without binding it to a property?

You can invoke a callback which runs CustomJS code on events other than model-property change events. In your first example, you can use an on-click event when a button is pressed.

There are a couple ways to do this that are syntactically different but functionally the same.

See the Adding Widgets section of the documentation for a button.js_on_click() example.

See the Events documentation for the syntax using the more general js_on_event() syntax.

For completeness, other types of user-interaction events such as manipulating or navigating plots also exist.

An event is still not a direct trigger/execution, in other words I’d like to do something like the following: CustomJS(…).execute() without any property/event/etc. binding which is apparently not possible as far as I know.

@Maxxner There’s nothing stopping you from calling execute but you would have to supply some dummy value for cb_obj.

why is not possible to execute a CustomJS directly without binding it to a property?

Same answer as always: that’s just the history of how things were developed in response to requirements at the time. FWIW very few people have ever asked about this, so naturally it has not gotten much thought or attention.

I wanted the same issue solved and I have my solution similar to Bryan’s.

  1. Make a dummy Div which is invisible and set a python event handler by on_XXX
  2. Set JS event by js_on_XXX, whose CustomJS would change the Div’s text at the end.
  3. Make another invisble Div and set CustomJS which calls a JS function to run JS codes for you
  4. Change the text of Div to call the all purpose JS codes. Here you may need to encode/decode the value of the text property.
    Here is the part of the codes.
def register_latent_event(self, py_handler, py_args:dict={}) -> Div:
        def newfunc(attr, old, new):
            return py_handler(attr=attr, old=old, new=new, **py_args)
        py_obj = Div(visible=False)
        py_obj.on_change('text', newfunc)
        self.add(py_obj, self.name + '_handler_' + py_handler.__name__) # Add it to doc and post it to the template
        self.latent_handlers.append(py_obj.name)
        return py_obj

def js_call_python_after_js(self, py_handler, py_args:dict={}, js_code:str='', js_args:dict={}) -> CustomJS:
        py_obj = self.register_latent_event(py_handler, py_args)
        js_args.update(dict(py_handler=py_obj, py_args=py_args))
        js_handler = CustomJS(args=js_args, code=js_code)
        return js_handler
1 Like