Template doesn't update when using callback

Hi,

I have a panel app that requires a bit of data, so it takes ~15 seconds to startup. Currently, I load the data during the creation of the view that I pass to .servable(). This works, but for the user it appears that nothing is happening while the data are being loaded.

I’m trying to start with showing a simple “please wait” text immediately on startup, and then re-populate my app after the data are loaded. I’m trying to do this with one of the callback functions. Everything seems to run, but the view is not updated. Is there a way to tell Panel that it should update?

import panel as pn
import time

pn.extension()


def view():
    template = pn.template.BootstrapTemplate()
    template.main.append(pn.pane.Markdown("**Loading ...**"))

    # Load dashboard with callback:
    # The callback is run (print statements are shown), but the panel app is not updated
    pn.state.curdoc.add_next_tick_callback(load_dashboard(template.main))

    # Explicitly load the dashboard:
    # This works but nothing is shown until data are loaded
    # load_dashboard(template.main)()

    return template


def load_dashboard(dashboard):
    def load_dashboard_callback():
        print("Loading")
        time.sleep(2)
        dashboard.append(pn.widgets.StaticText(name="Status", value="loaded"))
        print("Done")

    return load_dashboard_callback


if __name__.startswith("bokeh_"):
    view().servable(title="Callback testing")

When using the callback, printing template.main.objects show that things have updated, but it’s not showing on the screen.

This is in fact expected, templates as the name suggests, use templating to generate a HTML file that will be served on initial render. To achieve what you want you will have to add a Row/Column to the template.main object which you can then subsequently populate.

from functools import partial

def view():
    template = pn.template.BootstrapTemplate()
    container = pn.Column(sizing_mode='stretch_both')
    template.main.append(container)
    container.append(pn.pane.Markdown("**Loading ...**"))

    pn.state.onload(partial(load_dashboard, container))

    return template

def load_dashboard(container):
    print("Loading")
    container[:] = [pn.widgets.StaticText(name="Status", value="loaded")]
    print("Done")
1 Like

Thanks a lot, Philipp, for the quick reply! That makes sense! Your suggestion works perfectly, also in my bigger, real-world use case.

One small note: .on_load() seems to be spelled without the underscore: .onload().

Best,
Geir Arne

1 Like

Great, fixed the spelling.

1 Like