[FIX] component: fix issues with component internal template key

closes #298
This commit is contained in:
Géry Debongnie
2019-10-16 14:51:53 +02:00
committed by aab-odoo
parent 918945c11e
commit f6d6da8393
11 changed files with 610 additions and 393 deletions
+15 -17
View File
@@ -243,21 +243,19 @@ QWeb.addDirective({
ctx.addLine(`let key${keyID} = 'key' + ${key};`); ctx.addLine(`let key${keyID} = 'key' + ${key};`);
} }
ctx.addLine(`let def${defID};`); ctx.addLine(`let def${defID};`);
let templateID = key
? `key${keyID}` let locationExpr = `\`__${ctx.generateID()}__`;
: ctx.inLoop for (let i = 0; i < ctx.loopNumber - 1; i++) {
? ctx.currentKey locationExpr += `\${i${i + 1}}__`;
? `String(${ctx.currentKey} + '_k_' + i + '_c_' + ${componentID} )`
: `String(-${componentID} - i)`
: String(componentID);
if (ctx.allowMultipleRoots) {
templateID = `"_slot_${templateID}"`;
} }
if (key || ctx.inLoop) { if (key || ctx.currentKey) {
let id = ctx.generateID(); const k = key ? `key${keyID}` : ctx.currentKey;
ctx.addLine(`let templateId${id} = ${templateID};`); ctx.addLine(`let templateId${componentID} = ${locationExpr}\` + ${k};`);
templateID = `templateId${id}`; } else {
locationExpr += ctx.loopNumber ? `\${i${ctx.loopNumber}}__\`` : "`";
ctx.addLine(`let templateId${componentID} = ${locationExpr};`);
} }
const templateId = `templateId${componentID}`;
let ref = node.getAttribute("t-ref"); let ref = node.getAttribute("t-ref");
let refExpr = ""; let refExpr = "";
@@ -320,7 +318,7 @@ QWeb.addDirective({
.map(function([eventName, mods, handlerName, extraArgs]) { .map(function([eventName, mods, handlerName, extraArgs]) {
let params = "owner"; let params = "owner";
if (extraArgs) { if (extraArgs) {
if (ctx.inLoop) { if (ctx.loopNumber) {
let argId = ctx.generateID(); let argId = ctx.generateID();
// we need to evaluate the arguments now, because the handler will // we need to evaluate the arguments now, because the handler will
// be set asynchronously later when the widget is ready, and the // be set asynchronously later when the widget is ready, and the
@@ -352,7 +350,7 @@ QWeb.addDirective({
} }
ctx.addLine( ctx.addLine(
`let w${componentID} = ${templateID} in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[${templateID}]] : false;` `let w${componentID} = ${templateId} in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[${templateId}]] : false;`
); );
if (ctx.parentNode) { if (ctx.parentNode) {
ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`); ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`);
@@ -418,7 +416,7 @@ QWeb.addDirective({
ctx.addLine(`utils.validateProps(W${componentID}, props${componentID})`); ctx.addLine(`utils.validateProps(W${componentID}, props${componentID})`);
} }
ctx.addLine(`w${componentID} = new W${componentID}(parent, props${componentID});`); ctx.addLine(`w${componentID} = new W${componentID}(parent, props${componentID});`);
ctx.addLine(`parent.__owl__.cmap[${templateID}] = w${componentID}.__owl__.id;`); ctx.addLine(`parent.__owl__.cmap[${templateId}] = w${componentID}.__owl__.id;`);
// SLOTS // SLOTS
const varDefs: string[] = []; const varDefs: string[] = [];
@@ -470,7 +468,7 @@ QWeb.addDirective({
registerCode = `utils.defineProxy(vn${ctx.rootNode}, pvnode);`; registerCode = `utils.defineProxy(vn${ctx.rootNode}, pvnode);`;
} }
ctx.addLine( ctx.addLine(
`def${defID} = def${defID}.then(vnode=>{if (w${componentID}.__owl__.isDestroyed){return}${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}}}});${registerCode}w${componentID}.__owl__.pvnode = pvnode;});` `def${defID} = def${defID}.then(vnode=>{if (w${componentID}.__owl__.isDestroyed){return}${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}}}});${registerCode}w${componentID}.__owl__.pvnode = pvnode;});`
); );
ctx.addElse(); ctx.addElse();
+8 -7
View File
@@ -275,7 +275,7 @@ QWeb.addDirective({
priority: 10, priority: 10,
atNodeEncounter({ node, qweb, ctx }): boolean { atNodeEncounter({ node, qweb, ctx }): boolean {
ctx.rootContext.shouldProtectContext = true; ctx.rootContext.shouldProtectContext = true;
ctx = ctx.subContext("inLoop", true); ctx = ctx.subContext("loopNumber", ctx.loopNumber + 1);
const elems = node.getAttribute("t-foreach")!; const elems = node.getAttribute("t-foreach")!;
const name = node.getAttribute("t-as")!; const name = node.getAttribute("t-as")!;
let arrayID = ctx.generateID(); let arrayID = ctx.generateID();
@@ -289,13 +289,14 @@ QWeb.addDirective({
ctx.addLine(`_${valuesID} = Object.values(_${arrayID});`); ctx.addLine(`_${valuesID} = Object.values(_${arrayID});`);
ctx.closeIf(); ctx.closeIf();
ctx.addLine(`var _length${keysID} = _${keysID}.length;`); ctx.addLine(`var _length${keysID} = _${keysID}.length;`);
ctx.addLine(`for (let i = 0; i < _length${keysID}; i++) {`); const loopVar = `i${ctx.loopNumber}`;
ctx.addLine(`for (let ${loopVar} = 0; ${loopVar} < _length${keysID}; ${loopVar}++) {`);
ctx.indent(); ctx.indent();
ctx.addToScope(name + "_first", "i === 0"); ctx.addToScope(name + "_first", `${loopVar} === 0`);
ctx.addToScope(name + "_last", `i === _length${keysID} - 1`); ctx.addToScope(name + "_last", `${loopVar} === _length${keysID} - 1`);
ctx.addToScope(name + "_index", "i"); ctx.addToScope(name + "_index", loopVar);
ctx.addToScope(name, `_${keysID}[i]`); ctx.addToScope(name, `_${keysID}[${loopVar}]`);
ctx.addToScope(name + "_value", `_${valuesID}[i]`); ctx.addToScope(name + "_value", `_${valuesID}[${loopVar}]`);
const nodeCopy = <Element>node.cloneNode(true); const nodeCopy = <Element>node.cloneNode(true);
let shouldWarn = nodeCopy.tagName !== "t" && !nodeCopy.hasAttribute("t-key"); let shouldWarn = nodeCopy.tagName !== "t" && !nodeCopy.hasAttribute("t-key");
if (!shouldWarn && node.tagName === "t") { if (!shouldWarn && node.tagName === "t") {
+1 -1
View File
@@ -24,7 +24,7 @@ export class CompilationContext {
shouldDefineResult: boolean = true; shouldDefineResult: boolean = true;
shouldProtectContext: boolean = false; shouldProtectContext: boolean = false;
shouldTrackScope: boolean = false; shouldTrackScope: boolean = false;
inLoop: boolean = false; loopNumber: number = 0;
inPreTag: boolean = false; inPreTag: boolean = false;
templateName: string; templateName: string;
allowMultipleRoots: boolean = false; allowMultipleRoots: boolean = false;
+8 -6
View File
@@ -12,7 +12,8 @@ exports[`animations t-transition combined with component 1`] = `
var vn1 = h('div', p1, c1); var vn1 = h('div', p1, c1);
//COMPONENT //COMPONENT
let def3; let def3;
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false; let templateId4 = \`__5__\`;
let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false;
let _2_index = c1.length; let _2_index = c1.length;
c1.push(null); c1.push(null);
let props4 = {}; let props4 = {};
@@ -29,9 +30,9 @@ exports[`animations t-transition combined with component 1`] = `
let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child']; let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child'];
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
w4 = new W4(parent, props4); w4 = new W4(parent, props4);
parent.__owl__.cmap[4] = w4.__owl__.id; parent.__owl__.cmap[templateId4] = w4.__owl__.id;
def3 = w4.__prepare(extra.fiber, undefined, undefined); def3 = w4.__prepare(extra.fiber, undefined, undefined);
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => { def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
w4.destroy(); w4.destroy();
}; };
utils.transitionRemove(vn, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); utils.transitionRemove(vn, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
@@ -57,7 +58,8 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
if (context['state'].display) { if (context['state'].display) {
//COMPONENT //COMPONENT
let def3; let def3;
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false; let templateId4 = \`__5__\`;
let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false;
let _2_index = c1.length; let _2_index = c1.length;
c1.push(null); c1.push(null);
let props4 = {}; let props4 = {};
@@ -74,9 +76,9 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child']; let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child'];
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
w4 = new W4(parent, props4); w4 = new W4(parent, props4);
parent.__owl__.cmap[4] = w4.__owl__.id; parent.__owl__.cmap[templateId4] = w4.__owl__.id;
def3 = w4.__prepare(extra.fiber, undefined, undefined); def3 = w4.__prepare(extra.fiber, undefined, undefined);
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => { def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
w4.destroy(); w4.destroy();
}; };
utils.transitionRemove(vn, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); utils.transitionRemove(vn, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
+2 -2
View File
@@ -251,11 +251,11 @@ describe("animations", () => {
widget.state.display = false; widget.state.display = false;
patchNextFrame(cb => { patchNextFrame(cb => {
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe(
'<div><span class="chimay-leave chimay-leave-active" data-owl-key="4">blue</span></div>' '<div><span class="chimay-leave chimay-leave-active" data-owl-key="__5__">blue</span></div>'
); );
cb(); cb();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe(
'<div><span class="chimay-leave-active chimay-leave-to" data-owl-key="4">blue</span></div>' '<div><span class="chimay-leave-active chimay-leave-to" data-owl-key="__5__">blue</span></div>'
); );
def.resolve(); def.resolve();
}); });
File diff suppressed because it is too large Load Diff
@@ -16,7 +16,8 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
var vn1 = h('div', p1, c1); var vn1 = h('div', p1, c1);
//COMPONENT //COMPONENT
let def3; let def3;
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false; let templateId4 = \`__5__\`;
let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false;
let _2_index = c1.length; let _2_index = c1.length;
c1.push(null); c1.push(null);
let props4 = {message:1}; let props4 = {message:1};
@@ -34,9 +35,9 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
utils.validateProps(W4, props4) utils.validateProps(W4, props4)
w4 = new W4(parent, props4); w4 = new W4(parent, props4);
parent.__owl__.cmap[4] = w4.__owl__.id; parent.__owl__.cmap[templateId4] = w4.__owl__.id;
def3 = w4.__prepare(extra.fiber, undefined, undefined); def3 = w4.__prepare(extra.fiber, undefined, undefined);
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
} else { } else {
utils.validateProps(w4.constructor, props4) utils.validateProps(w4.constructor, props4)
def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined);
+46
View File
@@ -182,6 +182,52 @@ describe("basic widget properties", () => {
await widget.mount(fixture); await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>child</span><span>child</span></div>"); expect(fixture.innerHTML).toBe("<div><span>child</span><span>child</span></div>");
}); });
test("reconciliation alg works for t-foreach in t-foreach", async () => {
const warn = console.warn;
console.warn = () => {};
class Child extends Component<any, any> {
static template = xml`<div><t t-esc="props.blip"/></div>`;
}
class Parent extends Component<any, any> {
static template = xml`
<div>
<t t-foreach="state.s" t-as="section">
<t t-foreach="section.blips" t-as="blip">
<Child blip="blip"/>
</t>
</t>
</div>`;
static components = { Child };
state = { s: [{ blips: ["a1", "a2"] }, { blips: ["b1"] }] };
}
const widget = new Parent(env);
await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>a1</div><div>a2</div><div>b1</div></div>");
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
console.warn = warn;
});
test("same t-keys in two different places", async () => {
class Child extends Component<any, any> {
static template = xml`<span><t t-esc="props.blip"/></span>`;
}
class Parent extends Component<any, any> {
static template = xml`
<div>
<div><Child t-key="1" blip="'1'"/></div>
<div><Child t-key="1" blip="'2'"/></div>
</div>`;
static components = { Child };
}
const widget = new Parent(env);
await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div><span>1</span></div><div><span>2</span></div></div>");
});
}); });
describe("lifecycle hooks", () => { describe("lifecycle hooks", () => {
+147 -96
View File
@@ -325,12 +325,12 @@ exports[`foreach does not pollute the rendering context 1`] = `
_4 = Object.values(_2); _4 = Object.values(_2);
} }
var _length3 = _3.length; var _length3 = _3.length;
for (let i = 0; i < _length3; i++) { for (let i1 = 0; i1 < _length3; i1++) {
context.item_first = i === 0; context.item_first = i1 === 0;
context.item_last = i === _length3 - 1; context.item_last = i1 === _length3 - 1;
context.item_index = i; context.item_index = i1;
context.item = _3[i]; context.item = _3[i1];
context.item_value = _4[i]; context.item_value = _4[i1];
var _5 = context['item']; var _5 = context['item'];
if (_5 || _5 === 0) { if (_5 || _5 === 0) {
c1.push({text: _5}); c1.push({text: _5});
@@ -355,12 +355,12 @@ exports[`foreach iterate on items (on a element node) 1`] = `
_4 = Object.values(_2); _4 = Object.values(_2);
} }
var _length3 = _3.length; var _length3 = _3.length;
for (let i = 0; i < _length3; i++) { for (let i1 = 0; i1 < _length3; i1++) {
context.item_first = i === 0; context.item_first = i1 === 0;
context.item_last = i === _length3 - 1; context.item_last = i1 === _length3 - 1;
context.item_index = i; context.item_index = i1;
context.item = _3[i]; context.item = _3[i1];
context.item_value = _4[i]; context.item_value = _4[i1];
const nodeKey5 = context['item'] const nodeKey5 = context['item']
let c5 = [], p5 = {key:nodeKey5}; let c5 = [], p5 = {key:nodeKey5};
var vn5 = h('span', p5, c5); var vn5 = h('span', p5, c5);
@@ -389,12 +389,12 @@ exports[`foreach iterate on items 1`] = `
_4 = Object.values(_2); _4 = Object.values(_2);
} }
var _length3 = _3.length; var _length3 = _3.length;
for (let i = 0; i < _length3; i++) { for (let i1 = 0; i1 < _length3; i1++) {
context.item_first = i === 0; context.item_first = i1 === 0;
context.item_last = i === _length3 - 1; context.item_last = i1 === _length3 - 1;
context.item_index = i; context.item_index = i1;
context.item = _3[i]; context.item = _3[i1];
context.item_value = _4[i]; context.item_value = _4[i1];
c1.push({text: \` [\`}); c1.push({text: \` [\`});
var _5 = context['item_index']; var _5 = context['item_index'];
if (_5 || _5 === 0) { if (_5 || _5 === 0) {
@@ -431,12 +431,12 @@ exports[`foreach iterate, dict param 1`] = `
_4 = Object.values(_2); _4 = Object.values(_2);
} }
var _length3 = _3.length; var _length3 = _3.length;
for (let i = 0; i < _length3; i++) { for (let i1 = 0; i1 < _length3; i1++) {
context.item_first = i === 0; context.item_first = i1 === 0;
context.item_last = i === _length3 - 1; context.item_last = i1 === _length3 - 1;
context.item_index = i; context.item_index = i1;
context.item = _3[i]; context.item = _3[i1];
context.item_value = _4[i]; context.item_value = _4[i1];
c1.push({text: \` [\`}); c1.push({text: \` [\`});
var _5 = context['item_index']; var _5 = context['item_index'];
if (_5 || _5 === 0) { if (_5 || _5 === 0) {
@@ -473,12 +473,12 @@ exports[`foreach iterate, position 1`] = `
_4 = Object.values(_2); _4 = Object.values(_2);
} }
var _length3 = _3.length; var _length3 = _3.length;
for (let i = 0; i < _length3; i++) { for (let i1 = 0; i1 < _length3; i1++) {
context.elem_first = i === 0; context.elem_first = i1 === 0;
context.elem_last = i === _length3 - 1; context.elem_last = i1 === _length3 - 1;
context.elem_index = i; context.elem_index = i1;
context.elem = _3[i]; context.elem = _3[i1];
context.elem_value = _4[i]; context.elem_value = _4[i1];
c1.push({text: \` -\`}); c1.push({text: \` -\`});
if (context['elem_first']) { if (context['elem_first']) {
c1.push({text: \` first\`}); c1.push({text: \` first\`});
@@ -497,6 +497,57 @@ exports[`foreach iterate, position 1`] = `
}" }"
`; `;
exports[`foreach t-foreach in t-forach 1`] = `
"function anonymous(context,extra
) {
context = Object.create(context);
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
var _2 = context['numbers'];
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
var _3 = _4 = _2;
if (!(_2 instanceof Array)) {
_3 = Object.keys(_2);
_4 = Object.values(_2);
}
var _length3 = _3.length;
for (let i1 = 0; i1 < _length3; i1++) {
context.number_first = i1 === 0;
context.number_last = i1 === _length3 - 1;
context.number_index = i1;
context.number = _3[i1];
context.number_value = _4[i1];
var _5 = context['letters'];
if (!_5) { throw new Error('QWeb error: Invalid loop expression')}
var _6 = _7 = _5;
if (!(_5 instanceof Array)) {
_6 = Object.keys(_5);
_7 = Object.values(_5);
}
var _length6 = _6.length;
for (let i2 = 0; i2 < _length6; i2++) {
context.letter_first = i2 === 0;
context.letter_last = i2 === _length6 - 1;
context.letter_index = i2;
context.letter = _6[i2];
context.letter_value = _7[i2];
c1.push({text: \` [\`});
var _8 = context['number'];
if (_8 || _8 === 0) {
c1.push({text: _8});
}
var _9 = context['letter'];
if (_9 || _9 === 0) {
c1.push({text: _9});
}
c1.push({text: \`] \`});
}
}
return vn1;
}"
`;
exports[`foreach warn if no key in some case 1`] = ` exports[`foreach warn if no key in some case 1`] = `
"function anonymous(context,extra "function anonymous(context,extra
) { ) {
@@ -512,12 +563,12 @@ exports[`foreach warn if no key in some case 1`] = `
_4 = Object.values(_2); _4 = Object.values(_2);
} }
var _length3 = _3.length; var _length3 = _3.length;
for (let i = 0; i < _length3; i++) { for (let i1 = 0; i1 < _length3; i1++) {
context.item_first = i === 0; context.item_first = i1 === 0;
context.item_last = i === _length3 - 1; context.item_last = i1 === _length3 - 1;
context.item_index = i; context.item_index = i1;
context.item = _3[i]; context.item = _3[i1];
context.item_value = _4[i]; context.item_value = _4[i1];
let c5 = [], p5 = {key:5}; let c5 = [], p5 = {key:5};
var vn5 = h('span', p5, c5); var vn5 = h('span', p5, c5);
c1.push(vn5); c1.push(vn5);
@@ -575,12 +626,12 @@ exports[`misc global 1`] = `
_4 = Object.values(_2); _4 = Object.values(_2);
} }
var _length3 = _3.length; var _length3 = _3.length;
for (let i = 0; i < _length3; i++) { for (let i1 = 0; i1 < _length3; i1++) {
context.value_first = i === 0; context.value_first = i1 === 0;
context.value_last = i === _length3 - 1; context.value_last = i1 === _length3 - 1;
context.value_index = i; context.value_index = i1;
context.value = _3[i]; context.value = _3[i1];
context.value_value = _4[i]; context.value_value = _4[i1];
let c5 = [], p5 = {key:5}; let c5 = [], p5 = {key:5};
var vn5 = h('span', p5, c5); var vn5 = h('span', p5, c5);
c1.push(vn5); c1.push(vn5);
@@ -940,16 +991,16 @@ exports[`t-call (template calling recursive template, part 2 1`] = `
_8 = Object.values(_6); _8 = Object.values(_6);
} }
var _length7 = _7.length; var _length7 = _7.length;
for (let i = 0; i < _length7; i++) { for (let i1 = 0; i1 < _length7; i1++) {
context.subtree_first = i === 0; context.subtree_first = i1 === 0;
scope.subtree_first = context.subtree_first; scope.subtree_first = context.subtree_first;
context.subtree_last = i === _length7 - 1; context.subtree_last = i1 === _length7 - 1;
scope.subtree_last = context.subtree_last; scope.subtree_last = context.subtree_last;
context.subtree_index = i; context.subtree_index = i1;
scope.subtree_index = context.subtree_index; scope.subtree_index = context.subtree_index;
context.subtree = _7[i]; context.subtree = _7[i1];
scope.subtree = context.subtree; scope.subtree = context.subtree;
context.subtree_value = _8[i]; context.subtree_value = _8[i1];
scope.subtree_value = context.subtree_value; scope.subtree_value = context.subtree_value;
this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c3, fiber: {vars: {_v0: context['subtree']}, scope}})); this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c3, fiber: {vars: {_v0: context['subtree']}, scope}}));
} }
@@ -986,16 +1037,16 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
_17 = Object.values(_15); _17 = Object.values(_15);
} }
var _length16 = _16.length; var _length16 = _16.length;
for (let i = 0; i < _length16; i++) { for (let i1 = 0; i1 < _length16; i1++) {
context.subtree_first = i === 0; context.subtree_first = i1 === 0;
scope.subtree_first = context.subtree_first; scope.subtree_first = context.subtree_first;
context.subtree_last = i === _length16 - 1; context.subtree_last = i1 === _length16 - 1;
scope.subtree_last = context.subtree_last; scope.subtree_last = context.subtree_last;
context.subtree_index = i; context.subtree_index = i1;
scope.subtree_index = context.subtree_index; scope.subtree_index = context.subtree_index;
context.subtree = _16[i]; context.subtree = _16[i1];
scope.subtree = context.subtree; scope.subtree = context.subtree;
context.subtree_value = _17[i]; context.subtree_value = _17[i1];
scope.subtree_value = context.subtree_value; scope.subtree_value = context.subtree_value;
this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c12, fiber: {vars: {_v0: context['subtree']}, scope}})); this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c12, fiber: {vars: {_v0: context['subtree']}, scope}}));
} }
@@ -1031,16 +1082,16 @@ exports[`t-call (template calling recursive template, part 3 1`] = `
_8 = Object.values(_6); _8 = Object.values(_6);
} }
var _length7 = _7.length; var _length7 = _7.length;
for (let i = 0; i < _length7; i++) { for (let i1 = 0; i1 < _length7; i1++) {
context.subtree_first = i === 0; context.subtree_first = i1 === 0;
scope.subtree_first = context.subtree_first; scope.subtree_first = context.subtree_first;
context.subtree_last = i === _length7 - 1; context.subtree_last = i1 === _length7 - 1;
scope.subtree_last = context.subtree_last; scope.subtree_last = context.subtree_last;
context.subtree_index = i; context.subtree_index = i1;
scope.subtree_index = context.subtree_index; scope.subtree_index = context.subtree_index;
context.subtree = _7[i]; context.subtree = _7[i1];
scope.subtree = context.subtree; scope.subtree = context.subtree;
context.subtree_value = _8[i]; context.subtree_value = _8[i1];
scope.subtree_value = context.subtree_value; scope.subtree_value = context.subtree_value;
this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c3, fiber: {vars: {_v0: context['subtree']}, scope}})); this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c3, fiber: {vars: {_v0: context['subtree']}, scope}}));
} }
@@ -1077,16 +1128,16 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
_17 = Object.values(_15); _17 = Object.values(_15);
} }
var _length16 = _16.length; var _length16 = _16.length;
for (let i = 0; i < _length16; i++) { for (let i1 = 0; i1 < _length16; i1++) {
context.subtree_first = i === 0; context.subtree_first = i1 === 0;
scope.subtree_first = context.subtree_first; scope.subtree_first = context.subtree_first;
context.subtree_last = i === _length16 - 1; context.subtree_last = i1 === _length16 - 1;
scope.subtree_last = context.subtree_last; scope.subtree_last = context.subtree_last;
context.subtree_index = i; context.subtree_index = i1;
scope.subtree_index = context.subtree_index; scope.subtree_index = context.subtree_index;
context.subtree = _16[i]; context.subtree = _16[i1];
scope.subtree = context.subtree; scope.subtree = context.subtree;
context.subtree_value = _17[i]; context.subtree_value = _17[i1];
scope.subtree_value = context.subtree_value; scope.subtree_value = context.subtree_value;
this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c12, fiber: {vars: {_v0: context['subtree']}, scope}})); this.recursiveFns['__10'].call(this, context, Object.assign({}, extra, {parentNode: c12, fiber: {vars: {_v0: context['subtree']}, scope}}));
} }
@@ -1446,12 +1497,12 @@ exports[`t-key t-key directive in a list 1`] = `
_4 = Object.values(_2); _4 = Object.values(_2);
} }
var _length3 = _3.length; var _length3 = _3.length;
for (let i = 0; i < _length3; i++) { for (let i1 = 0; i1 < _length3; i1++) {
context.beer_first = i === 0; context.beer_first = i1 === 0;
context.beer_last = i === _length3 - 1; context.beer_last = i1 === _length3 - 1;
context.beer_index = i; context.beer_index = i1;
context.beer = _3[i]; context.beer = _3[i1];
context.beer_value = _4[i]; context.beer_value = _4[i1];
const nodeKey5 = context['beer'].id const nodeKey5 = context['beer'].id
let c5 = [], p5 = {key:nodeKey5}; let c5 = [], p5 = {key:nodeKey5};
var vn5 = h('li', p5, c5); var vn5 = h('li', p5, c5);
@@ -1548,12 +1599,12 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
_4 = Object.values(_2); _4 = Object.values(_2);
} }
var _length3 = _3.length; var _length3 = _3.length;
for (let i = 0; i < _length3; i++) { for (let i1 = 0; i1 < _length3; i1++) {
context.action_first = i === 0; context.action_first = i1 === 0;
context.action_last = i === _length3 - 1; context.action_last = i1 === _length3 - 1;
context.action_index = i; context.action_index = i1;
context.action = _3[i]; context.action = _3[i1];
context.action_value = _4[i]; context.action_value = _4[i1];
const nodeKey5 = context['action_index'] const nodeKey5 = context['action_index']
let c5 = [], p5 = {key:nodeKey5}; let c5 = [], p5 = {key:nodeKey5};
var vn5 = h('li', p5, c5); var vn5 = h('li', p5, c5);
@@ -1750,12 +1801,12 @@ exports[`t-on t-on with prevent modifier in t-foreach 1`] = `
_4 = Object.values(_2); _4 = Object.values(_2);
} }
var _length3 = _3.length; var _length3 = _3.length;
for (let i = 0; i < _length3; i++) { for (let i1 = 0; i1 < _length3; i1++) {
context.project_first = i === 0; context.project_first = i1 === 0;
context.project_last = i === _length3 - 1; context.project_last = i1 === _length3 - 1;
context.project_index = i; context.project_index = i1;
context.project = _3[i]; context.project = _3[i1];
context.project_value = _4[i]; context.project_value = _4[i1];
var _5 = '#'; var _5 = '#';
const nodeKey6 = context['project'] const nodeKey6 = context['project']
let c6 = [], p6 = {key:nodeKey6,attrs:{href: _5},on:{}}; let c6 = [], p6 = {key:nodeKey6,attrs:{href: _5},on:{}};
@@ -1960,12 +2011,12 @@ exports[`t-ref refs in a loop 1`] = `
_4 = Object.values(_2); _4 = Object.values(_2);
} }
var _length3 = _3.length; var _length3 = _3.length;
for (let i = 0; i < _length3; i++) { for (let i1 = 0; i1 < _length3; i1++) {
context.item_first = i === 0; context.item_first = i1 === 0;
context.item_last = i === _length3 - 1; context.item_last = i1 === _length3 - 1;
context.item_index = i; context.item_index = i1;
context.item = _3[i]; context.item = _3[i1];
context.item_value = _4[i]; context.item_value = _4[i1];
const nodeKey5 = context['item'] const nodeKey5 = context['item']
let c5 = [], p5 = {key:nodeKey5}; let c5 = [], p5 = {key:nodeKey5};
var vn5 = h('div', p5, c5); var vn5 = h('div', p5, c5);
@@ -2126,12 +2177,12 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
_5 = Object.values(_3); _5 = Object.values(_3);
} }
var _length4 = _4.length; var _length4 = _4.length;
for (let i = 0; i < _length4; i++) { for (let i1 = 0; i1 < _length4; i1++) {
context.elem_first = i === 0; context.elem_first = i1 === 0;
context.elem_last = i === _length4 - 1; context.elem_last = i1 === _length4 - 1;
context.elem_index = i; context.elem_index = i1;
context.elem = _4[i]; context.elem = _4[i1];
context.elem_value = _5[i]; context.elem_value = _5[i1];
const nodeKey6 = context['elem_index'] const nodeKey6 = context['elem_index']
let c6 = [], p6 = {key:nodeKey6}; let c6 = [], p6 = {key:nodeKey6};
var vn6 = h('div', p6, c6); var vn6 = h('div', p6, c6);
+17
View File
@@ -749,6 +749,23 @@ describe("foreach", () => {
expect(Object.keys(context).length).toBe(0); expect(Object.keys(context).length).toBe(0);
}); });
test("t-foreach in t-forach", () => {
qweb.addTemplate(
"test",
`<div>
<t t-foreach="numbers" t-as="number">
<t t-foreach="letters" t-as="letter">
[<t t-esc="number"/><t t-esc="letter"/>]
</t>
</t>
</div>`
);
const context = { numbers: [1, 2, 3], letters: ["a", "b"] };
expect(renderToString(qweb, "test", context)).toBe(
"<div> [1a] [1b] [2a] [2b] [3a] [3b] </div>"
);
});
test("throws error if invalid loop expression", () => { test("throws error if invalid loop expression", () => {
qweb.addTemplate( qweb.addTemplate(
"test", "test",
@@ -13,8 +13,8 @@ exports[`RouteComponent can render simple cases 1`] = `
//COMPONENT //COMPONENT
let key4 = 'key' + context['env'].router.currentRouteName; let key4 = 'key' + context['env'].router.currentRouteName;
let def2; let def2;
let templateId5 = key4; let templateId3 = \`__5__\` + key4;
let w3 = templateId5 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId5]] : false; let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false;
let vn6 = {}; let vn6 = {};
result = vn6; result = vn6;
let props3 = Object.assign({}, context['env'].router.currentParams); let props3 = Object.assign({}, context['env'].router.currentParams);
@@ -31,9 +31,9 @@ exports[`RouteComponent can render simple cases 1`] = `
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['routeComponent']; let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['routeComponent'];
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
w3 = new W3(parent, props3); w3 = new W3(parent, props3);
parent.__owl__.cmap[templateId5] = w3.__owl__.id; parent.__owl__.cmap[templateId3] = w3.__owl__.id;
def2 = w3.__prepare(extra.fiber, undefined, undefined); def2 = w3.__prepare(extra.fiber, undefined, undefined);
def2 = def2.then(vnode=>{if (w3.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId5, hook: {insert(vn) {let nvn=w3.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});utils.defineProxy(vn6, pvnode);w3.__owl__.pvnode = pvnode;}); def2 = def2.then(vnode=>{if (w3.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId3, hook: {insert(vn) {let nvn=w3.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});utils.defineProxy(vn6, pvnode);w3.__owl__.pvnode = pvnode;});
} else { } else {
def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined); def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined);
def2 = def2.then(()=>{if (w3.__owl__.isDestroyed) {return};let pvnode=w3.__owl__.pvnode;utils.defineProxy(vn6, pvnode);}); def2 = def2.then(()=>{if (w3.__owl__.isDestroyed) {return};let pvnode=w3.__owl__.pvnode;utils.defineProxy(vn6, pvnode);});