ReactiveHTML as a layout

Was wondering if it was possible to reference panel objects in the ReactiveHTML._template?
Like in standard templates using the embed function

1 Like

I have discussed this with Philipp and believe I’ve seen this in action. But I don’t have a reference example. And it’s not in the docs. Custom Components — Panel 0.12.0rc8 documentation (pyviz-dev.github.io)

But I think you can just try out if it works.

For the record this seems to work:

2 Likes

This looks so promissing @xavArtley . I’m looking so much forward to what the community can and will use this for. And if we will see a lot of “home grown” components created for Panel.

On question for @philippjfr . Would building larger layouts from this be slow due to the Bokeh layout engine? Or did you find a way to overcome this?

No, in fact ReactiveHTML bypasses the layout engine (if it can), i.e. if you add a ReactiveHTML based layout as a root to a document it completely avoids invoking the layout engine.

1 Like

No, in fact ReactiveHTML bypasses the layout engine (if it can), i.e. if you add a ReactiveHTML based layout as a root to a document it completely avoids invoking the layout engine.

@philippjfr could you elaborate on this? This seems very appealing to us, as we have quite a specific look in mind and are constantly fighting the Bokeh styling (we have seen the branch that uses Bokeh 3.0 that will also help with this in future).

My simple example did not work as expected, that means, my ReactiveHTML component is still wrapped in several divs generated by Bokeh with explicit styling.

import panel as pn
import param
from panel.reactive import ReactiveHTML


pn.extension()


class RootContainer(ReactiveHTML):

    child = param.Parameter()
    _template = '<div id="root">${child}</div>'

    def __init__(self, **params):
        super().__init__(**params)
        self.child = params.setdefault("child", pn.widgets.Button())


RootContainer().servable()
1 Like

Pn layouts are nice for allowing serialization of holoviews objects. Is there a way to access the plot’s underlying CDS from the _scripts, so that it can be synchronized with e.g. a param.DataFrame?*

*Yes, I know I can use hv.DynamicMap and/or streamz, but neither supports patching the CDS. And I prefer hvplot’s convenience too much to directly construct low-level bokeh plots. So, was hoping to create the plot with hvplot/holoviews, and then directly access the underlying CDS for updating/streaming/patching data.

Ah, I figured it out!

Docs mention that JS context has view object available to its namespace, which holds the bokeh view that renders the component. As such, you also get access to the bokeh plot’s CDS.

class Test(pn.reactive.ReactiveHTML):
    hv_pane = param.Parameter()
    df = param.DataFrame()

    _template = """
    <div id="hv_pane">${hv_pane}</div>
    """
    
    _scripts = {
        "df":"""
        /*
        JS context has access to:
        
        data: The data model holds the current values of the synced parameters, e.g. data.value will reflect the current value of the input node.
        model: The ReactiveHTML model which holds layout information and information about the children and events.
        state: An empty state dictionary which scripts can use to store state for the lifetime of the view.
        view: The Bokeh View class responsible for rendering the component. This provides access to method like invalidate_layout and run_script which allows invoking other scripts.
        <node>: All named DOM nodes in the HTML template, e.g. the input node in the example above.
        */
        
        // Let's access the plot's CDS, and change its values:
        var cds = view.child_models[0].renderers[0].data_source;
        cds.data['y'] = data.df['y']; // set new data from df
        cds.change.emit(); // emit changes
        """
    }
    
    def __init__(self,**params):
        super().__init__(**params)
        
    
test = Test(
    hv_pane=pn.pane.HoloViews(
        pd.DataFrame({'x':[1,2,3],'y':[0,0.5,1]}).set_index('x').hvplot(grid=True),
        name='test2',
        tags=['test2']
        
    ),
    df=pd.DataFrame({'x':[1,2,3],'y':[0,0.5,1]}).set_index('x'),
    name='test1',
    tags=['test1'],
)
test

Then, you can update the object’s df like so:

test.param.update(
    df=pd.DataFrame({'x':[1,2,3],'y':np.random.rand(3)}).set_index('x')
)

And the hvplot’s CDS will update accordingly and reflect in the plot updating as well:

@Marc

3 Likes