diff --git a/panel/models/reactive_html.py b/panel/models/reactive_html.py
index c7f03f6a9a..416943db21 100644
--- a/panel/models/reactive_html.py
+++ b/panel/models/reactive_html.py
@@ -159,7 +159,7 @@ def find_attrs(html):
}
-def construct_data_model(parameterized, name=None, ignore=[]):
+def construct_data_model(parameterized, name=None, ignore=[], types={}):
properties = {}
for pname in parameterized.param:
if pname in ignore:
@@ -167,7 +167,8 @@ def construct_data_model(parameterized, name=None, ignore=[]):
p = parameterized.param[pname]
if p.precedence and p.precedence < 0:
continue
- prop = PARAM_MAPPING.get(type(p))
+ ptype = types.get(pname, type(p))
+ prop = PARAM_MAPPING.get(ptype)
pname = parameterized._rename.get(pname, pname)
if pname == 'name' or pname is None:
continue
@@ -199,7 +200,7 @@ class ReactiveHTML(HTMLBox):
callbacks = bp.Dict(bp.String, bp.List(bp.Tuple(bp.String, bp.String)))
- children = bp.Dict(bp.String, bp.List(bp.Either(bp.Instance(LayoutDOM), bp.String)))
+ children = bp.Dict(bp.String, bp.Either(bp.List(bp.Either(bp.Instance(LayoutDOM), bp.String)), bp.String))
data = bp.Instance(DataModel)
diff --git a/panel/models/reactive_html.ts b/panel/models/reactive_html.ts
index 001de05ad9..a8f08cefcd 100644
--- a/panel/models/reactive_html.ts
+++ b/panel/models/reactive_html.ts
@@ -103,14 +103,21 @@ export class ReactiveHTMLView extends PanelHTMLBoxView {
this.connect(this.model.properties.children.change, async () => {
this.html = htmlDecode(this.model.html) || this.model.html
await this.rebuild()
- if (this._parent != null)
- this._parent.invalidate_layout()
+ this.invalidate_layout()
})
for (const prop in this.model.data.properties) {
this.connect(this.model.data.properties[prop].change, () => {
- if (!this._changing) {
- this._update(prop)
- this.invalidate_layout()
+ for (const node in this.model.children) {
+ if (this.model.children[node] == prop) {
+ let children = this.model.data[prop]
+ if (!isArray(children))
+ children = [children]
+ this._render_node(node, children)
+ this.invalidate_layout()
+ } else if (!this._changing) {
+ this._update(prop)
+ this.invalidate_layout()
+ }
}
})
}
@@ -195,35 +202,42 @@ export class ReactiveHTMLView extends PanelHTMLBoxView {
private _render_child(model: any, el: Element): void {
const view: any = this._child_views.get(model)
- view._parent = this
if (view == null)
el.innerHTML = model
- else
+ else {
+ view._parent = view
view.renderTo(el)
+ }
}
- private _render_children(): void {
+ _render_node(node: any, children: any[]): void {
const id = this.model.data.id
- for (const node in this.model.children) {
- const children = this.model.children[node]
- if (this.model.looped.indexOf(node) > -1) {
- for (let i = 0; i < children.length; i++) {
- let el: any = document.getElementById(`${node}-${i}-${id}`)
- if (el == null) {
- console.warn(`DOM node '${node}-${i}-${id}' could not be found. Cannot render children.`)
- continue
- }
- this._render_child(children[i], el)
- }
- } else {
- let el: any = document.getElementById(`${node}-${id}`)
+ if (this.model.looped.indexOf(node) > -1) {
+ for (let i = 0; i < children.length; i++) {
+ let el: any = document.getElementById(`${node}-${i}-${id}`)
if (el == null) {
- console.warn(`DOM node '${node}-${id}' could not be found. Cannot render children.`)
+ console.warn(`DOM node '${node}-${i}-${id}' could not be found. Cannot render children.`)
continue
}
- for (const child of children)
- this._render_child(child, el)
+ this._render_child(children[i], el)
}
+ } else {
+ let el: any = document.getElementById(`${node}-${id}`)
+ if (el == null) {
+ console.warn(`DOM node '${node}-${id}' could not be found. Cannot render children.`)
+ return
+ }
+ for (const child of children)
+ this._render_child(child, el)
+ }
+ }
+
+ private _render_children(): void {
+ for (const node in this.model.children) {
+ let children = this.model.data[prop]
+ if (!isArray(children))
+ children = [children]
+ this._render_node(node, children)
}
}
diff --git a/panel/reactive.py b/panel/reactive.py
index 0630ff6864..4bf51c6828 100644
--- a/panel/reactive.py
+++ b/panel/reactive.py
@@ -996,13 +996,21 @@ def __init__(mcs, name, bases, dict_):
if node not in mcs._attrs:
mcs._attrs[node] = []
mcs._attrs[node].append((attr, param_attrs, template))
- ignored = list(Reactive.param)+list(mcs._parser.children.values())
+ ignored = list(Reactive.param)
+ types = {}
+ for child in mcs._parser.children.values():
+ if mcs._child_config.get(child) == 'literal':
+ types[child] = param.String
+ else:
+ ignored.append(child)
ignored.remove('name')
# Create model with unique name
ReactiveHTMLMetaclass._name_counter[name] += 1
model_name = f'{name}{ReactiveHTMLMetaclass._name_counter[name]}'
- mcs._data_model = construct_data_model(mcs, name=model_name, ignore=ignored)
+ mcs._data_model = construct_data_model(
+ mcs, name=model_name, ignore=ignored, types=types
+ )
@@ -1174,7 +1182,10 @@ def _process_children(self, doc, root, model, comm, children):
return children
def _init_params(self):
- ignored = list(Reactive.param)+list(self._parser.children.values())
+ ignored = list(Reactive.param)
+ for child in self._parser.children.values():
+ if self._child_config.get(child) != 'literal':
+ ignored.append(child)
params = {
p : getattr(self, p) for p in list(Layoutable.param)
if getattr(self, p) is not None and p != 'name'
@@ -1223,6 +1234,7 @@ def _get_children(self, doc, root, model, comm, old_children=None):
for parent, children_param in self._parser.children.items():
mode = self._child_config.get(children_param, 'model')
if mode == 'literal':
+ new_panes[parent] = None
continue
panes = getattr(self, children_param)
if isinstance(panes, dict):
@@ -1256,7 +1268,7 @@ def _get_children(self, doc, root, model, comm, old_children=None):
child_panes = child_panes.values()
mode = self._child_config.get(children_param, 'model')
if mode == 'literal':
- new_models[parent] = child_panes
+ new_models[parent] = children_param
elif children_param in old_children:
# Find existing models
old_panes = old_children[children_param]
@@ -1397,6 +1409,8 @@ def _update_model(self, events, msg, root, model, doc, comm):
for prop, v in list(msg.items()):
if prop in child_params:
new_children[prop] = prop
+ if self._child_config.get(prop) == 'literal':
+ data_msg[prop] = bleach.clean(v)
elif prop in list(Reactive.param)+['events']:
model_msg[prop] = v
elif prop in self.param and (self.param[prop].precedence or 0) < 0: