[FIX] qweb/component: handle nested t-foreach and components

The way keys were handled in QWeb was mostly ok, but a little naive. It
is fine when we deal with a list of dom nodes, since the reconciliation
algorithm need only to be able to differentiate/reconcile nodes in that
list, but it is an issue with components, which needs to globally be
able to find themselves in their parent's children map.

Because of the way this was handled, there were situations were Owl
internal virtual dom would crash, since components wrongly assumed that
they were already rendered in a different place.

To fix this, we generalize the way keys are generated, by concatenating
all sub keys coming from each iteration loops.

closes #584
This commit is contained in:
Géry Debongnie
2019-12-16 16:50:21 +01:00
committed by aab-odoo
parent 9216c5c24b
commit 6465665550
9 changed files with 716 additions and 507 deletions
+8 -1
View File
@@ -265,7 +265,7 @@ QWeb.addDirective({
// ------------------------------------------------ // ------------------------------------------------
const callingScope = hasBody ? "scope" : "Object.assign(Object.create(context), scope)"; const callingScope = hasBody ? "scope" : "Object.assign(Object.create(context), scope)";
const parentComponent = `utils.getComponent(context)`; const parentComponent = `utils.getComponent(context)`;
const keyCode = ctx.loopNumber ? `, key: ${ctx.generateTemplateKey()}` : ""; const keyCode = ctx.loopNumber || ctx.hasKey0 ? `, key: ${ctx.generateTemplateKey()}` : "";
const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result"; const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result";
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}${keyCode}})`; const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}${keyCode}})`;
if (ctx.parentNode) { if (ctx.parentNode) {
@@ -334,6 +334,13 @@ QWeb.addDirective({
`Directive t-foreach should always be used with a t-key! (in template: '${ctx.templateName}')` `Directive t-foreach should always be used with a t-key! (in template: '${ctx.templateName}')`
); );
} }
if (nodeCopy.hasAttribute("t-key")) {
const expr = ctx.formatExpression(nodeCopy.getAttribute("t-key")!);
ctx.addLine(`let key${ctx.loopNumber} = ${expr};`);
nodeCopy.removeAttribute("t-key");
} else {
ctx.addLine(`let key${ctx.loopNumber} = i${ctx.loopNumber};`);
}
nodeCopy.removeAttribute("t-foreach"); nodeCopy.removeAttribute("t-foreach");
qweb._compileNode(nodeCopy, ctx); qweb._compileNode(nodeCopy, ctx);
+10 -14
View File
@@ -26,7 +26,8 @@ export class CompilationContext {
templateName: string; templateName: string;
allowMultipleRoots: boolean = false; allowMultipleRoots: boolean = false;
hasParentWidget: boolean = false; hasParentWidget: boolean = false;
currentKey: string = ""; hasKey0: boolean = false;
keyStack: boolean[] = [];
constructor(name?: string) { constructor(name?: string) {
this.rootContext = this; this.rootContext = this;
@@ -48,20 +49,15 @@ export class CompilationContext {
*/ */
generateTemplateKey(prefix: string = ""): string { generateTemplateKey(prefix: string = ""): string {
const id = this.generateID(); const id = this.generateID();
if (this.loopNumber === 0 && !this.currentKey) { if (this.loopNumber === 0 && !this.hasKey0) {
return `'${prefix}__${id}__'`; return `'${prefix}__${id}__'`;
} }
let locationExpr = `\`${prefix}__${id}__`; let key = `\`${prefix}__${id}__`;
for (let i = 0; i < this.loopNumber - 1; i++) { let start = this.hasKey0 ? 0 : 1;
locationExpr += `\${i${i + 1}}__`; for (let i = start; i < this.loopNumber + 1; i++) {
} key += `\${key${i}}__`;
if (this.currentKey) {
const k = this.currentKey;
this.addLine(`let k${id} = ${locationExpr}\` + ${k};`);
} else {
locationExpr += this.loopNumber ? `\${i${this.loopNumber}}__\`` : "`";
this.addLine(`let k${id} = ${locationExpr};`);
} }
this.addLine(`let k${id} = ${key}\`;`);
return `k${id}`; return `k${id}`;
} }
@@ -116,11 +112,11 @@ export class CompilationContext {
} }
indent() { indent() {
this.indentLevel++; this.rootContext.indentLevel++;
} }
dedent() { dedent() {
this.indentLevel--; this.rootContext.indentLevel--;
} }
addLine(line: string): number { addLine(line: string): number {
+15 -4
View File
@@ -303,9 +303,20 @@ QWeb.addDirective({
QWeb.addDirective({ QWeb.addDirective({
name: "key", name: "key",
priority: 45, priority: 45,
atNodeEncounter({ ctx, value }) { atNodeEncounter({ ctx, value, node }) {
let id = ctx.generateID(); if (ctx.loopNumber === 0) {
ctx.addLine(`const nodeKey${id} = ${ctx.formatExpression(value)};`); ctx.keyStack.push(ctx.rootContext.hasKey0);
ctx.currentKey = `nodeKey${id}`; ctx.rootContext.hasKey0 = true;
}
ctx.addLine("{");
ctx.indent();
ctx.addLine(`let key${ctx.loopNumber} = ${ctx.formatExpression(value)};`);
},
finalize({ ctx }) {
ctx.dedent();
ctx.addLine("}");
if (ctx.loopNumber === 0) {
ctx.rootContext.hasKey0 = ctx.keyStack.pop() as boolean;
}
} }
}); });
+7 -7
View File
@@ -402,8 +402,8 @@ export class QWeb extends EventBus {
ctx.shouldDefineResult = false; ctx.shouldDefineResult = false;
ctx.addLine(`let c${ctx.parentNode} = extra.parentNode;`); ctx.addLine(`let c${ctx.parentNode} = extra.parentNode;`);
if (defineKey) { if (defineKey) {
ctx.currentKey = `key${ctx.generateID()}`; ctx.addLine(`let key0 = extra.key || "";`);
ctx.addLine(`let ${ctx.currentKey} = extra.key || '';`); ctx.hasKey0 = true;
} }
} }
this._compileNode(elem, ctx); this._compileNode(elem, ctx);
@@ -482,9 +482,6 @@ export class QWeb extends EventBus {
} }
return; return;
} }
if (ctx !== ctx.rootContext) {
ctx = ctx.subContext("currentKey", ctx.currentKey);
}
const firstLetter = node.tagName[0]; const firstLetter = node.tagName[0];
if (firstLetter === firstLetter.toUpperCase()) { if (firstLetter === firstLetter.toUpperCase()) {
@@ -759,8 +756,8 @@ export class QWeb extends EventBus {
} }
} }
let nodeID = ctx.generateID(); let nodeID = ctx.generateID();
let nodeKey = ctx.currentKey || nodeID; let key = ctx.loopNumber || ctx.hasKey0 ? `\`\${key${ctx.loopNumber}}_${nodeID}\`` : nodeID;
const parts = [`key:${nodeKey}`]; const parts = [`key:${key}`];
if (attrs.length + tattrs.length > 0) { if (attrs.length + tattrs.length > 0) {
parts.push(`attrs:{${attrs.join(",")}}`); parts.push(`attrs:{${attrs.join(",")}}`);
} }
@@ -789,6 +786,9 @@ export class QWeb extends EventBus {
ctx.addLine(`let vn${nodeID} = h('${node.nodeName}', p${nodeID}, c${nodeID});`); ctx.addLine(`let vn${nodeID} = h('${node.nodeName}', p${nodeID}, c${nodeID});`);
if (ctx.parentNode) { if (ctx.parentNode) {
ctx.addLine(`c${ctx.parentNode}.push(vn${nodeID});`); ctx.addLine(`c${ctx.parentNode}.push(vn${nodeID});`);
} else if (ctx.loopNumber || ctx.hasKey0) {
ctx.rootContext.shouldDefineResult = true;
ctx.addLine(`result = vn${nodeID};`);
} }
return nodeID; return nodeID;
@@ -27,6 +27,7 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
scope.section_index = i1 scope.section_index = i1
scope.section = _3[i1] scope.section = _3[i1]
scope.section_value = _4[i1] scope.section_value = _4[i1]
let key1 = i1;
let _6 = scope['section'].blips; let _6 = scope['section'].blips;
if (!_6) { throw new Error('QWeb error: Invalid loop expression')} if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
let _7 = _8 = _6; let _7 = _8 = _6;
@@ -43,8 +44,9 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
scope.blip_index = i2 scope.blip_index = i2
scope.blip = _7[i2] scope.blip = _7[i2]
scope.blip_value = _8[i2] scope.blip_value = _8[i2]
let key2 = i2;
// Component 'Child' // Component 'Child'
let k11 = \`__11__\${i1}__\${i2}__\`; let k11 = \`__11__\${key1}__\${key2}__\`;
let w10 = k11 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k11]] : false; let w10 = k11 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k11]] : false;
let props10 = {blip:scope['blip']}; let props10 = {blip:scope['blip']};
if (w10 && w10.__owl__.currentFiber && !w10.__owl__.vnode) { if (w10 && w10.__owl__.currentFiber && !w10.__owl__.vnode) {
@@ -75,6 +77,81 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
}" }"
`; `;
exports[`basic widget properties same t-keys in two different places 1`] = `
"function anonymous(context, extra
) {
// Template name: \\"__template__2\\"
let utils = this.constructor.utils;
let QWeb = this.constructor;
let parent = context;
let scope = Object.create(context);
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
let c2 = [], p2 = {key:2};
let vn2 = h('div', p2, c2);
c1.push(vn2);
{
let key0 = 1;
// Component 'Child'
let k4 = \`__4__\${key0}__\`;
let w3 = k4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k4]] : false;
let props3 = {blip:'1'};
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
w3.destroy();
w3 = false;
}
if (w3) {
w3.__updateProps(props3, extra.fiber, undefined);
let pvnode = w3.__owl__.pvnode;
c2.push(pvnode);
} else {
let componentKey3 = \`Child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| scope['Child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
w3 = new W3(parent, props3);
parent.__owl__.cmap[k4] = w3.__owl__.id;
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
c2.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
w3.__owl__.parentLastFiberId = extra.fiber.id;
}
let c5 = [], p5 = {key:5};
let vn5 = h('div', p5, c5);
c1.push(vn5);
{
let key0 = 1;
// Component 'Child'
let k7 = \`__7__\${key0}__\`;
let w6 = k7 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k7]] : false;
let props6 = {blip:'2'};
if (w6 && w6.__owl__.currentFiber && !w6.__owl__.vnode) {
w6.destroy();
w6 = false;
}
if (w6) {
w6.__updateProps(props6, extra.fiber, undefined);
let pvnode = w6.__owl__.pvnode;
c5.push(pvnode);
} else {
let componentKey6 = \`Child\`;
let W6 = context.constructor.components[componentKey6] || QWeb.components[componentKey6]|| scope['Child'];
if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')}
w6 = new W6(parent, props6);
parent.__owl__.cmap[k7] = w6.__owl__.id;
let fiber = w6.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k7, hook: {remove() {},destroy(vn) {w6.destroy();}}});
c5.push(pvnode);
w6.__owl__.pvnode = pvnode;
}
w6.__owl__.parentLastFiberId = extra.fiber.id;
}
return vn1;
}"
`;
exports[`basic widget properties t-key on a component with t-if, and a sibling component 1`] = ` exports[`basic widget properties t-key on a component with t-if, and a sibling component 1`] = `
"function anonymous(context, extra "function anonymous(context, extra
) { ) {
@@ -87,55 +164,57 @@ exports[`basic widget properties t-key on a component with t-if, and a sibling c
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
if (false) { if (false) {
const nodeKey2 = 'str'; {
// Component 'Child' let key0 = 'str';
let k4 = \`__4__\` + nodeKey2; // Component 'Child'
let w3 = k4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k4]] : false; let k3 = \`__3__\${key0}__\`;
let props3 = {}; let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { let props2 = {};
w3.destroy(); if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
w3 = false; w2.destroy();
w2 = false;
}
if (w2) {
w2.__updateProps(props2, extra.fiber, undefined);
let pvnode = w2.__owl__.pvnode;
c1.push(pvnode);
} else {
let componentKey2 = \`Child\`;
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
w2 = new W2(parent, props2);
parent.__owl__.cmap[k3] = w2.__owl__.id;
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
c1.push(pvnode);
w2.__owl__.pvnode = pvnode;
}
w2.__owl__.parentLastFiberId = extra.fiber.id;
} }
if (w3) {
w3.__updateProps(props3, extra.fiber, undefined);
let pvnode = w3.__owl__.pvnode;
c1.push(pvnode);
} else {
let componentKey3 = \`Child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| scope['Child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
w3 = new W3(parent, props3);
parent.__owl__.cmap[k4] = w3.__owl__.id;
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
w3.__owl__.parentLastFiberId = extra.fiber.id;
} }
// Component 'Child' // Component 'Child'
let w5 = '__6__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__6__']] : false; let w4 = '__5__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__5__']] : false;
let props5 = {}; let props4 = {};
if (w5 && w5.__owl__.currentFiber && !w5.__owl__.vnode) { if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) {
w5.destroy(); w4.destroy();
w5 = false; w4 = false;
} }
if (w5) { if (w4) {
w5.__updateProps(props5, extra.fiber, undefined); w4.__updateProps(props4, extra.fiber, undefined);
let pvnode = w5.__owl__.pvnode; let pvnode = w4.__owl__.pvnode;
c1.push(pvnode); c1.push(pvnode);
} else { } else {
let componentKey5 = \`Child\`; let componentKey4 = \`Child\`;
let W5 = context.constructor.components[componentKey5] || QWeb.components[componentKey5]|| scope['Child']; let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| scope['Child'];
if (!W5) {throw new Error('Cannot find the definition of component \\"' + componentKey5 + '\\"')} if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
w5 = new W5(parent, props5); w4 = new W4(parent, props4);
parent.__owl__.cmap['__6__'] = w5.__owl__.id; parent.__owl__.cmap['__5__'] = w4.__owl__.id;
let fiber = w5.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w4.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: '__6__', hook: {remove() {},destroy(vn) {w5.destroy();}}}); let pvnode = h('dummy', {key: '__5__', hook: {remove() {},destroy(vn) {w4.destroy();}}});
c1.push(pvnode); c1.push(pvnode);
w5.__owl__.pvnode = pvnode; w4.__owl__.pvnode = pvnode;
} }
w5.__owl__.parentLastFiberId = extra.fiber.id; w4.__owl__.parentLastFiberId = extra.fiber.id;
return vn1; return vn1;
}" }"
`; `;
@@ -322,31 +401,34 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
scope.number_index = i1 scope.number_index = i1
scope.number = _3[i1] scope.number = _3[i1]
scope.number_value = _4[i1] scope.number_value = _4[i1]
const nodeKey6 = scope['number']; let key1 = i1;
// Component 'ChildWidget' {
let k8 = \`__8__\` + nodeKey6; let key1 = scope['number'];
let w7 = k8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k8]] : false; // Component 'ChildWidget'
let props7 = {}; let k7 = \`__7__\${key1}__\`;
if (w7 && w7.__owl__.currentFiber && !w7.__owl__.vnode) { let w6 = k7 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k7]] : false;
w7.destroy(); let props6 = {};
w7 = false; if (w6 && w6.__owl__.currentFiber && !w6.__owl__.vnode) {
w6.destroy();
w6 = false;
}
if (w6) {
w6.__updateProps(props6, extra.fiber, undefined);
let pvnode = w6.__owl__.pvnode;
c1.push(pvnode);
} else {
let componentKey6 = \`ChildWidget\`;
let W6 = context.constructor.components[componentKey6] || QWeb.components[componentKey6]|| scope['ChildWidget'];
if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')}
w6 = new W6(parent, props6);
parent.__owl__.cmap[k7] = w6.__owl__.id;
let fiber = w6.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k7, hook: {remove() {},destroy(vn) {w6.destroy();}}});
c1.push(pvnode);
w6.__owl__.pvnode = pvnode;
}
w6.__owl__.parentLastFiberId = extra.fiber.id;
} }
if (w7) {
w7.__updateProps(props7, extra.fiber, undefined);
let pvnode = w7.__owl__.pvnode;
c1.push(pvnode);
} else {
let componentKey7 = \`ChildWidget\`;
let W7 = context.constructor.components[componentKey7] || QWeb.components[componentKey7]|| scope['ChildWidget'];
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')}
w7 = new W7(parent, props7);
parent.__owl__.cmap[k8] = w7.__owl__.id;
let fiber = w7.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k8, hook: {remove() {},destroy(vn) {w7.destroy();}}});
c1.push(pvnode);
w7.__owl__.pvnode = pvnode;
}
w7.__owl__.parentLastFiberId = extra.fiber.id;
} }
scope = _origScope5; scope = _origScope5;
return vn1; return vn1;
@@ -922,31 +1004,33 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
let h = this.h; let h = this.h;
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
const nodeKey2 = 'somestring'; {
// Component 'child' let key0 = 'somestring';
let k4 = \`__4__\` + nodeKey2; // Component 'child'
let w3 = k4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k4]] : false; let k3 = \`__3__\${key0}__\`;
let props3 = {flag:scope['state'].flag}; let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { let props2 = {flag:scope['state'].flag};
w3.destroy(); if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
w3 = false; w2.destroy();
w2 = false;
}
if (w2) {
w2.__updateProps(props2, extra.fiber, undefined);
let pvnode = w2.__owl__.pvnode;
c1.push(pvnode);
} else {
let componentKey2 = \`child\`;
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['child'];
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
w2 = new W2(parent, props2);
parent.__owl__.cmap[k3] = w2.__owl__.id;
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
c1.push(pvnode);
w2.__owl__.pvnode = pvnode;
}
w2.__owl__.parentLastFiberId = extra.fiber.id;
} }
if (w3) {
w3.__updateProps(props3, extra.fiber, undefined);
let pvnode = w3.__owl__.pvnode;
c1.push(pvnode);
} else {
let componentKey3 = \`child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| scope['child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
w3 = new W3(parent, props3);
parent.__owl__.cmap[k4] = w3.__owl__.id;
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
w3.__owl__.parentLastFiberId = extra.fiber.id;
return vn1; return vn1;
}" }"
`; `;
@@ -978,32 +1062,35 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
const nodeKey6 = scope['item']; let key1 = i1;
// Component 'Child' {
let k8 = \`__8__\` + nodeKey6; let key1 = scope['item'];
let args9 = [scope['item']]; // Component 'Child'
let w7 = k8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k8]] : false; let k7 = \`__7__\${key1}__\`;
let props7 = {}; let args8 = [scope['item']];
if (w7 && w7.__owl__.currentFiber && !w7.__owl__.vnode) { let w6 = k7 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k7]] : false;
w7.destroy(); let props6 = {};
w7 = false; if (w6 && w6.__owl__.currentFiber && !w6.__owl__.vnode) {
w6.destroy();
w6 = false;
}
if (w6) {
w6.__updateProps(props6, extra.fiber, undefined);
let pvnode = w6.__owl__.pvnode;
c1.push(pvnode);
} else {
let componentKey6 = \`Child\`;
let W6 = context.constructor.components[componentKey6] || QWeb.components[componentKey6]|| scope['Child'];
if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')}
w6 = new W6(parent, props6);
parent.__owl__.cmap[k7] = w6.__owl__.id;
let fiber = w6.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['onEv'](...args8, e);});}};});
let pvnode = h('dummy', {key: k7, hook: {remove() {},destroy(vn) {w6.destroy();}}});
c1.push(pvnode);
w6.__owl__.pvnode = pvnode;
}
w6.__owl__.parentLastFiberId = extra.fiber.id;
} }
if (w7) {
w7.__updateProps(props7, extra.fiber, undefined);
let pvnode = w7.__owl__.pvnode;
c1.push(pvnode);
} else {
let componentKey7 = \`Child\`;
let W7 = context.constructor.components[componentKey7] || QWeb.components[componentKey7]|| scope['Child'];
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')}
w7 = new W7(parent, props7);
parent.__owl__.cmap[k8] = w7.__owl__.id;
let fiber = w7.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['onEv'](...args9, e);});}};});
let pvnode = h('dummy', {key: k8, hook: {remove() {},destroy(vn) {w7.destroy();}}});
c1.push(pvnode);
w7.__owl__.pvnode = pvnode;
}
w7.__owl__.parentLastFiberId = extra.fiber.id;
} }
scope = _origScope5; scope = _origScope5;
return vn1; return vn1;
@@ -1017,14 +1104,14 @@ exports[`t-call handlers are properly bound through a t-call 1`] = `
let utils = this.constructor.utils; let utils = this.constructor.utils;
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key2 = extra.key || ''; let key0 = extra.key || \\"\\";
let c3 = [], p3 = {key:key2,on:{}}; let c2 = [], p2 = {key:\`\${key0}_2\`,on:{}};
let vn3 = h('p', p3, c3); let vn2 = h('p', p2, c2);
c1.push(vn3); c1.push(vn2);
let k4 = \`click__4__\` + key2; let k3 = \`click__3__\${key0}__\`;
extra.handlers[k4] = extra.handlers[k4] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['update'](e);}; extra.handlers[k3] = extra.handlers[k3] || function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['update'](e);};
p3.on['click'] = extra.handlers[k4]; p2.on['click'] = extra.handlers[k3];
c3.push({text: \`lucas\`}); c2.push({text: \`lucas\`});
}" }"
`; `;
@@ -1036,13 +1123,13 @@ exports[`t-call handlers with arguments are properly bound through a t-call 1`]
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key2 = extra.key || ''; let key0 = extra.key || \\"\\";
let c3 = [], p3 = {key:key2,on:{}}; let c2 = [], p2 = {key:\`\${key0}_2\`,on:{}};
let vn3 = h('p', p3, c3); let vn2 = h('p', p2, c2);
c1.push(vn3); c1.push(vn2);
let args4 = [scope['a']]; let args3 = [scope['a']];
p3.on['click'] = function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['update'](...args4, e);}; p2.on['click'] = function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['update'](...args3, e);};
c3.push({text: \`lucas\`}); c2.push({text: \`lucas\`});
}" }"
`; `;
@@ -1056,31 +1143,31 @@ exports[`t-call parent is set within t-call 1`] = `
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key2 = extra.key || ''; let key0 = extra.key || \\"\\";
// Component 'Child' // Component 'Child'
let k4 = \`__4__\` + key2; let k3 = \`__3__\${key0}__\`;
let w3 = k4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k4]] : false; let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
let props3 = {}; let props2 = {};
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
w3.destroy(); w2.destroy();
w3 = false; w2 = false;
} }
if (w3) { if (w2) {
w3.__updateProps(props3, extra.fiber, undefined); w2.__updateProps(props2, extra.fiber, undefined);
let pvnode = w3.__owl__.pvnode; let pvnode = w2.__owl__.pvnode;
c1.push(pvnode); c1.push(pvnode);
} else { } else {
let componentKey3 = \`Child\`; let componentKey2 = \`Child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| scope['Child']; let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
w3 = new W3(parent, props3); w2 = new W2(parent, props2);
parent.__owl__.cmap[k4] = w3.__owl__.id; parent.__owl__.cmap[k3] = w2.__owl__.id;
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
c1.push(pvnode); c1.push(pvnode);
w3.__owl__.pvnode = pvnode; w2.__owl__.pvnode = pvnode;
} }
w3.__owl__.parentLastFiberId = extra.fiber.id; w2.__owl__.parentLastFiberId = extra.fiber.id;
}" }"
`; `;
@@ -1094,31 +1181,31 @@ exports[`t-call parent is set within t-call with no parentNode 1`] = `
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key2 = extra.key || ''; let key0 = extra.key || \\"\\";
// Component 'Child' // Component 'Child'
let k4 = \`__4__\` + key2; let k3 = \`__3__\${key0}__\`;
let w3 = k4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k4]] : false; let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
let props3 = {}; let props2 = {};
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
w3.destroy(); w2.destroy();
w3 = false; w2 = false;
} }
if (w3) { if (w2) {
w3.__updateProps(props3, extra.fiber, undefined); w2.__updateProps(props2, extra.fiber, undefined);
let pvnode = w3.__owl__.pvnode; let pvnode = w2.__owl__.pvnode;
c1.push(pvnode); c1.push(pvnode);
} else { } else {
let componentKey3 = \`Child\`; let componentKey2 = \`Child\`;
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| scope['Child']; let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Child'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
w3 = new W3(parent, props3); w2 = new W2(parent, props2);
parent.__owl__.cmap[k4] = w3.__owl__.id; parent.__owl__.cmap[k3] = w2.__owl__.id;
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
c1.push(pvnode); c1.push(pvnode);
w3.__owl__.pvnode = pvnode; w2.__owl__.pvnode = pvnode;
} }
w3.__owl__.parentLastFiberId = extra.fiber.id; w2.__owl__.parentLastFiberId = extra.fiber.id;
}" }"
`; `;
@@ -1224,16 +1311,16 @@ exports[`t-model directive in a t-foreach 1`] = `
scope.thing_index = i1 scope.thing_index = i1
scope.thing = _3[i1] scope.thing = _3[i1]
scope.thing_value = _4[i1] scope.thing_value = _4[i1]
const nodeKey6 = scope['thing'].id; let key1 = scope['thing'].id;
let _7 = 'checkbox'; let _6 = 'checkbox';
let c8 = [], p8 = {key:nodeKey6,attrs:{type: _7},on:{}}; let c7 = [], p7 = {key:\`\${key1}_7\`,attrs:{type: _6},on:{}};
let vn8 = h('input', p8, c8); let vn7 = h('input', p7, c7);
c1.push(vn8); c1.push(vn7);
let expr8 = scope['thing']; let expr7 = scope['thing'];
let k9 = \`__9__\` + nodeKey6; let k8 = \`__8__\${key1}__\`;
p8.props = {checked: expr8.f}; p7.props = {checked: expr7.f};
extra.handlers[k9] = extra.handlers[k9] || ((ev) => {expr8.f = ev.target.checked}); extra.handlers[k8] = extra.handlers[k8] || ((ev) => {expr7.f = ev.target.checked});
p8.on['input'] = extra.handlers[k9]; p7.on['input'] = extra.handlers[k8];
} }
scope = _origScope5; scope = _origScope5;
return vn1; return vn1;
@@ -1382,6 +1469,36 @@ exports[`t-model directive on an input, type=checkbox 1`] = `
}" }"
`; `;
exports[`t-model directive two inputs in a div with a t-key 1`] = `
"function anonymous(context, extra
) {
// Template name: \\"__template__1\\"
let scope = Object.create(context);
let result;
let h = this.h;
{
let key0 = 'key';
let c1 = [], p1 = {key:\`\${key0}_1\`};
let vn1 = h('div', p1, c1);
result = vn1;
result = vn1;
if (scope['state'].flag) {
let _3 = {'a':true};
let c4 = [], p4 = {key:\`\${key0}_4\`,class:_3};
let vn4 = h('input', p4, c4);
c1.push(vn4);
}
if (!scope['state'].flag) {
let _6 = {'b':true};
let c7 = [], p7 = {key:\`\${key0}_7\`,class:_6};
let vn7 = h('input', p7, c7);
c1.push(vn7);
}
}
return result;
}"
`;
exports[`top level sub widgets basic use 1`] = ` exports[`top level sub widgets basic use 1`] = `
"function anonymous(context, extra "function anonymous(context, extra
) { ) {
@@ -229,14 +229,14 @@ exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
// Template name: \\"Link\\" // Template name: \\"Link\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let _12 = scope['props'].to; let _11 = scope['props'].to;
let c13 = [], p13 = {key:13,attrs:{href: _12}}; let c12 = [], p12 = {key:12,attrs:{href: _11}};
let vn13 = h('a', p13, c13); let vn12 = h('a', p12, c12);
const slot14 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
if (slot14) { if (slot13) {
slot14.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c13, parent: extra.parent || context})); slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context}));
} }
return vn13; return vn12;
}" }"
`; `;
@@ -270,35 +270,35 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
scope.user_index = i1 scope.user_index = i1
scope.user = _4[i1] scope.user = _4[i1]
scope.user_value = _5[i1] scope.user_value = _5[i1]
const nodeKey7 = scope['user'].id; let key1 = scope['user'].id;
let c8 = [], p8 = {key:nodeKey7}; let c7 = [], p7 = {key:\`\${key1}_7\`};
let vn8 = h('li', p8, c8); let vn7 = h('li', p7, c7);
c2.push(vn8); c2.push(vn7);
// Component 'Link' // Component 'Link'
let k10 = \`__10__\` + nodeKey7; let k9 = \`__9__\${key1}__\`;
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false; let w8 = k9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k9]] : false;
let props9 = {to:'/user/'+scope['user'].id}; let props8 = {to:'/user/'+scope['user'].id};
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) { if (w8 && w8.__owl__.currentFiber && !w8.__owl__.vnode) {
w9.destroy(); w8.destroy();
w9 = false; w8 = false;
} }
if (w9) { if (w8) {
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope)); w8.__updateProps(props8, extra.fiber, Object.assign(Object.create(context), scope));
let pvnode = w9.__owl__.pvnode; let pvnode = w8.__owl__.pvnode;
c8.push(pvnode); c7.push(pvnode);
} else { } else {
let componentKey9 = \`Link\`; let componentKey8 = \`Link\`;
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link']; let W8 = context.constructor.components[componentKey8] || QWeb.components[componentKey8]|| scope['Link'];
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')} if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
w9 = new W9(parent, props9); w8 = new W8(parent, props8);
parent.__owl__.cmap[k10] = w9.__owl__.id; parent.__owl__.cmap[k9] = w8.__owl__.id;
w9.__owl__.slotId = 1; w8.__owl__.slotId = 1;
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w8.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}}); let pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
c8.push(pvnode); c7.push(pvnode);
w9.__owl__.pvnode = pvnode; w8.__owl__.pvnode = pvnode;
} }
w9.__owl__.parentLastFiberId = extra.fiber.id; w8.__owl__.parentLastFiberId = extra.fiber.id;
} }
scope = _origScope6; scope = _origScope6;
return vn1; return vn1;
@@ -311,11 +311,11 @@ exports[`t-slot directive slots are rendered with proper context, part 2 3`] = `
// Template name: \\"slot_default_template\\" // Template name: \\"slot_default_template\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c8 = extra.parentNode; let c7 = extra.parentNode;
c8.push({text: \`User \`}); c7.push({text: \`User \`});
let _11 = scope['user'].name; let _10 = scope['user'].name;
if (_11 != null) { if (_10 != null) {
c8.push({text: _11}); c7.push({text: _10});
} }
}" }"
`; `;
@@ -326,14 +326,14 @@ exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
// Template name: \\"Link\\" // Template name: \\"Link\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let _11 = scope['props'].to; let _10 = scope['props'].to;
let c12 = [], p12 = {key:12,attrs:{href: _11}}; let c11 = [], p11 = {key:11,attrs:{href: _10}};
let vn12 = h('a', p12, c12); let vn11 = h('a', p11, c11);
const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; const slot12 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
if (slot13) { if (slot12) {
slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context})); slot12.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c11, parent: extra.parent || context}));
} }
return vn12; return vn11;
}" }"
`; `;
@@ -367,36 +367,36 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
scope.user_index = i1 scope.user_index = i1
scope.user = _4[i1] scope.user = _4[i1]
scope.user_value = _5[i1] scope.user_value = _5[i1]
const nodeKey7 = scope['user'].id; let key1 = scope['user'].id;
let c8 = [], p8 = {key:nodeKey7}; let c7 = [], p7 = {key:\`\${key1}_7\`};
let vn8 = h('li', p8, c8); let vn7 = h('li', p7, c7);
c2.push(vn8); c2.push(vn7);
scope.userdescr = 'User '+scope['user'].name; scope.userdescr = 'User '+scope['user'].name;
// Component 'Link' // Component 'Link'
let k10 = \`__10__\` + nodeKey7; let k9 = \`__9__\${key1}__\`;
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false; let w8 = k9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k9]] : false;
let props9 = {to:'/user/'+scope['user'].id}; let props8 = {to:'/user/'+scope['user'].id};
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) { if (w8 && w8.__owl__.currentFiber && !w8.__owl__.vnode) {
w9.destroy(); w8.destroy();
w9 = false; w8 = false;
} }
if (w9) { if (w8) {
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope)); w8.__updateProps(props8, extra.fiber, Object.assign(Object.create(context), scope));
let pvnode = w9.__owl__.pvnode; let pvnode = w8.__owl__.pvnode;
c8.push(pvnode); c7.push(pvnode);
} else { } else {
let componentKey9 = \`Link\`; let componentKey8 = \`Link\`;
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link']; let W8 = context.constructor.components[componentKey8] || QWeb.components[componentKey8]|| scope['Link'];
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')} if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
w9 = new W9(parent, props9); w8 = new W8(parent, props8);
parent.__owl__.cmap[k10] = w9.__owl__.id; parent.__owl__.cmap[k9] = w8.__owl__.id;
w9.__owl__.slotId = 1; w8.__owl__.slotId = 1;
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); let fiber = w8.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}}); let pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
c8.push(pvnode); c7.push(pvnode);
w9.__owl__.pvnode = pvnode; w8.__owl__.pvnode = pvnode;
} }
w9.__owl__.parentLastFiberId = extra.fiber.id; w8.__owl__.parentLastFiberId = extra.fiber.id;
} }
scope = _origScope6; scope = _origScope6;
return vn1; return vn1;
@@ -409,9 +409,9 @@ exports[`t-slot directive slots are rendered with proper context, part 3 3`] = `
// Template name: \\"slot_default_template\\" // Template name: \\"slot_default_template\\"
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c8 = extra.parentNode; let c7 = extra.parentNode;
if (scope.userdescr != null) { if (scope.userdescr != null) {
c8.push({text: scope.userdescr}); c7.push({text: scope.userdescr});
} }
}" }"
`; `;
+53
View File
@@ -322,6 +322,36 @@ describe("basic widget properties", () => {
console.warn = warn; console.warn = warn;
}); });
test("reconciliation alg works for t-foreach in t-foreach, 2", async () => {
class Child extends Component<any, any> {
static template = xml`<div><t t-esc="props.row + '_' + props.col"/></div>`;
}
class Parent extends Component<any, any> {
static template = xml`
<div>
<p t-foreach="state.rows" t-as="row" t-key="row">
<p t-foreach="state.cols" t-as="col" t-key="col">
<Child row="row" col="col"/>
</p>
</p>
</div>`;
static components = { Child };
state = useState({ rows: [1, 2], cols: ["a", "b"] });
}
const widget = new Parent();
await widget.mount(fixture);
expect(fixture.innerHTML).toBe(
"<div><p><p><div>1_a</div></p><p><div>1_b</div></p></p><p><p><div>2_a</div></p><p><div>2_b</div></p></p></div>"
);
widget.state.rows = [2, 1];
await nextTick();
expect(fixture.innerHTML).toBe(
"<div><p><p><div>2_a</div></p><p><div>2_b</div></p></p><p><p><div>1_a</div></p><p><div>1_b</div></p></p></div>"
);
});
test("same t-keys in two different places", async () => { test("same t-keys in two different places", async () => {
class Child extends Component<any, any> { class Child extends Component<any, any> {
static template = xml`<span><t t-esc="props.blip"/></span>`; static template = xml`<span><t t-esc="props.blip"/></span>`;
@@ -339,6 +369,7 @@ describe("basic widget properties", () => {
const widget = new Parent(); const widget = new Parent();
await widget.mount(fixture); await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div><span>1</span></div><div><span>2</span></div></div>"); expect(fixture.innerHTML).toBe("<div><div><span>1</span></div><div><span>2</span></div></div>");
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
}); });
test("t-key on a component with t-if, and a sibling component", async () => { test("t-key on a component with t-if, and a sibling component", async () => {
@@ -4418,6 +4449,28 @@ describe("t-model directive", () => {
expect(comp.state[2].f).toBe(false); expect(comp.state[2].f).toBe(false);
expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot(); expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot();
}); });
test("two inputs in a div with a t-key", async () => {
class SomeComponent extends Component<any, any> {
static template = xml`
<div t-key="'key'">
<input class="a" t-if="state.flag"/>
<input class="b" t-if="!state.flag"/>
</div>
`;
state = useState({ flag: true });
}
const comp = new SomeComponent();
await comp.mount(fixture);
expect(fixture.innerHTML).toBe('<div><input class="a"></div>');
expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot();
fixture.querySelector("input")!.value = "asdf";
expect(fixture.querySelector("input")!.value).toBe("asdf");
comp.state.flag = false;
await nextTick();
expect(fixture.innerHTML).toBe('<div><input class="b"></div>');
expect(fixture.querySelector("input")!.value).toBe("");
});
}); });
describe("environment and plugins", () => { describe("environment and plugins", () => {
+229 -206
View File
@@ -428,6 +428,7 @@ exports[`foreach does not pollute the rendering context 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
let key1 = i1;
let _6 = scope['item']; let _6 = scope['item'];
if (_6 != null) { if (_6 != null) {
c1.push({text: _6}); c1.push({text: _6});
@@ -462,13 +463,13 @@ exports[`foreach iterate on items (on a element node) 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
const nodeKey6 = scope['item']; let key1 = scope['item'];
let c7 = [], p7 = {key:nodeKey6}; let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn7 = h('span', p7, c7); let vn6 = h('span', p6, c6);
c1.push(vn7); c1.push(vn6);
let _8 = scope['item']; let _7 = scope['item'];
if (_8 != null) { if (_7 != null) {
c7.push({text: _8}); c6.push({text: _7});
} }
} }
scope = _origScope5; scope = _origScope5;
@@ -500,6 +501,7 @@ exports[`foreach iterate on items 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
let key1 = i1;
c1.push({text: \` [\`}); c1.push({text: \` [\`});
let _6 = scope['item_index']; let _6 = scope['item_index'];
if (_6 != null) { if (_6 != null) {
@@ -546,6 +548,7 @@ exports[`foreach iterate, dict param 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
let key1 = i1;
c1.push({text: \` [\`}); c1.push({text: \` [\`});
let _6 = scope['item_index']; let _6 = scope['item_index'];
if (_6 != null) { if (_6 != null) {
@@ -592,6 +595,7 @@ exports[`foreach iterate, position 1`] = `
scope.elem_index = i1 scope.elem_index = i1
scope.elem = _3[i1] scope.elem = _3[i1]
scope.elem_value = _4[i1] scope.elem_value = _4[i1]
let key1 = i1;
c1.push({text: \` -\`}); c1.push({text: \` -\`});
if (scope['elem_first']) { if (scope['elem_first']) {
c1.push({text: \` first\`}); c1.push({text: \` first\`});
@@ -635,6 +639,7 @@ exports[`foreach t-foreach in t-forach 1`] = `
scope.number_index = i1 scope.number_index = i1
scope.number = _3[i1] scope.number = _3[i1]
scope.number_value = _4[i1] scope.number_value = _4[i1]
let key1 = i1;
let _6 = scope['letters']; let _6 = scope['letters'];
if (!_6) { throw new Error('QWeb error: Invalid loop expression')} if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
let _7 = _8 = _6; let _7 = _8 = _6;
@@ -651,6 +656,7 @@ exports[`foreach t-foreach in t-forach 1`] = `
scope.letter_index = i2 scope.letter_index = i2
scope.letter = _7[i2] scope.letter = _7[i2]
scope.letter_value = _8[i2] scope.letter_value = _8[i2]
let key2 = i2;
c1.push({text: \` [\`}); c1.push({text: \` [\`});
let _10 = scope['number']; let _10 = scope['number'];
if (_10 != null) { if (_10 != null) {
@@ -693,7 +699,8 @@ exports[`foreach warn if no key in some case 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
let c6 = [], p6 = {key:6}; let key1 = i1;
let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn6 = h('span', p6, c6); let vn6 = h('span', p6, c6);
c1.push(vn6); c1.push(vn6);
let _7 = scope['item']; let _7 = scope['item'];
@@ -757,7 +764,8 @@ exports[`misc global 1`] = `
scope.value_index = i1 scope.value_index = i1
scope.value = _3[i1] scope.value = _3[i1]
scope.value_value = _4[i1] scope.value_value = _4[i1]
let c6 = [], p6 = {key:6}; let key1 = i1;
let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn6 = h('span', p6, c6); let vn6 = h('span', p6, c6);
c1.push(vn6); c1.push(vn6);
let _7 = scope['value']; let _7 = scope['value'];
@@ -765,32 +773,32 @@ exports[`misc global 1`] = `
c6.push({text: _7}); c6.push({text: _7});
} }
{ {
let _origScope11 = scope; let _origScope10 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
{ {
let _origScope15 = scope; let _origScope13 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
scope.foo = 'aaa'; scope.foo = 'aaa';
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
let k16 = \`__16__\${i1}__\`; let k14 = \`__14__\${key1}__\`;
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k16})); this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k14}));
scope = _origScope15; scope = _origScope13;
} }
let k17 = \`__17__\${i1}__\`; let k15 = \`__15__\${key1}__\`;
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k17})); this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k15}));
scope.foo = 'bbb'; scope.foo = 'bbb';
let k18 = \`__18__\${i1}__\`; let k16 = \`__16__\${key1}__\`;
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k18})); this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k16}));
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
let k19 = \`__19__\${i1}__\`; let k17 = \`__17__\${key1}__\`;
this.subTemplates['_callee-asc'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k19})); this.subTemplates['_callee-asc'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k17}));
scope = _origScope11; scope = _origScope10;
} }
} }
scope = _origScope5; scope = _origScope5;
@@ -1044,11 +1052,11 @@ exports[`t-call (template calling basic caller 2`] = `
// Template name: \\"_basic-callee\\" // Template name: \\"_basic-callee\\"
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key2 = extra.key || ''; let key0 = extra.key || \\"\\";
let c3 = [], p3 = {key:key2}; let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn3 = h('span', p3, c3); let vn2 = h('span', p2, c2);
c1.push(vn3); c1.push(vn2);
c3.push({text: \`ok\`}); c2.push({text: \`ok\`});
}" }"
`; `;
@@ -1073,11 +1081,11 @@ exports[`t-call (template calling basic caller, no parent node 2`] = `
// Template name: \\"_basic-callee\\" // Template name: \\"_basic-callee\\"
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key2 = extra.key || ''; let key0 = extra.key || \\"\\";
let c3 = [], p3 = {key:key2}; let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn3 = h('div', p3, c3); let vn2 = h('div', p2, c2);
c1.push(vn3); c1.push(vn2);
c3.push({text: \`ok\`}); c2.push({text: \`ok\`});
}" }"
`; `;
@@ -1091,23 +1099,23 @@ exports[`t-call (template calling call with several sub nodes on same line 1`] =
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
{ {
let _origScope4 = scope; let _origScope3 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
let c4 = [], p4 = {key:4};
let vn4 = h('span', p4, c4);
c__0.push(vn4);
c4.push({text: \`hey\`});
c__0.push({text: \` \`});
let c5 = [], p5 = {key:5}; let c5 = [], p5 = {key:5};
let vn5 = h('span', p5, c5); let vn5 = h('span', p5, c5);
c__0.push(vn5); c__0.push(vn5);
c5.push({text: \`hey\`}); c5.push({text: \`yay\`});
c__0.push({text: \` \`});
let c6 = [], p6 = {key:6};
let vn6 = h('span', p6, c6);
c__0.push(vn6);
c6.push({text: \`yay\`});
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
scope = _origScope4; scope = _origScope3;
} }
return vn1; return vn1;
}" }"
@@ -1121,11 +1129,11 @@ exports[`t-call (template calling call with several sub nodes on same line 2`] =
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key2 = extra.key || ''; let key0 = extra.key || \\"\\";
let c3 = [], p3 = {key:key2}; let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn3 = h('div', p3, c3); let vn2 = h('div', p2, c2);
c1.push(vn3); c1.push(vn2);
c3.push(...scope[utils.zero]); c2.push(...scope[utils.zero]);
}" }"
`; `;
@@ -1139,23 +1147,23 @@ exports[`t-call (template calling cascading t-call t-raw='0' 1`] = `
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
{ {
let _origScope13 = scope; let _origScope12 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
let c13 = [], p13 = {key:13};
let vn13 = h('span', p13, c13);
c__0.push(vn13);
c13.push({text: \`hey\`});
c__0.push({text: \` \`});
let c14 = [], p14 = {key:14}; let c14 = [], p14 = {key:14};
let vn14 = h('span', p14, c14); let vn14 = h('span', p14, c14);
c__0.push(vn14); c__0.push(vn14);
c14.push({text: \`hey\`}); c14.push({text: \`yay\`});
c__0.push({text: \` \`});
let c15 = [], p15 = {key:15};
let vn15 = h('span', p15, c15);
c__0.push(vn15);
c15.push({text: \`yay\`});
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
scope = _origScope13; scope = _origScope12;
} }
return vn1; return vn1;
}" }"
@@ -1204,16 +1212,17 @@ exports[`t-call (template calling recursive template, part 1 2`] = `
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key3 = extra.key || ''; let key0 = extra.key || \\"\\";
let c4 = [], p4 = {key:key3}; let c3 = [], p3 = {key:\`\${key0}_3\`};
let vn4 = h('div', p4, c4); let vn3 = h('div', p3, c3);
c1.push(vn4); c1.push(vn3);
let c5 = [], p5 = {key:key3}; let c4 = [], p4 = {key:\`\${key0}_4\`};
let vn5 = h('span', p5, c5); let vn4 = h('span', p4, c4);
c4.push(vn5); c3.push(vn4);
c5.push({text: \`hey\`}); c4.push({text: \`hey\`});
if (false) { if (false) {
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c4, parent: utils.getComponent(context)})); let k5 = \`__5__\${key0}__\`;
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k5}));
} }
}" }"
`; `;
@@ -1228,7 +1237,7 @@ exports[`t-call (template calling recursive template, part 2 1`] = `
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
{ {
let _origScope12 = scope; let _origScope11 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
@@ -1236,7 +1245,7 @@ exports[`t-call (template calling recursive template, part 2 1`] = `
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
scope = _origScope12; scope = _origScope11;
} }
return vn1; return vn1;
}" }"
@@ -1250,47 +1259,48 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key2 = extra.key || ''; let key0 = extra.key || \\"\\";
let c3 = [], p3 = {key:key2}; let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn3 = h('div', p3, c3); let vn2 = h('div', p2, c2);
c1.push(vn3); c1.push(vn2);
let c4 = [], p4 = {key:key2}; let c3 = [], p3 = {key:\`\${key0}_3\`};
let vn4 = h('p', p4, c4); let vn3 = h('p', p3, c3);
c3.push(vn4); c2.push(vn3);
let _5 = scope['node'].val; let _4 = scope['node'].val;
if (_5 != null) { if (_4 != null) {
c4.push({text: _5}); c3.push({text: _4});
} }
let _6 = scope['node'].children||[]; let _5 = scope['node'].children||[];
if (!_6) { throw new Error('QWeb error: Invalid loop expression')} if (!_5) { throw new Error('QWeb error: Invalid loop expression')}
let _7 = _8 = _6; let _6 = _7 = _5;
if (!(_6 instanceof Array)) { if (!(_5 instanceof Array)) {
_7 = Object.keys(_6); _6 = Object.keys(_5);
_8 = Object.values(_6); _7 = Object.values(_5);
} }
let _length7 = _7.length; let _length6 = _6.length;
let _origScope9 = scope; let _origScope8 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
for (let i1 = 0; i1 < _length7; i1++) { for (let i1 = 0; i1 < _length6; i1++) {
scope.subtree_first = i1 === 0 scope.subtree_first = i1 === 0
scope.subtree_last = i1 === _length7 - 1 scope.subtree_last = i1 === _length6 - 1
scope.subtree_index = i1 scope.subtree_index = i1
scope.subtree = _7[i1] scope.subtree = _6[i1]
scope.subtree_value = _8[i1] scope.subtree_value = _7[i1]
let key1 = i1;
{ {
let _origScope10 = scope; let _origScope9 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
scope.node = scope['subtree']; scope.node = scope['subtree'];
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
let k11 = \`__11__\` + key2; let k10 = \`__10__\${key0}__\${key1}__\`;
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k11})); this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
scope = _origScope10; scope = _origScope9;
} }
} }
scope = _origScope9; scope = _origScope8;
}" }"
`; `;
@@ -1304,7 +1314,7 @@ exports[`t-call (template calling recursive template, part 3 1`] = `
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
{ {
let _origScope12 = scope; let _origScope11 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
@@ -1312,7 +1322,7 @@ exports[`t-call (template calling recursive template, part 3 1`] = `
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
scope = _origScope12; scope = _origScope11;
} }
return vn1; return vn1;
}" }"
@@ -1326,47 +1336,48 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
let scope = Object.create(context); let scope = Object.create(context);
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key2 = extra.key || ''; let key0 = extra.key || \\"\\";
let c3 = [], p3 = {key:key2}; let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn3 = h('div', p3, c3); let vn2 = h('div', p2, c2);
c1.push(vn3); c1.push(vn2);
let c4 = [], p4 = {key:key2}; let c3 = [], p3 = {key:\`\${key0}_3\`};
let vn4 = h('p', p4, c4); let vn3 = h('p', p3, c3);
c3.push(vn4); c2.push(vn3);
let _5 = scope['node'].val; let _4 = scope['node'].val;
if (_5 != null) { if (_4 != null) {
c4.push({text: _5}); c3.push({text: _4});
} }
let _6 = scope['node'].children||[]; let _5 = scope['node'].children||[];
if (!_6) { throw new Error('QWeb error: Invalid loop expression')} if (!_5) { throw new Error('QWeb error: Invalid loop expression')}
let _7 = _8 = _6; let _6 = _7 = _5;
if (!(_6 instanceof Array)) { if (!(_5 instanceof Array)) {
_7 = Object.keys(_6); _6 = Object.keys(_5);
_8 = Object.values(_6); _7 = Object.values(_5);
} }
let _length7 = _7.length; let _length6 = _6.length;
let _origScope9 = scope; let _origScope8 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
for (let i1 = 0; i1 < _length7; i1++) { for (let i1 = 0; i1 < _length6; i1++) {
scope.subtree_first = i1 === 0 scope.subtree_first = i1 === 0
scope.subtree_last = i1 === _length7 - 1 scope.subtree_last = i1 === _length6 - 1
scope.subtree_index = i1 scope.subtree_index = i1
scope.subtree = _7[i1] scope.subtree = _6[i1]
scope.subtree_value = _8[i1] scope.subtree_value = _7[i1]
let key1 = i1;
{ {
let _origScope10 = scope; let _origScope9 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
scope.node = scope['subtree']; scope.node = scope['subtree'];
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
let k11 = \`__11__\` + key2; let k10 = \`__10__\${key0}__\${key1}__\`;
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k11})); this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
scope = _origScope10; scope = _origScope9;
} }
} }
scope = _origScope9; scope = _origScope8;
}" }"
`; `;
@@ -1380,7 +1391,7 @@ exports[`t-call (template calling scoped parameters 1`] = `
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
{ {
let _origScope3 = scope; let _origScope2 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
@@ -1388,7 +1399,7 @@ exports[`t-call (template calling scoped parameters 1`] = `
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
scope = _origScope3; scope = _origScope2;
} }
if (scope.foo != null) { if (scope.foo != null) {
c1.push({text: scope.foo}); c1.push({text: scope.foo});
@@ -1419,11 +1430,11 @@ exports[`t-call (template calling t-call with t-if 2`] = `
// Template name: \\"sub\\" // Template name: \\"sub\\"
let h = this.h; let h = this.h;
let c1 = extra.parentNode; let c1 = extra.parentNode;
let key2 = extra.key || ''; let key0 = extra.key || \\"\\";
let c3 = [], p3 = {key:key2}; let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn3 = h('span', p3, c3); let vn2 = h('span', p2, c2);
c1.push(vn3); c1.push(vn2);
c3.push({text: \`ok\`}); c2.push({text: \`ok\`});
}" }"
`; `;
@@ -1452,18 +1463,19 @@ exports[`t-call (template calling t-call with t-set inside and outside 1`] = `
scope.v_index = i1 scope.v_index = i1
scope.v = _3[i1] scope.v = _3[i1]
scope.v_value = _4[i1] scope.v_value = _4[i1]
let key1 = i1;
scope.val = scope['v'].val; scope.val = scope['v'].val;
{ {
let _origScope9 = scope; let _origScope8 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
scope.val3 = scope.val*3; scope.val3 = scope.val*3;
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
let k10 = \`__10__\${i1}__\`; let k9 = \`__9__\${key1}__\`;
this.subTemplates['sub'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k10})); this.subTemplates['sub'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k9}));
scope = _origScope9; scope = _origScope8;
} }
} }
scope = _origScope5; scope = _origScope5;
@@ -1501,7 +1513,7 @@ exports[`t-call (template calling t-call, conditional and t-set in t-call body 1
} }
else if (scope.v1==='elif') { else if (scope.v1==='elif') {
{ {
let _origScope7 = scope; let _origScope5 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
@@ -1509,7 +1521,7 @@ exports[`t-call (template calling t-call, conditional and t-set in t-call body 1
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['callee2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); this.subTemplates['callee2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
scope = _origScope7; scope = _origScope5;
} }
} }
return vn1; return vn1;
@@ -1539,7 +1551,7 @@ exports[`t-call (template calling with unused body 1`] = `
let result; let result;
let h = this.h; let h = this.h;
{ {
let _origScope4 = scope; let _origScope3 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
@@ -1549,7 +1561,7 @@ exports[`t-call (template calling with unused body 1`] = `
result = [] result = []
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)})); this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)}));
result = result[0] result = result[0]
scope = _origScope4; scope = _origScope3;
} }
return result; return result;
}" }"
@@ -1564,7 +1576,7 @@ exports[`t-call (template calling with unused setbody 1`] = `
let result; let result;
let h = this.h; let h = this.h;
{ {
let _origScope4 = scope; let _origScope3 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
@@ -1574,7 +1586,7 @@ exports[`t-call (template calling with unused setbody 1`] = `
result = [] result = []
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)})); this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)}));
result = result[0] result = result[0]
scope = _origScope4; scope = _origScope3;
} }
return result; return result;
}" }"
@@ -1589,7 +1601,7 @@ exports[`t-call (template calling with used body 1`] = `
let result; let result;
let h = this.h; let h = this.h;
{ {
let _origScope4 = scope; let _origScope3 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
@@ -1599,7 +1611,7 @@ exports[`t-call (template calling with used body 1`] = `
result = [] result = []
this.subTemplates['_callee-printsbody'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)})); this.subTemplates['_callee-printsbody'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)}));
result = result[0] result = result[0]
scope = _origScope4; scope = _origScope3;
} }
return result; return result;
}" }"
@@ -1615,7 +1627,7 @@ exports[`t-call (template calling with used set body 1`] = `
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('span', p1, c1); let vn1 = h('span', p1, c1);
{ {
let _origScope4 = scope; let _origScope3 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
@@ -1623,7 +1635,7 @@ exports[`t-call (template calling with used set body 1`] = `
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
scope = _origScope4; scope = _origScope3;
} }
return vn1; return vn1;
}" }"
@@ -1812,18 +1824,18 @@ exports[`t-esc t-esc=0 is escaped 1`] = `
let c1 = [], p1 = {key:1}; let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1); let vn1 = h('div', p1, c1);
{ {
let _origScope4 = scope; let _origScope3 = scope;
scope = Object.assign(Object.create(context), scope); scope = Object.assign(Object.create(context), scope);
{ {
let c__0 = []; let c__0 = [];
let c5 = [], p5 = {key:5}; let c4 = [], p4 = {key:4};
let vn5 = h('p', p5, c5); let vn4 = h('p', p4, c4);
c__0.push(vn5); c__0.push(vn4);
c5.push({text: \`escaped\`}); c4.push({text: \`escaped\`});
scope[utils.zero] = c__0; scope[utils.zero] = c__0;
} }
this.subTemplates['test'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)})); this.subTemplates['test'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
scope = _origScope4; scope = _origScope3;
} }
return vn1; return vn1;
}" }"
@@ -2110,15 +2122,20 @@ exports[`t-key can use t-key directive on a node 1`] = `
) { ) {
// Template name: \\"test\\" // Template name: \\"test\\"
let scope = Object.create(context); let scope = Object.create(context);
let result;
let h = this.h; let h = this.h;
const nodeKey1 = scope['beer'].id; {
let c2 = [], p2 = {key:nodeKey1}; let key0 = scope['beer'].id;
let vn2 = h('div', p2, c2); let c1 = [], p1 = {key:\`\${key0}_1\`};
let _3 = scope['beer'].name; let vn1 = h('div', p1, c1);
if (_3 != null) { result = vn1;
c2.push({text: _3}); result = vn1;
let _2 = scope['beer'].name;
if (_2 != null) {
c1.push({text: _2});
}
} }
return vn2; return result;
}" }"
`; `;
@@ -2146,13 +2163,13 @@ exports[`t-key t-key directive in a list 1`] = `
scope.beer_index = i1 scope.beer_index = i1
scope.beer = _3[i1] scope.beer = _3[i1]
scope.beer_value = _4[i1] scope.beer_value = _4[i1]
const nodeKey6 = scope['beer'].id; let key1 = scope['beer'].id;
let c7 = [], p7 = {key:nodeKey6}; let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn7 = h('li', p7, c7); let vn6 = h('li', p6, c6);
c1.push(vn7); c1.push(vn6);
let _8 = scope['beer'].name; let _7 = scope['beer'].name;
if (_8 != null) { if (_7 != null) {
c7.push({text: _8}); c6.push({text: _7});
} }
} }
scope = _origScope5; scope = _origScope5;
@@ -2250,16 +2267,16 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
scope.action_index = i1 scope.action_index = i1
scope.action = _3[i1] scope.action = _3[i1]
scope.action_value = _4[i1] scope.action_value = _4[i1]
const nodeKey6 = scope['action_index']; let key1 = scope['action_index'];
let c7 = [], p7 = {key:nodeKey6}; let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn7 = h('li', p7, c7); let vn6 = h('li', p6, c6);
c1.push(vn7); c1.push(vn6);
let c8 = [], p8 = {key:nodeKey6,on:{}}; let c7 = [], p7 = {key:\`\${key1}_7\`,on:{}};
let vn8 = h('a', p8, c8); let vn7 = h('a', p7, c7);
c7.push(vn8); c6.push(vn7);
let args9 = [scope['action']]; let args8 = [scope['action']];
p8.on['click'] = function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['activate'](...args9, e);}; p7.on['click'] = function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['activate'](...args8, e);};
c8.push({text: \`link\`}); c7.push({text: \`link\`});
} }
scope = _origScope5; scope = _origScope5;
return vn1; return vn1;
@@ -2507,17 +2524,20 @@ exports[`t-on t-on with prevent modifier in t-foreach 1`] = `
scope.project_index = i1 scope.project_index = i1
scope.project = _3[i1] scope.project = _3[i1]
scope.project_value = _4[i1] scope.project_value = _4[i1]
const nodeKey6 = scope['project']; let key1 = i1;
let _7 = '#'; {
let c8 = [], p8 = {key:nodeKey6,attrs:{href: _7},on:{}}; let key1 = scope['project'];
let vn8 = h('a', p8, c8); let _6 = '#';
c1.push(vn8); let c7 = [], p7 = {key:\`\${key1}_7\`,attrs:{href: _6},on:{}};
let args9 = [scope['project'].id]; let vn7 = h('a', p7, c7);
p8.on['click'] = function (e) {if (!context.__owl__.isMounted){return}e.preventDefault();utils.getComponent(context)['onEdit'](...args9, e);}; c1.push(vn7);
c8.push({text: \` Edit \`}); let args8 = [scope['project'].id];
let _10 = scope['project'].name; p7.on['click'] = function (e) {if (!context.__owl__.isMounted){return}e.preventDefault();utils.getComponent(context)['onEdit'](...args8, e);};
if (_10 != null) { c7.push({text: \` Edit \`});
c8.push({text: _10}); let _9 = scope['project'].name;
if (_9 != null) {
c7.push({text: _9});
}
} }
} }
scope = _origScope5; scope = _origScope5;
@@ -2750,22 +2770,25 @@ exports[`t-ref refs in a loop 1`] = `
scope.item_index = i1 scope.item_index = i1
scope.item = _3[i1] scope.item = _3[i1]
scope.item_value = _4[i1] scope.item_value = _4[i1]
const nodeKey6 = scope['item']; let key1 = i1;
let c7 = [], p7 = {key:nodeKey6}; {
let vn7 = h('div', p7, c7); let key1 = scope['item'];
c1.push(vn7); let c6 = [], p6 = {key:\`\${key1}_6\`};
const ref8 = (scope['item']); let vn6 = h('div', p6, c6);
p7.hook = { c1.push(vn6);
create: (_, n) => { const ref7 = (scope['item']);
context.__owl__.refs[ref8] = n.elm; p6.hook = {
}, create: (_, n) => {
destroy: () => { context.__owl__.refs[ref7] = n.elm;
delete context.__owl__.refs[ref8]; },
}, destroy: () => {
}; delete context.__owl__.refs[ref7];
let _9 = scope['item']; },
if (_9 != null) { };
c7.push({text: _9}); let _8 = scope['item'];
if (_8 != null) {
c6.push({text: _8});
}
} }
} }
scope = _origScope5; scope = _origScope5;
@@ -2988,16 +3011,16 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
scope.elem_index = i1 scope.elem_index = i1
scope.elem = _3[i1] scope.elem = _3[i1]
scope.elem_value = _4[i1] scope.elem_value = _4[i1]
const nodeKey6 = scope['elem_index']; let key1 = scope['elem_index'];
let c7 = [], p7 = {key:nodeKey6}; let c6 = [], p6 = {key:\`\${key1}_6\`};
let vn7 = h('div', p7, c7); let vn6 = h('div', p6, c6);
c1.push(vn7); c1.push(vn6);
let c8 = [], p8 = {key:nodeKey6}; let c7 = [], p7 = {key:\`\${key1}_7\`};
let vn8 = h('span', p8, c8); let vn7 = h('span', p7, c7);
c7.push(vn8); c6.push(vn7);
c8.push({text: \`v\`}); c7.push({text: \`v\`});
if (scope.v != null) { if (scope.v != null) {
c8.push({text: scope.v}); c7.push({text: scope.v});
} }
scope.v = scope['elem']; scope.v = scope['elem'];
} }
@@ -11,33 +11,35 @@ exports[`RouteComponent can render simple cases 1`] = `
let result; let result;
let h = this.h; let h = this.h;
if (scope['routeComponent']) { if (scope['routeComponent']) {
const nodeKey4 = scope['env'].router.currentRouteName; {
// Component 'routeComponent' let key0 = scope['env'].router.currentRouteName;
let k6 = \`__6__\` + nodeKey4; // Component 'routeComponent'
let w5 = k6 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k6]] : false; let k5 = \`__5__\${key0}__\`;
let vn7 = {}; let w4 = k5 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k5]] : false;
result = vn7; let vn6 = {};
let props5 = Object.assign({}, scope['env'].router.currentParams); result = vn6;
if (w5 && w5.__owl__.currentFiber && !w5.__owl__.vnode) { let props4 = Object.assign({}, scope['env'].router.currentParams);
w5.destroy(); if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) {
w5 = false; w4.destroy();
w4 = false;
}
if (w4) {
w4.__updateProps(props4, extra.fiber, undefined);
let pvnode = w4.__owl__.pvnode;
utils.defineProxy(vn6, pvnode);
} else {
let componentKey4 = \`routeComponent\`;
let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| scope['routeComponent'];
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
w4 = new W4(parent, props4);
parent.__owl__.cmap[k5] = w4.__owl__.id;
let fiber = w4.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k5, hook: {remove() {},destroy(vn) {w4.destroy();}}});
utils.defineProxy(vn6, pvnode);
w4.__owl__.pvnode = pvnode;
}
w4.__owl__.parentLastFiberId = extra.fiber.id;
} }
if (w5) {
w5.__updateProps(props5, extra.fiber, undefined);
let pvnode = w5.__owl__.pvnode;
utils.defineProxy(vn7, pvnode);
} else {
let componentKey5 = \`routeComponent\`;
let W5 = context.constructor.components[componentKey5] || QWeb.components[componentKey5]|| scope['routeComponent'];
if (!W5) {throw new Error('Cannot find the definition of component \\"' + componentKey5 + '\\"')}
w5 = new W5(parent, props5);
parent.__owl__.cmap[k6] = w5.__owl__.id;
let fiber = w5.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
let pvnode = h('dummy', {key: k6, hook: {remove() {},destroy(vn) {w5.destroy();}}});
utils.defineProxy(vn7, pvnode);
w5.__owl__.pvnode = pvnode;
}
w5.__owl__.parentLastFiberId = extra.fiber.id;
} }
return result; return result;
}" }"