Unable to trigger client side model change event

I’ve a custom widget and I’m trying to indirectly trigger a change event. However, I’m unable to get the javascript model.on('change:', ...) function to trigger.

In the following snippet. I can see by initializing my widget with pre_data, the depends picks it up and sets data (which is also printed). On the javascript side, I can see a log (and header div) showing the render function has been executed.

I just don’t see the change:data event being triggered. Am I missing something obvious?

import panel as pn
import param

pn.extension()

class FooWidget(pn.custom.AnyWidgetComponent, pn.widgets.WidgetBase):

    _esm = """
        function render({ model, el }) {
            console.log("FooWidget render", model, el);
            let div = document.createElement("div");
            div.innerHTML = "<h1>FooWidget</h1>";
            el.appendChild(div);

            model.on("change:data", function () {
                // this doesn't get triggered?
                div.innerHTML = model.get("data").toString();
            });
        }
        export default { render }
    """

    data = param.List(
        default=[],
    )

    pre_data = param.List(
        default=[],
    )

    @param.depends("pre_data", watch=True, on_init=True)
    def _pre_data_to_data(self):
        def some_transform(_):
            return _

        self.data = some_transform(self.pre_data)
        print(self.data)


app = FooWidget(pre_data=["a", "b", "c"])

pn.serve(
    dict(app=app),
    threaded=True,
    autoreload=True,
)

So the callback triggers when the data is updated, but data isn’t updated after initialization in your example. The _pre_data_to_data callback runs on instantiation of the widget and the callback is never triggered. If you add a Button that changes the pre_data you can see the event is triggered:

app = FooWidget(pre_data=["a", "b", "c"])

def add(_):
    app.pre_data = ['d']

b = pn.widgets.Button(on_click=add, name='Add')

pn.serve(
    dict(app=pn.Row(app, b)),
    threaded=True,
    autoreload=True,
)
1 Like

That makes sense. Thank you.