diff --git a/web/static/src/ts/core/qweb_vdom.ts b/web/static/src/ts/core/qweb_vdom.ts index 8ab751c8..5e65f37b 100644 --- a/web/static/src/ts/core/qweb_vdom.ts +++ b/web/static/src/ts/core/qweb_vdom.ts @@ -330,6 +330,8 @@ export class QWeb { fullName: string; }[] = []; + let withHandlers = false; + for (let directive of this.directives) { // const value = attributes[i].textContent!; let fullName; @@ -342,13 +344,13 @@ export class QWeb { ) { fullName = name; value = attributes[i].textContent; + validDirectives.push({ directive, value, fullName }); + if (directive.name === "on") { + withHandlers = true; + } } } - if (fullName) { - validDirectives.push({ directive, value, fullName }); - } } - for (let { directive, value, fullName } of validDirectives) { if (directive.atNodeEncounter) { const isDone = directive.atNodeEncounter({ @@ -365,7 +367,7 @@ export class QWeb { } if (node.nodeName !== "t") { - let nodeID = this._compileGenericNode(node, ctx); + let nodeID = this._compileGenericNode(node, ctx, withHandlers); ctx = ctx.withParent(nodeID); for (let { directive, value, fullName } of validDirectives) { @@ -391,7 +393,11 @@ export class QWeb { } } - _compileGenericNode(node: ChildNode, ctx: Context): number { + _compileGenericNode( + node: ChildNode, + ctx: Context, + withHandlers: boolean = true + ): number { // nodeType 1 is generic tag if (node.nodeType !== 1) { throw new Error("unsupported node type"); @@ -458,11 +464,15 @@ export class QWeb { } } let nodeID = ctx.generateID(); - let p = - attrs.length + tattrs.length > 0 - ? `{key:${nodeID},attrs:{${attrs.join(",")}}}` - : `{key:${nodeID}}`; - ctx.addLine(`let c${nodeID} = [], p${nodeID} = ${p};`); + const parts = [`key:${nodeID}`]; + if (attrs.length + tattrs.length > 0) { + parts.push(`attrs:{${attrs.join(",")}}`); + } + if (withHandlers) { + parts.push(`on:{}`); + } + + ctx.addLine(`let c${nodeID} = [], p${nodeID} = {${parts.join(",")}};`); for (let id of tattrs) { ctx.addIf(`_${id} instanceof Array`); ctx.addLine(`p${nodeID}.attrs[_${id}[0]] = _${id}[1];`); @@ -763,15 +773,17 @@ const onDirective: Directive = { }); if (extraArgs) { ctx.addLine( - `p${nodeID}.on = {${eventName}: context['${handler}'].bind(owner, ${qweb._formatExpression( + `p${nodeID}.on['${eventName}'] = context['${handler}'].bind(owner, ${qweb._formatExpression( extraArgs - )})};` + )});` ); } else { ctx.addLine( - `extra.handlers[${nodeID}] = extra.handlers[${nodeID}] || context['${handler}'].bind(owner);` + `extra.handlers['${eventName}' + ${nodeID}] = extra.handlers['${eventName}' + ${nodeID}] || context['${handler}'].bind(owner);` + ); + ctx.addLine( + `p${nodeID}.on['${eventName}'] = extra.handlers['${eventName}' + ${nodeID}];` ); - ctx.addLine(`p${nodeID}.on = {${eventName}: extra.handlers[${nodeID}]};`); } } }; diff --git a/web/static/tests/core/__snapshots__/qweb_vdom.test.ts.snap b/web/static/tests/core/__snapshots__/qweb_vdom.test.ts.snap index 2a279ee6..fb3de8e4 100644 --- a/web/static/tests/core/__snapshots__/qweb_vdom.test.ts.snap +++ b/web/static/tests/core/__snapshots__/qweb_vdom.test.ts.snap @@ -506,7 +506,7 @@ exports[`misc global 1`] = ` context.value_value = _4[i]; c1.push({text: \` \`}); - let c5 = [], p5 = {key:5}; + let c5 = [], p5 = {key:5,on:{}}; let vn5 = h('span', p5, c5); c1.push(vn5); let e6 = context['value']; @@ -516,12 +516,12 @@ exports[`misc global 1`] = ` c1.push({text: \` \`}); let _7 = 'agüero'; - let c8 = [], p8 = {key:8,attrs:{\\"falló\\": _7}}; + let c8 = [], p8 = {key:8,attrs:{\\"falló\\": _7},on:{}}; let vn8 = h('Año', p8, c8); c1.push(vn8); c8.push({text: \` \`}); - let c9 = [], p9 = {key:9}; + let c9 = [], p9 = {key:9,on:{}}; let vn9 = h('span', p9, c9); c8.push(vn9); let e10 = 'aaa'; @@ -532,7 +532,7 @@ exports[`misc global 1`] = ` } c8.push({text: \` \`}); - let c11 = [], p11 = {key:11}; + let c11 = [], p11 = {key:11,on:{}}; let vn11 = h('span', p11, c11); c8.push(vn11); let e12 = context['foo']; @@ -545,7 +545,7 @@ exports[`misc global 1`] = ` \`}); c8.push({text: \` \`}); - let c13 = [], p13 = {key:13}; + let c13 = [], p13 = {key:13,on:{}}; let vn13 = h('span', p13, c13); c8.push(vn13); let e14 = 'bbb'; @@ -561,7 +561,7 @@ exports[`misc global 1`] = ` } c1.push({text: \` \`}); - let c15 = [], p15 = {key:15}; + let c15 = [], p15 = {key:15,on:{}}; let vn15 = h('div', p15, c15); c1.push(vn15); let e16 = context['toto']; @@ -722,7 +722,7 @@ exports[`t-esc escaping on a node 1`] = ` "function anonymous(context,extra ) { let h = this.utils.h; - let c1 = [], p1 = {key:1}; + let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('span', p1, c1); let e2 = 'ok'; if (e2 || e2 === 0) { @@ -736,7 +736,7 @@ exports[`t-esc escaping on a node with a body 1`] = ` "function anonymous(context,extra ) { let h = this.utils.h; - let c1 = [], p1 = {key:1}; + let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('span', p1, c1); let e2 = 'ok'; if (e2 || e2 === 0) { @@ -752,7 +752,7 @@ exports[`t-esc escaping on a node with a body, as a default 1`] = ` "function anonymous(context,extra ) { let h = this.utils.h; - let c1 = [], p1 = {key:1}; + let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('span', p1, c1); let e2 = context['var']; if (e2 || e2 === 0) { @@ -914,10 +914,10 @@ exports[`t-on can bind event handler 1`] = ` ) { let owner = context; let h = this.utils.h; - let c1 = [], p1 = {key:1}; + let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - extra.handlers[1] = extra.handlers[1] || context['add'].bind(owner); - p1.on = {click: extra.handlers[1]}; + extra.handlers['click' + 1] = extra.handlers['click' + 1] || context['add'].bind(owner); + p1.on['click'] = extra.handlers['click' + 1]; c1.push({text: \`Click\`}); return vn1; }" @@ -928,9 +928,9 @@ exports[`t-on can bind handlers with arguments 1`] = ` ) { let owner = context; let h = this.utils.h; - let c1 = [], p1 = {key:1}; + let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - p1.on = {click: context['add'].bind(owner, 5)}; + p1.on['click'] = context['add'].bind(owner, 5); c1.push({text: \`Click\`}); return vn1; }" @@ -961,10 +961,10 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = ` let c5 = [], p5 = {key:5}; let vn5 = h('li', p5, c5); c1.push(vn5); - let c6 = [], p6 = {key:6}; + let c6 = [], p6 = {key:6,on:{}}; let vn6 = h('a', p6, c6); c5.push(vn6); - p6.on = {click: context['activate'].bind(owner, context['action'])}; + p6.on['click'] = context['activate'].bind(owner, context['action']); c6.push({text: \`link\`}); } c1.push({text: \` @@ -973,15 +973,31 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = ` }" `; +exports[`t-on can bind two event handlers 1`] = ` +"function anonymous(context,extra +) { + let owner = context; + let h = this.utils.h; + let c1 = [], p1 = {key:1,on:{}}; + let vn1 = h('button', p1, c1); + extra.handlers['click' + 1] = extra.handlers['click' + 1] || context['handleClick'].bind(owner); + p1.on['click'] = extra.handlers['click' + 1]; + extra.handlers['dblclick' + 1] = extra.handlers['dblclick' + 1] || context['handleDblClick'].bind(owner); + p1.on['dblclick'] = extra.handlers['dblclick' + 1]; + c1.push({text: \`Click\`}); + return vn1; +}" +`; + exports[`t-on handler is bound to proper owner 1`] = ` "function anonymous(context,extra ) { let owner = context; let h = this.utils.h; - let c1 = [], p1 = {key:1}; + let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - extra.handlers[1] = extra.handlers[1] || context['add'].bind(owner); - p1.on = {click: extra.handlers[1]}; + extra.handlers['click' + 1] = extra.handlers['click' + 1] || context['add'].bind(owner); + p1.on['click'] = extra.handlers['click' + 1]; c1.push({text: \`Click\`}); return vn1; }" diff --git a/web/static/tests/core/qweb_vdom.test.ts b/web/static/tests/core/qweb_vdom.test.ts index 0b112fb0..f5006237 100644 --- a/web/static/tests/core/qweb_vdom.test.ts +++ b/web/static/tests/core/qweb_vdom.test.ts @@ -692,6 +692,32 @@ describe("t-on", () => { expect(a).toBe(3); }); + test("can bind two event handlers", () => { + qweb.addTemplate( + "test", + `` + ); + let steps: string[] = []; + const node = renderToDOM( + qweb, + "test", + { + handleClick() { + steps.push("click"); + }, + handleDblClick() { + steps.push("dblclick"); + } + }, + { handlers: [] } + ); + expect(steps).toEqual([]); + (node).click(); + expect(steps).toEqual(["click"]); + (node).dispatchEvent(new Event("dblclick")); + expect(steps).toEqual(["click", "dblclick"]); + }); + test("can bind handlers with arguments", () => { qweb.addTemplate("test", ``); let a = 1;