Skip to content

Commit

Permalink
Handle rerender of template
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Mar 29, 2021
1 parent 5ed9569 commit c12c40c
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 28 deletions.
2 changes: 2 additions & 0 deletions panel/models/reactive_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ def construct_data_model(parameterized, name=None, ignore=[]):
if pname in ignore:
continue
p = parameterized.param[pname]
if p.precedence and p.precedence < 0:
continue
prop = PARAM_MAPPING.get(type(p))
pname = parameterized._rename.get(pname, pname)
if pname == 'name':
Expand Down
42 changes: 25 additions & 17 deletions panel/models/reactive_html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ function extractToken(template: string, str: string, tokens: string[]) {
export class ReactiveHTMLView extends PanelHTMLBoxView {
model: ReactiveHTML
html: string
_parent: any = null
_changing: boolean = false
_event_listeners: any = {}
_mutation_observers: MutationObserver[] = []
Expand All @@ -97,7 +98,13 @@ export class ReactiveHTMLView extends PanelHTMLBoxView {

connect_signals(): void {
super.connect_signals()
this.connect(this.model.properties.children.change, () => this.rebuild())

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()
})
for (const prop in this.model.data.properties) {
this.connect(this.model.data.properties[prop].change, () => {
if (!this._changing) {
Expand Down Expand Up @@ -179,6 +186,15 @@ export class ReactiveHTMLView extends PanelHTMLBoxView {
this.model.trigger_event(new DOMEvent(elname, serialized))
}

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
view.renderTo(el)
}

private _render_children(): void {
const id = this.model.data.id
for (const node in this.model.children) {
Expand All @@ -190,37 +206,27 @@ export class ReactiveHTMLView extends PanelHTMLBoxView {
console.warn(`DOM node '${node}-${i}-${id}' could not be found. Cannot render children.`)
continue
}
const cm = children[i]
const view: any = this._child_views.get(cm)
if (view == null)
el.innerHTML = cm
else
view.renderTo(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.`)
continue
}
for (let i = 0; i < children.length; i++) {
const cm = children[i]
const view: any = this._child_views.get(cm)
if (view == null)
el.innerHTML = cm
else
view.renderTo(el)
}
for (const child of children)
this._render_child(child, el)
}
}
}

after_layout(): void {
super.after_layout()
for (const child_view of this.child_views) {
child_view.resize_layout()
this._align_view(child_view)
child_view.after_layout()
}
this._has_finished = true
}

private _align_view(view: any): void {
Expand All @@ -242,7 +248,6 @@ export class ReactiveHTMLView extends PanelHTMLBoxView {
view.el.style.marginTop = 'auto';
}


private _render_html(literal: any, state: any={}): any {
let htm = literal
let callbacks = ''
Expand Down Expand Up @@ -385,9 +390,12 @@ export class ReactiveHTMLView extends PanelHTMLBoxView {
}
}
}

try {
this._changing = true
this.model.data.setv(serialize_attrs(attrs))
} catch {
console.log('Could not serialize', attrs)
} finally {
this._changing = false
}
Expand Down
41 changes: 30 additions & 11 deletions panel/reactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,7 @@ class ReactiveHTMLMetaclass(ParameterizedMetaclass):
_name_counter = Counter()

def __init__(mcs, name, bases, dict_):
mcs.__original_doc__ = mcs.__doc__
ParameterizedMetaclass.__init__(mcs, name, bases, dict_)
for name, child_type in mcs._child_config.items():
if name not in mcs.param:
Expand Down Expand Up @@ -1130,7 +1131,15 @@ def __init__(self, **params):

def _cleanup(self, root):
for children_param in self._parser.children.values():
for child in getattr(self, children_param):
children = getattr(self, children_param)
mode = self._child_config.get(children_param)
if mode != 'model':
continue
if isinstance(children, dict):
children = children.values()
elif not isinstance(children, list):
children = [children]
for child in children:
child._cleanup(root)
super()._cleanup(root)

Expand All @@ -1153,7 +1162,7 @@ def _init_params(self):
}
data_params = {}
for k, v in self.param.get_param_values():
if k in ignored and k != 'name':
if (k in ignored and k != 'name') or ((self.param[k].precedence or 0) < 0):
continue
if isinstance(v, str):
v = bleach.clean(v)
Expand Down Expand Up @@ -1213,6 +1222,7 @@ def _get_children(self, doc, root, model, comm, old_children=None):
new_panes = getattr(self, children_param)
if not isinstance(new_panes, (list, dict)):
new_panes = [new_panes]
old_panes = [old_panes]
elif isinstance(new_panes, dict):
new_panes = new_panes.values()
for old_pane in old_panes:
Expand All @@ -1231,8 +1241,10 @@ def _get_children(self, doc, root, model, comm, old_children=None):
elif children_param in old_children:
# Find existing models
old_panes = old_children[children_param]
if not isinstance(old_panes, (list, dict)):
old_panes = [old_panes]
for i, pane in enumerate(new_panes):
if pane in old_panes:
if pane in old_panes and root.ref['id'] in pane._models:
child, _ = pane._models[root.ref['id']]
else:
child = pane._get_model(doc, root, model, comm)
Expand All @@ -1250,12 +1262,11 @@ def _get_children(self, doc, root, model, comm, old_children=None):
def _get_template(self):
import jinja2
template = jinja2.Template(self._template)
child_names = self._child_names
context = {}
for _, loop_param in self._parser.looped:
context[loop_param] = getattr(self, loop_param)
if loop_param in child_names:
context[f'{loop_param}_names'] = self._child_names[loop_param]
context = {'param': self.param, '__doc__': self.__original_doc__}
for parameter, value in self.param.get_param_values():
context[parameter] = value
if parameter in self._child_names:
context[f'{parameter}_names'] = self._child_names[parameter]
html = template.render(context)
parser = ReactiveHTMLParser(self.__class__)
parser.feed(html)
Expand Down Expand Up @@ -1323,16 +1334,24 @@ def _update_model(self, events, msg, root, model, doc, comm):
if prop in child_params:
new_children[prop] = prop
elif prop in Reactive.param:
model_msg[prop] = prop
model_msg[prop] = v
elif prop in self.param and (self.param[prop].precedence or 0) < 0:
continue
elif isinstance(v, str):
data_msg[prop] = bleach.clean(v)
else:
data_msg[prop] = v
if new_children:
old_children = {key: events[key].old for key in new_children}
model_msg['children'] = self._get_children(doc, root, model, comm, old_children)
if self._parser.looped:
model_msg['html'] = escape(self._get_template())
children = self._get_children(doc, root, model, comm, old_children)
else:
children = None
self._set_on_model(data_msg, root, model.data)
self._set_on_model(model_msg, root, model)
if children is not None:
self._set_on_model({'children': children}, root, model)

def on_event(self, node, event, callback):
"""
Expand Down

0 comments on commit c12c40c

Please sign in to comment.