[REF] component: use 'component' instead of 'widget'

This commit is contained in:
Géry Debongnie
2019-06-20 09:42:33 +02:00
parent 5524c2e323
commit e6a5934162
22 changed files with 553 additions and 586 deletions
+71 -71
View File
@@ -10,7 +10,7 @@ import { VNode } from "./vdom";
* - t-on
* - t-ref
* - t-transition
* - t-widget/t-keepalive
* - t-component/t-keepalive
* - t-mounted
* - t-slot
* - t-model
@@ -191,21 +191,21 @@ QWeb.addDirective({
});
//------------------------------------------------------------------------------
// t-widget
// t-component
//------------------------------------------------------------------------------
const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
const T_COMPONENT_MODS_CODE = Object.assign({}, MODS_CODE, {
self: "if (e.target !== vn.elm) {return}"
});
/**
* The t-widget directive is certainly a complicated and hard to maintain piece
* The t-component directive is certainly a complicated and hard to maintain piece
* of code. To help you, fellow developer, if you have to maintain it, I offer
* you this advice: Good luck...
*
* Since it is not 'direct' code, but rather code that generates other code, it
* is not easy to understand. To help you, here is a detailed and commented
* explanation of the code generated by the t-widget directive for the following
* explanation of the code generated by the t-component directive for the following
* situation:
* ```xml
* <Child
@@ -216,23 +216,23 @@ const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
*
* ```js
* // we assign utils on top of the function because it will be useful for
* // each widgets
* // each components
* let utils = this.utils;
*
* // this is the virtual node representing the parent div
* let c1 = [], p1 = { key: 1 };
* var vn1 = h("div", p1, c1);
*
* // t-widget directive: we start by evaluating the expression given by t-key:
* // t-component directive: we start by evaluating the expression given by t-key:
* let key5 = "somestring";
*
* // def3 is the promise that will contain later either the new widget
* // def3 is the promise that will contain later either the new component
* // creation, or the props update...
* let def3;
*
* // this is kind of tricky: we need here to find if the widget was already
* // this is kind of tricky: we need here to find if the component 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,
* // `cmap` (children map) of the parent component: it maps keys to component ids,
* // and, then, if there is an id, we look into the children list to get the
* // instance
* let w4 =
@@ -240,9 +240,9 @@ const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
* ? context.__owl__.children[context.__owl__.cmap[key5]]
* : false;
*
* // 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
* // We keep the index of the position of the component in the closure. We push
* // null to reserve the slot, and will replace it later by the component vnode,
* // when it will be ready (do not forget that preparing/rendering a component is
* // asynchronous)
* let _2_index = c1.length;
* c1.push(null);
@@ -252,7 +252,7 @@ const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
* // 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, we do not want
* // If we have a component, currently rendering, but not ready yet, we do not want
* // to wait for it to be ready if we can avoid it
* if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode) {
* // we check if the props are the same. In that case, we can simply reuse
@@ -260,7 +260,7 @@ const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
* if (utils.shallowEqual(props4, w4.__owl__.renderProps)) {
* def3 = w4.__owl__.renderPromise;
* } else {
* // if the props are not the same, we destroy the widget and starts anew.
* // if the props are not the same, we destroy the component and starts anew.
* // this will be faster than waiting for its rendering, then updating it
* w4.destroy();
* w4 = false;
@@ -268,29 +268,29 @@ const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
* }
*
* if (!w4) {
* // in this situation, we need to create a new widget. First step is
* // in this situation, we need to create a new component. 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 && context.widgets[widgetKey4] || QWeb.widgets[widgetKey4];
* let W4 = context.component && context.components[componentKey4] || QWeb.component[componentKey4];
* if (!W4) {
* throw new Error("Cannot find the definition of widget 'child'");
* throw new Error("Cannot find the definition of component '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
* // Whenever we rerender the parent component, we need to be sure that we
* // are able to find the component 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
* // used here, since this is what identify the component from the template
* // perspective.
* context.__owl__.cmap[key5] = w4.__owl__.id;
*
* // _prepare is called, to basically call willStart, then render the
* // widget
* // component
* def3 = w4._prepare();
*
* def3 = def3.then(vnode => {
* // we create here a virtual node for the parent (NOT the widget). This
* // we create here a virtual node for the parent (NOT the component). 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
@@ -298,16 +298,16 @@ const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
* 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
* // component at the proper time
* pvnode.data.hook = {
* insert(vn) {
* // the _mount method will patch the widget vdom into the elm vn.elm,
* // the _mount method will patch the component 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;
* // what follows is only present if there are animations on the widget
* // what follows is only present if there are animations on the component
* utils.transitionInsert(vn, "fade");
* },
* remove() {
@@ -317,7 +317,7 @@ const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
* },
* destroy() {
* // if there are animations, we delay the call to destroy on the
* // widget, if not, we call it directly.
* // component, if not, we call it directly.
* let finalize = () => {
* w4.destroy();
* };
@@ -328,19 +328,19 @@ const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
* 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
* // component, so we can reuse it later whenever we update the component
* w4.__owl__.pvnode = pvnode;
* });
* } else {
* // this is the 'update' path of the directive.
* // the call to _updateProps is the actual widget update
* // the call to _updateProps is the actual component update
* // Note that we only update the props if we cannot reuse the previous
* // rendering work (in the case it was rendered with the same props)
* def3 = def3 || w4._updateProps(props4, extra.forceUpdate, extra.patchQueue);
* def3 = def3.then(() => {
* // if widget was destroyed in the meantime, we do nothing (so, this
* // if component 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
* // the component's position, which will cause the pvnode to be removed
* // when it is patched.
* if (w4.__owl__.isDestroyed) {
* return;
@@ -360,11 +360,11 @@ const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
*/
QWeb.addDirective({
name: "widget",
name: "component",
extraNames: ["props", "keepalive", "asyncroot"],
priority: 100,
atNodeEncounter({ ctx, value, node, qweb }): boolean {
ctx.addLine("//WIDGET");
ctx.addLine("//COMPONENT");
ctx.rootContext.shouldDefineOwner = true;
ctx.rootContext.shouldDefineQWeb = true;
ctx.rootContext.shouldDefineUtils = true;
@@ -408,7 +408,7 @@ QWeb.addDirective({
.join(",");
let dummyID = ctx.generateID();
let defID = ctx.generateID();
let widgetID = ctx.generateID();
let componentID = ctx.generateID();
let keyID = key && ctx.generateID();
if (key) {
// we bind a variable to the key (could be a complex expression, so we
@@ -419,8 +419,8 @@ QWeb.addDirective({
let templateID = key
? `key${keyID}`
: ctx.inLoop
? `String(-${widgetID} - i)`
: String(widgetID);
? `String(-${componentID} - i)`
: String(componentID);
let ref = node.getAttribute("t-ref");
let refExpr = "";
@@ -428,21 +428,21 @@ QWeb.addDirective({
if (ref) {
refKey = `ref${ctx.generateID()}`;
ctx.addLine(`const ${refKey} = ${ctx.interpolate(ref)};`);
refExpr = `context.refs[${refKey}] = w${widgetID};`;
refExpr = `context.refs[${refKey}] = w${componentID};`;
}
let transitionsInsertCode = "";
if (transition) {
transitionsInsertCode = `utils.transitionInsert(vn, '${transition}');`;
}
let finalizeWidgetCode = `w${widgetID}.${
let finalizeComponentCode = `w${componentID}.${
keepAlive ? "unmount" : "destroy"
}();`;
if (ref && !keepAlive) {
finalizeWidgetCode += `delete context.refs[${refKey}];`;
finalizeComponentCode += `delete context.refs[${refKey}];`;
}
if (transition) {
finalizeWidgetCode = `let finalize = () => {
${finalizeWidgetCode}
finalizeComponentCode = `let finalize = () => {
${finalizeComponentCode}
};
utils.transitionRemove(vn, '${transition}', finalize);`;
}
@@ -475,7 +475,7 @@ QWeb.addDirective({
vn.elm.classList.add(k);
}
}`;
updateClassCode = `let cl=w${widgetID}.el.classList;for (let k in ${attVar}) {if (${attVar}[k]) {cl.add(k)} else {cl.remove(k)}}`;
updateClassCode = `let cl=w${componentID}.el.classList;for (let k in ${attVar}) {if (${attVar}[k]) {cl.add(k)} else {cl.remove(k)}}`;
}
let eventsCode = events
.map(function([eventName, mods, handlerName, extraArgs]) {
@@ -487,7 +487,7 @@ QWeb.addDirective({
handler = `function (e) {`;
handler += mods
.map(function(mod) {
return T_WIDGET_MODS_CODE[mod];
return T_COMPONENT_MODS_CODE[mod];
})
.join("");
handler += `owner['${handlerName}'].call(${params}, e);}`;
@@ -503,47 +503,47 @@ QWeb.addDirective({
}
ctx.addLine(
`let w${widgetID} = ${templateID} in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[${templateID}]] : false;`
`let w${componentID} = ${templateID} in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[${templateID}]] : false;`
);
ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`);
if (async) {
ctx.addLine(`const patchQueue${widgetID} = [];`);
ctx.addLine(`const patchQueue${componentID} = [];`);
ctx.addLine(
`c${
ctx.parentNode
}.push(w${widgetID} && w${widgetID}.__owl__.pvnode || null);`
}.push(w${componentID} && w${componentID}.__owl__.pvnode || null);`
);
} else {
ctx.addLine(`c${ctx.parentNode}.push(null);`);
}
ctx.addLine(`let props${widgetID} = {${propStr}};`);
ctx.addLine(`let props${componentID} = {${propStr}};`);
ctx.addIf(
`w${widgetID} && w${widgetID}.__owl__.renderPromise && !w${widgetID}.__owl__.vnode`
`w${componentID} && w${componentID}.__owl__.renderPromise && !w${componentID}.__owl__.vnode`
);
ctx.addIf(
`utils.shallowEqual(props${widgetID}, w${widgetID}.__owl__.renderProps)`
`utils.shallowEqual(props${componentID}, w${componentID}.__owl__.renderProps)`
);
ctx.addLine(`def${defID} = w${widgetID}.__owl__.renderPromise;`);
ctx.addLine(`def${defID} = w${componentID}.__owl__.renderPromise;`);
ctx.addElse();
ctx.addLine(`w${widgetID}.destroy();`);
ctx.addLine(`w${widgetID} = false;`);
ctx.addLine(`w${componentID}.destroy();`);
ctx.addLine(`w${componentID} = false;`);
ctx.closeIf();
ctx.closeIf();
ctx.addIf(`!w${widgetID}`);
// new widget
ctx.addLine(`let widgetKey${widgetID} = ${ctx.interpolate(value)};`);
ctx.addIf(`!w${componentID}`);
// new component
ctx.addLine(`let componentKey${componentID} = ${ctx.interpolate(value)};`);
ctx.addLine(
`let W${widgetID} = context.widgets && context.widgets[widgetKey${widgetID}] || QWeb.widgets[widgetKey${widgetID}];`
`let W${componentID} = context.components && context.components[componentKey${componentID}] || QWeb.components[componentKey${componentID}];`
);
// maybe only do this in dev mode...
ctx.addLine(
`if (!W${widgetID}) {throw new Error('Cannot find the definition of widget "' + widgetKey${widgetID} + '"')}`
`if (!W${componentID}) {throw new Error('Cannot find the definition of component "' + componentKey${componentID} + '"')}`
);
ctx.addLine(`w${widgetID} = new W${widgetID}(owner, props${widgetID});`);
ctx.addLine(`w${componentID} = new W${componentID}(owner, props${componentID});`);
ctx.addLine(
`context.__owl__.cmap[${templateID}] = w${widgetID}.__owl__.id;`
`context.__owl__.cmap[${templateID}] = w${componentID}.__owl__.id;`
);
// SLOTS
@@ -551,7 +551,7 @@ QWeb.addDirective({
const clone = <Element>node.cloneNode(true);
const slotNodes = clone.querySelectorAll("[t-set]");
const slotId = qweb.nextSlotId++;
ctx.addLine(`w${widgetID}.__owl__.slotId = ${slotId};`);
ctx.addLine(`w${componentID}.__owl__.slotId = ${slotId};`);
if (slotNodes.length) {
for (let i = 0, length = slotNodes.length; i < length; i++) {
const slotNode = slotNodes[i];
@@ -569,28 +569,28 @@ QWeb.addDirective({
}
}
ctx.addLine(`def${defID} = w${widgetID}._prepare();`);
ctx.addLine(`def${defID} = w${componentID}._prepare();`);
// hack: specify empty remove hook to prevent the node from being removed from the DOM
ctx.addLine(
`def${defID} = def${defID}.then(vnode=>{${createHook}let pvnode=h(vnode.sel, {key: ${templateID}, hook: {insert(vn) {let nvn=w${widgetID}._mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeWidgetCode}}}});c${
`def${defID} = def${defID}.then(vnode=>{${createHook}let pvnode=h(vnode.sel, {key: ${templateID}, hook: {insert(vn) {let nvn=w${componentID}._mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeComponentCode}}}});c${
ctx.parentNode
}[_${dummyID}_index]=pvnode;w${widgetID}.__owl__.pvnode = pvnode;});`
}[_${dummyID}_index]=pvnode;w${componentID}.__owl__.pvnode = pvnode;});`
);
ctx.addElse();
// need to update widget
const patchQueueCode = async ? `patchQueue${widgetID}` : "extra.patchQueue";
// need to update component
const patchQueueCode = async ? `patchQueue${componentID}` : "extra.patchQueue";
ctx.addLine(
`def${defID} = def${defID} || w${widgetID}._updateProps(props${widgetID}, extra.forceUpdate, ${patchQueueCode});`
`def${defID} = def${defID} || w${componentID}._updateProps(props${componentID}, extra.forceUpdate, ${patchQueueCode});`
);
let keepAliveCode = "";
if (keepAlive) {
keepAliveCode = `pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w${widgetID}.el,vn.elm);vn.elm=w${widgetID}.el;w${widgetID}._remount();};`;
keepAliveCode = `pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w${componentID}.el,vn.elm);vn.elm=w${componentID}.el;w${componentID}._remount();};`;
}
ctx.addLine(
`def${defID} = def${defID}.then(()=>{if (w${widgetID}.__owl__.isDestroyed) {return};${
tattStyle ? `w${widgetID}.el.style=${tattStyle};` : ""
}${updateClassCode}let pvnode=w${widgetID}.__owl__.pvnode;${keepAliveCode}c${
`def${defID} = def${defID}.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};${
tattStyle ? `w${componentID}.el.style=${tattStyle};` : ""
}${updateClassCode}let pvnode=w${componentID}.__owl__.pvnode;${keepAliveCode}c${
ctx.parentNode
}[_${dummyID}_index]=pvnode;});`
);
@@ -598,7 +598,7 @@ QWeb.addDirective({
if (async) {
ctx.addLine(
`def${defID}.then(w${widgetID}._applyPatchQueue.bind(w${widgetID}, patchQueue${widgetID}));`
`def${defID}.then(w${componentID}._applyPatchQueue.bind(w${componentID}, patchQueue${componentID}));`
);
} else {
ctx.addLine(`extra.promises.push(def${defID});`);