[FIX] component: scoping issue with t-on and t-foreach

closes #193
This commit is contained in:
Géry Debongnie
2019-06-21 16:23:42 +02:00
parent e6a5934162
commit 63a8fcd7e2
3 changed files with 107 additions and 5 deletions
+21 -5
View File
@@ -479,9 +479,21 @@ QWeb.addDirective({
} }
let eventsCode = events let eventsCode = events
.map(function([eventName, mods, handlerName, extraArgs]) { .map(function([eventName, mods, handlerName, extraArgs]) {
let params = extraArgs let params = "owner";
? `owner, ${ctx.formatExpression(extraArgs)}` if (extraArgs) {
: "owner"; if (ctx.inLoop) {
let argId = ctx.generateID();
// we need to evaluate the arguments now, because the handler will
// be set asynchronously later when the widget is ready, and the
// context might be different.
ctx.addLine(
`let arg${argId} = ${ctx.formatExpression(extraArgs)};`
);
params = `owner, arg${argId}`;
} else {
params = `owner, ${ctx.formatExpression(extraArgs)}`;
}
}
let handler; let handler;
if (mods.length > 0) { if (mods.length > 0) {
handler = `function (e) {`; handler = `function (e) {`;
@@ -541,7 +553,9 @@ QWeb.addDirective({
ctx.addLine( ctx.addLine(
`if (!W${componentID}) {throw new Error('Cannot find the definition of component "' + componentKey${componentID} + '"')}` `if (!W${componentID}) {throw new Error('Cannot find the definition of component "' + componentKey${componentID} + '"')}`
); );
ctx.addLine(`w${componentID} = new W${componentID}(owner, props${componentID});`); ctx.addLine(
`w${componentID} = new W${componentID}(owner, props${componentID});`
);
ctx.addLine( ctx.addLine(
`context.__owl__.cmap[${templateID}] = w${componentID}.__owl__.id;` `context.__owl__.cmap[${templateID}] = w${componentID}.__owl__.id;`
); );
@@ -579,7 +593,9 @@ QWeb.addDirective({
ctx.addElse(); ctx.addElse();
// need to update component // need to update component
const patchQueueCode = async ? `patchQueue${componentID}` : "extra.patchQueue"; const patchQueueCode = async
? `patchQueue${componentID}`
: "extra.patchQueue";
ctx.addLine( ctx.addLine(
`def${defID} = def${defID} || w${componentID}._updateProps(props${componentID}, extra.forceUpdate, ${patchQueueCode});` `def${defID} = def${defID} || w${componentID}._updateProps(props${componentID}, extra.forceUpdate, ${patchQueueCode});`
); );
@@ -827,6 +827,64 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
}" }"
`; `;
exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument on a t-foreach 1`] = `
"function anonymous(context,extra
) {
let utils = this.utils;
let QWeb = this.constructor;
let owner = context;
context = Object.create(context);
var h = this.utils.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
var _2 = context['props'].items;
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 i = 0; i < _length3; i++) {
context.item_first = i === 0;
context.item_last = i === _length3 - 1;
context.item_index = i;
context.item = _3[i];
context.item_value = _4[i];
//COMPONENT
let key8 = context['item'];
let def6;
let arg9 = context['item'];
let w7 = key8 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[key8]] : false;
let _5_index = c1.length;
c1.push(null);
let props7 = {};
if (w7 && w7.__owl__.renderPromise && !w7.__owl__.vnode) {
if (utils.shallowEqual(props7, w7.__owl__.renderProps)) {
def6 = w7.__owl__.renderPromise;
} else {
w7.destroy();
w7 = false;
}
}
if (!w7) {
let componentKey7 = \`Child\`;
let W7 = context.components && context.components[componentKey7] || QWeb.components[componentKey7];
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')}
w7 = new W7(owner, props7);
context.__owl__.cmap[key8] = w7.__owl__.id;
def6 = w7._prepare();
def6 = def6.then(vnode=>{vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', owner['onEv'].bind(owner, arg9));}};let pvnode=h(vnode.sel, {key: key8, hook: {insert(vn) {let nvn=w7._mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});c1[_5_index]=pvnode;w7.__owl__.pvnode = pvnode;});
} else {
def6 = def6 || w7._updateProps(props7, extra.forceUpdate, extra.patchQueue);
def6 = def6.then(()=>{if (w7.__owl__.isDestroyed) {return};let pvnode=w7.__owl__.pvnode;c1[_5_index]=pvnode;});
}
extra.promises.push(def6);
}
return vn1;
}"
`;
exports[`t-model directive .lazy modifier 1`] = ` exports[`t-model directive .lazy modifier 1`] = `
"function anonymous(context,extra "function anonymous(context,extra
) { ) {
+28
View File
@@ -1888,6 +1888,34 @@ describe("random stuff/miscellaneous", () => {
expect(fixture.innerHTML).toBe("<div>txttxt<div></div></div>"); expect(fixture.innerHTML).toBe("<div>txttxt<div></div></div>");
}); });
test("t-on with handler bound to dynamic argument on a t-foreach", async () => {
expect.assertions(3);
env.qweb.addTemplates(`
<templates>
<div t-name="ParentWidget">
<t t-foreach="props.items" t-as="item">
<Child t-key="item" t-on-ev="onEv(item)"/>
</t>
</div>
</templates>
`);
const items = [1, 2, 3, 4];
class ParentWidget extends Widget {
components = { Child };
onEv(n, ev) {
expect(n).toBe(1);
expect(ev.detail).toBe(43);
}
}
class Child extends Widget {}
const widget = new ParentWidget(env, { items });
await widget.mount(fixture);
children(widget)[0].trigger("ev", 43);
expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot();
});
test("updating widget immediately", async () => { test("updating widget immediately", async () => {
// in this situation, we protect against a bug that occurred: because of the // in this situation, we protect against a bug that occurred: because of the
// interplay between components and vnodes, a sub widget vnode was patched // interplay between components and vnodes, a sub widget vnode was patched