Skip to content

Instantly share code, notes, and snippets.

@ged-odoo
Last active May 17, 2019 08:28
Show Gist options
  • Save ged-odoo/4f393dabcc7a255c14eff7d107b42d8f to your computer and use it in GitHub Desktop.
Save ged-odoo/4f393dabcc7a255c14eff7d107b42d8f to your computer and use it in GitHub Desktop.
Notes on t-widget
// This is a detailed explanation on the compiled code generated by the
// t-widget directive for a given template:
// <div><t t-widget="child" t-key="'somestring'" t-props="{flag:state.flag}"/></div>
//
// Hopefully, this may help someday some poor developer that has to maintain
// this code! Good luck, my friend...
// This is the virtual node representing the parent div
let c1 = [],
p1 = { key: 1 };
var vn1 = h("div", p1, c1);
// Beginning of the t-widget code
// we evaluate the expression given by t-key
let key5 = "somestring";
// We keep the index of the position of the widget in the closure. We push null to
// reserve the slot, and will replace it later by the widget vnode, when it will be
// ready (do not forget that preparing/rendering a widget is asynchronous)
let _2_index = c1.length;
c1.push(null);
// def3 is the deferred that will contain later either the new widget creation, or the
// props update...
let def3;
// this is kind of tricky: we need here to find if the widget was already created by
// a previous rendering. This is done by checking the internal `cmap` (children map)
// of the parent widget: it maps keys to widget ids, and, then, if there is an id,
// we look into the children list to get the instance
let w4 =
key5 in context.__owl__.cmap
? context.__owl__.children[context.__owl__.cmap[key5]]
: false;
// we evaluate here the props given to the component. It is done here to be able to
// easily reference it later, and also, it might be an expensive computation, so it
// is certainly better to do it only once
let props4 = { flag: context["state"].flag };
// If we have a widget, currently rendering, but not ready yet, and which was
// rendered with different props, we do not want to wait for it to be ready,
// then update it. We simply destroy it, and start anew.
if (
w4 &&
w4.__owl__.renderPromise &&
!w4.__owl__.isStarted &&
props4 !== w4.__owl__.renderProps
) {
w4.destroy();
w4 = false;
}
if (!w4) {
// in this situation, we need to create a new widget. First step is
// to get a reference to the class, then create an instance with
// current context as parent, and the props.
let W4 = context.widgets["child"];
if (!W4) {
throw new Error("Cannot find the definition of widget 'child'");
}
w4 = new W4(owner, props4);
// Whenever we rerender the parent widget, we need to be sure that we
// are able to find the widget instance. To do that, we register it to
// the parent cmap (children map). Note that the 'template' key is
// used here, since this is what identify the widget from the template
// perspective.
context.__owl__.cmap[key5] = w4.__owl__.id;
// _prepare is called, to basically call willStart, then render the
// widget
def3 = w4._prepare();
def3 = def3.then(vnode => {
// we create here a virtual node for the parent (NOT the widget). This
// means that the vdom of the parent will be stopped here, and from
// the parent's perspective, it simply is a vnode with no children.
// However, it shares the same dom element with the component root
// vnode.
let pvnode = h(vnode.sel, { key: key5 });
// we add hooks to the parent vnode so we can interact with the new
// widget at the proper time
pvnode.data.hook = {
insert(vn) {
// the _mount method will patch the widget vdom into the elm vn.elm,
// then call the mounted hooks. However, suprisingly, the snabbdom
// patch method actually replace the elm by a new elm, so we need
// to synchronise the pvnode elm with the resulting elm
let nvn = w4._mount(vnode, vn.elm);
pvnode.elm = nvn.elm;
},
remove() {
// apparently, in some cases, it is necessary to call the destroy
// method here
w4.destroy();
},
destroy() {
// and here...
w4.destroy();
}
};
// the pvnode is inserted at the correct position in the div's children
c1[_2_index] = pvnode;
// we keep here a reference to the parent vnode (representing the
// widget, so we can reuse it later whenever we update the widget
w4.__owl__.pvnode = pvnode;
});
} else {
// this is the 'update' path of the directive.
// the call to _updateProps is the actual widget update
def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue);
def3 = def3.then(() => {
// if widget was destroyed in the meantime, we do nothing (so, this
// means that the parent's element children list will have a null in
// the widget's position, which will cause the pvnode to be removed
// when it is patched.
if (w4.__owl__.isDestroyed) {
return;
}
// like above, we register the pvnode to the children list, so it
// will not be patched out of the dom.
let pvnode = w4.__owl__.pvnode;
c1[_2_index] = pvnode;
});
}
// we register the deferred here so the parent can coordinate its patch operation
// with all the children.
extra.promises.push(def3);
return vn1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment