mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] components: capture context in prop expressions
This commit is contained in:
committed by
Aaron Bohy
parent
c0cf2c9e3d
commit
7143dd3ff5
@@ -344,8 +344,23 @@ export class CodeGenerator {
|
||||
}
|
||||
this.addLine(`}`);
|
||||
}
|
||||
|
||||
captureExpression(expr: string): string {
|
||||
/**
|
||||
* Captures variables that are used inside of an expression. This is useful
|
||||
* because in compiled code, almost all variables are accessed through the ctx
|
||||
* object. In the case of functions, that lookup in the context can be delayed
|
||||
* which can cause issues if the value has changed since the function was
|
||||
* defined.
|
||||
*
|
||||
* @param expr the expression to capture
|
||||
* @param forceCapture whether the expression should capture its scope even if
|
||||
* it doesn't contain a function. Useful when the expression will be used as
|
||||
* a function body.
|
||||
* @returns a new expression that uses the captured values
|
||||
*/
|
||||
captureExpression(expr: string, forceCapture: boolean = false): string {
|
||||
if (!forceCapture && !expr.includes("=>")) {
|
||||
return compileExpr(expr);
|
||||
}
|
||||
const tokens = compileExprToArray(expr);
|
||||
const mapping = new Map<string, string>();
|
||||
return tokens
|
||||
@@ -535,7 +550,7 @@ export class CodeGenerator {
|
||||
if (isDynamic) {
|
||||
const str = ast.ref.replace(
|
||||
INTERP_REGEXP,
|
||||
(expr) => "${" + this.captureExpression(expr.slice(2, -2)) + "}"
|
||||
(expr) => "${" + this.captureExpression(expr.slice(2, -2), true) + "}"
|
||||
);
|
||||
const idx = block!.insertData(`(el) => refs[\`${str}\`] = el`);
|
||||
attrs["block-ref"] = String(idx);
|
||||
@@ -952,7 +967,7 @@ export class CodeGenerator {
|
||||
// props
|
||||
const props: string[] = [];
|
||||
for (let p in ast.props) {
|
||||
props.push(`${p}: ${compileExpr(ast.props[p]) || undefined}`);
|
||||
props.push(`${p}: ${this.captureExpression(ast.props[p]) || undefined}`);
|
||||
}
|
||||
const propStr = `{${props.join(",")}}`;
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ exports[`t-on can bind event handler 1`] = `
|
||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['add'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['add'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
@@ -116,10 +115,8 @@ exports[`t-on can bind two event handlers 1`] = `
|
||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\" block-handler-1=\\"dblclick\\">Click</button>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['handleClick'];
|
||||
let d1 = [v1, ctx];
|
||||
const v2 = ctx['handleDblClick'];
|
||||
let d2 = [v2, ctx];
|
||||
let d1 = [ctx['handleClick'], ctx];
|
||||
let d2 = [ctx['handleDblClick'], ctx];
|
||||
return block1([d1, d2]);
|
||||
}
|
||||
}"
|
||||
@@ -134,8 +131,7 @@ exports[`t-on handler is bound to proper owner 1`] = `
|
||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['add'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['add'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
@@ -155,8 +151,7 @@ exports[`t-on handler is bound to proper owner, part 2 1`] = `
|
||||
for (let i1 = 0; i1 < l_block1; i1++) {
|
||||
ctx[\`value\`] = v_block1[i1];
|
||||
let key1 = ctx['value'];
|
||||
const v1 = ctx['add'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['add'], ctx];
|
||||
c_block1[i1] = withKey(block2([d1]), key1);
|
||||
}
|
||||
return list(c_block1);
|
||||
@@ -173,8 +168,7 @@ exports[`t-on handler is bound to proper owner, part 3 1`] = `
|
||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['add'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['add'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
@@ -202,8 +196,7 @@ exports[`t-on handler is bound to proper owner, part 4 1`] = `
|
||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['add'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['add'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
@@ -242,8 +235,7 @@ exports[`t-on receive event in first argument 1`] = `
|
||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['add'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['add'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
@@ -258,10 +250,8 @@ exports[`t-on t-on modifiers (native listener) basic support for native listener
|
||||
let block1 = createBlock(\`<div class=\\"myClass\\" block-handler-0=\\"click\\"><button block-handler-1=\\"click\\">Button</button></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['divClicked'];
|
||||
let d1 = [v1, ctx];
|
||||
const v2 = ctx['btnClicked'];
|
||||
let d2 = [v2, ctx];
|
||||
let d1 = [ctx['divClicked'], ctx];
|
||||
let d2 = [ctx['btnClicked'], ctx];
|
||||
return block1([d1, d2]);
|
||||
}
|
||||
}"
|
||||
@@ -276,8 +266,7 @@ exports[`t-on t-on modifiers (native listener) t-on combined with t-esc 1`] = `
|
||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><block-text-1/></button></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['onClick'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['onClick'], ctx];
|
||||
let d2 = ctx['text'];
|
||||
return block1([d1, d2]);
|
||||
}
|
||||
@@ -293,8 +282,7 @@ exports[`t-on t-on modifiers (native listener) t-on combined with t-out 1`] = `
|
||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><block-child-0/></button></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['onClick'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['onClick'], ctx];
|
||||
let b2 = safeOutput(ctx['html']);
|
||||
return block1([d1], [b2]);
|
||||
}
|
||||
@@ -310,10 +298,8 @@ exports[`t-on t-on modifiers (native listener) t-on with .capture modifier 1`] =
|
||||
let block1 = createBlock(\`<div block-handler-0=\\"click.capture\\"><button block-handler-1=\\"click\\">Button</button></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['onCapture'];
|
||||
let d1 = [\\"capture\\", v1, ctx];
|
||||
const v2 = ctx['doSomething'];
|
||||
let d2 = [v2, ctx];
|
||||
let d1 = [\\"capture\\", ctx['onCapture'], ctx];
|
||||
let d2 = [ctx['doSomething'], ctx];
|
||||
return block1([d1, d2]);
|
||||
}
|
||||
}"
|
||||
@@ -343,8 +329,7 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent and self modifi
|
||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click.prevent.self\\"><span>Button</span></button></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['onClick'];
|
||||
let d1 = [\\"prevent\\",\\"self\\", v1, ctx];
|
||||
let d1 = [\\"prevent\\",\\"self\\", ctx['onClick'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
@@ -359,12 +344,9 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent and/or stop mod
|
||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click.prevent\\">Button 1</button><button block-handler-1=\\"click.stop\\">Button 2</button><button block-handler-2=\\"click.prevent.stop\\">Button 3</button></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['onClickPrevented'];
|
||||
let d1 = [\\"prevent\\", v1, ctx];
|
||||
const v2 = ctx['onClickStopped'];
|
||||
let d2 = [\\"stop\\", v2, ctx];
|
||||
const v3 = ctx['onClickPreventedAndStopped'];
|
||||
let d3 = [\\"prevent\\",\\"stop\\", v3, ctx];
|
||||
let d1 = [\\"prevent\\", ctx['onClickPrevented'], ctx];
|
||||
let d2 = [\\"stop\\", ctx['onClickStopped'], ctx];
|
||||
let d3 = [\\"prevent\\",\\"stop\\", ctx['onClickPreventedAndStopped'], ctx];
|
||||
return block1([d1, d2, d3]);
|
||||
}
|
||||
}"
|
||||
@@ -406,8 +388,7 @@ exports[`t-on t-on modifiers (native listener) t-on with self and prevent modifi
|
||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click.self.prevent\\"><span>Button</span></button></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['onClick'];
|
||||
let d1 = [\\"self\\",\\"prevent\\", v1, ctx];
|
||||
let d1 = [\\"self\\",\\"prevent\\", ctx['onClick'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
@@ -422,10 +403,8 @@ exports[`t-on t-on modifiers (native listener) t-on with self modifier 1`] = `
|
||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><span>Button</span></button><button block-handler-1=\\"click.self\\"><span>Button</span></button></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['onClick'];
|
||||
let d1 = [v1, ctx];
|
||||
const v2 = ctx['onClickSelf'];
|
||||
let d2 = [\\"self\\", v2, ctx];
|
||||
let d1 = [ctx['onClick'], ctx];
|
||||
let d2 = [\\"self\\", ctx['onClickSelf'], ctx];
|
||||
return block1([d1, d2]);
|
||||
}
|
||||
}"
|
||||
@@ -440,10 +419,8 @@ exports[`t-on t-on modifiers (synthetic listener) basic support for synthetic 1`
|
||||
let block1 = createBlock(\`<div block-handler-0=\\"click.synthetic\\"><button block-handler-1=\\"click.synthetic\\">Button</button></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['divClicked'];
|
||||
let d1 = [\\"synthetic\\", v1, ctx];
|
||||
const v2 = ctx['btnClicked'];
|
||||
let d2 = [\\"synthetic\\", v2, ctx];
|
||||
let d1 = [\\"synthetic\\", ctx['divClicked'], ctx];
|
||||
let d2 = [\\"synthetic\\", ctx['btnClicked'], ctx];
|
||||
return block1([d1, d2]);
|
||||
}
|
||||
}"
|
||||
@@ -523,8 +500,7 @@ exports[`t-on t-on with t-call 1`] = `
|
||||
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['update'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['update'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
|
||||
@@ -182,7 +182,7 @@ exports[`misc other complex template 1`] = `
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||
const callTemplate_14 = getTemplate(\`LOAD_INFOS_TEMPLATE\`);
|
||||
const callTemplate_2 = getTemplate(\`LOAD_INFOS_TEMPLATE\`);
|
||||
|
||||
let block1 = createBlock(\`<div><header><nav class=\\"navbar navbar-expand-md navbar-light bg-light\\"><a block-attribute-0=\\"href\\"><b style=\\"color:#777;\\"><block-text-1/></b></a><button type=\\"button\\" class=\\"navbar-toggler\\" data-toggle=\\"collapse\\" data-target=\\"#top_menu_collapse\\"><span class=\\"navbar-toggler-icon\\"/></button><div class=\\"collapse navbar-collapse\\" id=\\"top_menu_collapse\\" aria-expanded=\\"false\\"><ul class=\\"nav navbar-nav ml-auto text-right\\" id=\\"top_menu\\"><block-child-0/><li class=\\"nav-item divider\\"/><block-child-1/></ul><div><div class=\\"input-group input-group-sm\\"><div class=\\"input-group-prepend input-group-sm\\"><button class=\\"btn btn-default fa fa-cog\\" title=\\"Settings\\" block-handler-2=\\"click\\"/><button class=\\"btn btn-default\\" block-handler-3=\\"click\\"> More </button><block-child-2/></div><input class=\\"form-control\\" type=\\"text\\" placeholder=\\"Search\\" aria-label=\\"Search\\" name=\\"search\\" block-attribute-4=\\"value\\" block-handler-5=\\"keyup\\" block-handler-6=\\"change\\" block-ref=\\"7\\"/><div class=\\"input-group-append\\"><button class=\\"btn btn-default fa fa-eraser\\" block-handler-8=\\"click\\"/></div></div></div></div></nav></header><div class=\\"container-fluid\\" block-ref=\\"9\\"><div class=\\"row\\"><!--div class=\\"form-group col-md-6\\">
|
||||
<h5>Search options</h5>
|
||||
@@ -223,9 +223,7 @@ exports[`misc other complex template 1`] = `
|
||||
for (let i1 = 0; i1 < l_block2; i1++) {
|
||||
ctx[\`project\`] = v_block2[i1];
|
||||
let key1 = ctx['project'].id;
|
||||
const v1 = ctx['selectProject'];
|
||||
const v2 = ctx['project'];
|
||||
let d3 = [v1(v2), ctx];
|
||||
let d3 = [ctx['selectProject'](ctx['project']), ctx];
|
||||
let d4 = ctx['project'].name;
|
||||
c_block2[i1] = withKey(block3([d3, d4]), key1);
|
||||
}
|
||||
@@ -257,10 +255,8 @@ exports[`misc other complex template 1`] = `
|
||||
}
|
||||
b4 = multi([b5, b6]);
|
||||
}
|
||||
const v3 = ctx['toggleSettingsMenu'];
|
||||
let d11 = [v3, ctx];
|
||||
const v4 = ctx['toggleMore'];
|
||||
let d12 = [v4, ctx];
|
||||
let d11 = [ctx['toggleSettingsMenu'], ctx];
|
||||
let d12 = [ctx['toggleMore'], ctx];
|
||||
if (ctx['categories']&&ctx['categories'].length>1) {
|
||||
ctx = Object.create(ctx);
|
||||
const [k_block15, v_block15, l_block15, c_block15] = prepareList(ctx['categories']);
|
||||
@@ -277,13 +273,10 @@ exports[`misc other complex template 1`] = `
|
||||
b14 = block14([], [b15]);
|
||||
}
|
||||
let d16 = ctx['search'].value;
|
||||
const v5 = ctx['updateFilter'];
|
||||
let d17 = [v5, ctx];
|
||||
const v6 = ctx['updateFilter'];
|
||||
let d18 = [v6, ctx];
|
||||
let d17 = [ctx['updateFilter'], ctx];
|
||||
let d18 = [ctx['updateFilter'], ctx];
|
||||
let d19 = (el) => refs[\`search_input\`] = el;
|
||||
const v7 = ctx['clearSearch'];
|
||||
let d20 = [v7, ctx];
|
||||
let d20 = [ctx['clearSearch'], ctx];
|
||||
let d21 = (el) => refs[\`settings_menu\`] = el;
|
||||
if (ctx['triggers']) {
|
||||
ctx = Object.create(ctx);
|
||||
@@ -297,8 +290,7 @@ exports[`misc other complex template 1`] = `
|
||||
let d23 = \`trigger_\${ctx['trigger'].id}\`;
|
||||
let d24 = ctx['options'].trigger_display[ctx['trigger'].id];
|
||||
let d25 = ctx['trigger'].id;
|
||||
const v8 = ctx['updateTriggerDisplay'];
|
||||
let d26 = [v8, ctx];
|
||||
let d26 = [ctx['updateTriggerDisplay'], ctx];
|
||||
let d27 = \`trigger_\${ctx['trigger'].id}\`;
|
||||
let d28 = ctx['trigger'].name;
|
||||
b20 = block20([d22, d23, d24, d25, d26, d27, d28]);
|
||||
@@ -307,19 +299,15 @@ exports[`misc other complex template 1`] = `
|
||||
}
|
||||
ctx = ctx.__proto__;
|
||||
let b18 = list(c_block18);
|
||||
const v9 = ctx['triggerAll'];
|
||||
let d29 = [v9, ctx];
|
||||
const v10 = ctx['triggerNone'];
|
||||
let d30 = [v10, ctx];
|
||||
const v11 = ctx['triggerDefault'];
|
||||
let d31 = [v11, ctx];
|
||||
const v12 = ctx['toggleSettingsMenu'];
|
||||
let d32 = [v12, ctx];
|
||||
let d29 = [ctx['triggerAll'], ctx];
|
||||
let d30 = [ctx['triggerNone'], ctx];
|
||||
let d31 = [ctx['triggerDefault'], ctx];
|
||||
let d32 = [ctx['toggleSettingsMenu'], ctx];
|
||||
let b21 = block21([d29, d30, d31, d32]);
|
||||
b17 = multi([b18, b21]);
|
||||
}
|
||||
if (ctx['load_infos']) {
|
||||
b22 = callTemplate_14.call(this, ctx, node, key + \`__13\`);
|
||||
b22 = callTemplate_2.call(this, ctx, node, key + \`__1\`);
|
||||
}
|
||||
if (ctx['message']) {
|
||||
let d33 = ctx['message'];
|
||||
@@ -328,8 +316,8 @@ exports[`misc other complex template 1`] = `
|
||||
if (!ctx['project']) {
|
||||
b24 = block24();
|
||||
} else {
|
||||
let b26 = component(\`BundlesList\`, {bundles: ctx['bundles'].sticky,category_custom_views: ctx['category_custom_views'],search: ctx['search']}, key + \`__15\`, node, ctx);
|
||||
let b27 = component(\`BundlesList\`, {bundles: ctx['bundles'].dev,search: ctx['search']}, key + \`__16\`, node, ctx);
|
||||
let b26 = component(\`BundlesList\`, {bundles: ctx['bundles'].sticky,category_custom_views: ctx['category_custom_views'],search: ctx['search']}, key + \`__3\`, node, ctx);
|
||||
let b27 = component(\`BundlesList\`, {bundles: ctx['bundles'].dev,search: ctx['search']}, key + \`__4\`, node, ctx);
|
||||
b25 = block25([], [b26, b27]);
|
||||
}
|
||||
return block1([d1, d2, d11, d12, d16, d17, d18, d19, d20, d21], [b2, b4, b14, b17, b22, b23, b24, b25]);
|
||||
|
||||
@@ -1130,10 +1130,9 @@ exports[`rendering component again in next microtick 2`] = `
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2;
|
||||
const v1 = ctx['onClick'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['onClick'], ctx];
|
||||
if (ctx['env'].config.flag) {
|
||||
b2 = component(\`Child\`, {}, key + \`__2\`, node, ctx);
|
||||
b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||
}
|
||||
return block1([d1], [b2]);
|
||||
}
|
||||
|
||||
@@ -23,15 +23,40 @@ exports[`event handling handler receive the event as argument 2`] = `
|
||||
let block1 = createBlock(\`<span block-handler-0=\\"click\\"><block-child-0/><block-text-1/></span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['inc'];
|
||||
let d1 = [v1, ctx];
|
||||
let b2 = component(\`Child\`, {}, key + \`__2\`, node, ctx);
|
||||
let d1 = [ctx['inc'], ctx];
|
||||
let b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||
let d2 = ctx['state'].value;
|
||||
return block1([d1, d2], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`event handling objects from scope are properly captured by t-on 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block3 = createBlock(\`<div class=\\"item\\" block-handler-0=\\"click\\"/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['items']);
|
||||
for (let i1 = 0; i1 < l_block2; i1++) {
|
||||
ctx[\`item\`] = v_block2[i1];
|
||||
let key1 = ctx['item'];
|
||||
const v1 = ctx['onClick'];
|
||||
const v2 = ctx['item'];
|
||||
let d1 = [ev=>v1(v2.val,ev), ctx];
|
||||
c_block2[i1] = withKey(block3([d1]), key1);
|
||||
}
|
||||
let b2 = list(c_block2);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`event handling support for callable expression in event handler 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
@@ -42,8 +67,7 @@ exports[`event handling support for callable expression in event handler 1`] = `
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let d1 = ctx['state'].value;
|
||||
const v1 = ctx['obj'];
|
||||
let d2 = [v1.onInput, ctx];
|
||||
let d2 = [ctx['obj'].onInput, ctx];
|
||||
return block1([d1, d2]);
|
||||
}
|
||||
}"
|
||||
|
||||
@@ -130,8 +130,7 @@ exports[`basics sub widget is interactive 1`] = `
|
||||
let block1 = createBlock(\`<span><button block-handler-0=\\"click\\">click</button>child<block-text-1/></span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['inc'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['inc'], ctx];
|
||||
let d2 = ctx['state'].val;
|
||||
return block1([d1, d2]);
|
||||
}
|
||||
|
||||
@@ -540,8 +540,7 @@ exports[`lifecycle hooks onWillRender 1`] = `
|
||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\"><block-text-1/></button>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['increment'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['increment'], ctx];
|
||||
let d2 = ctx['state'].value;
|
||||
return block1([d1, d2]);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,42 @@ exports[`basics accept ES6-like syntax for props (with getters) 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics arrow functions as prop correctly capture their scope 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\"/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let d1 = [ctx['props'].onClick, ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics arrow functions as prop correctly capture their scope 2`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
const [k_block1, v_block1, l_block1, c_block1] = prepareList(ctx['items']);
|
||||
for (let i1 = 0; i1 < l_block1; i1++) {
|
||||
ctx[\`item\`] = v_block1[i1];
|
||||
let key1 = ctx['item'].val;
|
||||
const v1 = ctx['onClick'];
|
||||
const v2 = ctx['item'];
|
||||
c_block1[i1] = withKey(component(\`Child\`, {onClick: ev=>v1(v2.val,ev)}, key + \`__3__\${key1}\`, node, ctx), key1);
|
||||
}
|
||||
return list(c_block1);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics explicit object prop 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
|
||||
@@ -43,8 +43,7 @@ exports[`refs refs are properly bound in slots 2`] = `
|
||||
|
||||
const slot3 = ctx => (node, key) => {
|
||||
const refs = ctx.__owl__.refs
|
||||
const v4 = ctx['doSomething'];
|
||||
let d2 = [v4, ctx];
|
||||
let d2 = [ctx['doSomething'], ctx];
|
||||
let d3 = (el) => refs[\`myButton\`] = el;
|
||||
return block2([d2, d3]);
|
||||
}
|
||||
|
||||
@@ -389,10 +389,9 @@ exports[`slots dynamic t-slot call 1`] = `
|
||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\"><block-child-0/></button>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['toggle'];
|
||||
let d1 = [v1, ctx];
|
||||
const slot2 = (ctx['current'].slot);
|
||||
let b2 = toggler(slot2, callSlot(ctx, node, key, slot2));
|
||||
let d1 = [ctx['toggle'], ctx];
|
||||
const slot1 = (ctx['current'].slot);
|
||||
let b2 = toggler(slot1, callSlot(ctx, node, key, slot1));
|
||||
return block1([d1], [b2]);
|
||||
}
|
||||
}"
|
||||
@@ -436,14 +435,13 @@ exports[`slots dynamic t-slot call with default 1`] = `
|
||||
|
||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\"><block-child-0/></button>\`);
|
||||
|
||||
const defaultSlot2 = ctx => {
|
||||
const defaultSlot1 = ctx => {
|
||||
return text(\` owl \`);
|
||||
}
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['toggle'];
|
||||
let d1 = [v1, ctx];
|
||||
let b3 = callSlot(ctx, node, key, (ctx['current'].slot), defaultSlot2, true);
|
||||
let d1 = [ctx['toggle'], ctx];
|
||||
let b3 = callSlot(ctx, node, key, (ctx['current'].slot), defaultSlot1, true);
|
||||
return block1([d1], [b3]);
|
||||
}
|
||||
}"
|
||||
@@ -1262,15 +1260,14 @@ exports[`slots slot are properly rendered if inner props are changed 3`] = `
|
||||
|
||||
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\">Inc[<block-text-1/>]</button><block-child-0/></div>\`);
|
||||
|
||||
const slot3 = ctx => (node, key) => {
|
||||
return component(\`SomeComponent\`, {val: ctx['state'].val}, key + \`__4\`, node, ctx);
|
||||
const slot2 = ctx => (node, key) => {
|
||||
return component(\`SomeComponent\`, {val: ctx['state'].val}, key + \`__3\`, node, ctx);
|
||||
}
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['inc'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['inc'], ctx];
|
||||
let d2 = ctx['state'].val;
|
||||
let b3 = assign(component(\`GenericComponent\`, {}, key + \`__2\`, node, ctx), {slots: {'default': slot3(ctx)}});
|
||||
let b3 = assign(component(\`GenericComponent\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
|
||||
return block1([d1, d2], [b3]);
|
||||
}
|
||||
}"
|
||||
@@ -1301,8 +1298,7 @@ exports[`slots slot content is bound to caller 2`] = `
|
||||
let block1 = createBlock(\`<button block-handler-0=\\"click\\">some text</button>\`);
|
||||
|
||||
const slot2 = ctx => (node, key) => {
|
||||
const v3 = ctx['inc'];
|
||||
let d1 = [v3, ctx];
|
||||
let d1 = [ctx['inc'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
|
||||
@@ -1472,8 +1468,7 @@ exports[`slots slots are rendered with proper context 2`] = `
|
||||
let block2 = createBlock(\`<button block-handler-0=\\"click\\">do something</button>\`);
|
||||
|
||||
const slot3 = ctx => (node, key) => {
|
||||
const v4 = ctx['doSomething'];
|
||||
let d2 = [v4, ctx];
|
||||
let d2 = [ctx['doSomething'], ctx];
|
||||
return block2([d2]);
|
||||
}
|
||||
|
||||
@@ -2083,8 +2078,7 @@ exports[`slots t-slot scope context 2`] = `
|
||||
let block1 = createBlock(\`<div block-handler-0=\\"click\\"><block-child-0/></div>\`);
|
||||
|
||||
const slot2 = ctx => (node, key) => {
|
||||
const v3 = ctx['onClick'];
|
||||
let d1 = [v3, ctx];
|
||||
let d1 = [ctx['onClick'], ctx];
|
||||
let b2 = callSlot(ctx, node, key, 'default');
|
||||
return block1([d1], [b2]);
|
||||
}
|
||||
|
||||
@@ -52,8 +52,7 @@ exports[`t-call handlers are properly bound through a t-call 1`] = `
|
||||
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['update'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['update'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
|
||||
@@ -117,11 +117,10 @@ exports[`t-model directive can also define t-on directive on same event, part 1
|
||||
let block1 = createBlock(\`<div><input block-handler-0=\\"input\\" block-attribute-1=\\"value\\" block-handler-2=\\"input\\"/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['onInput'];
|
||||
let d1 = [v1, ctx];
|
||||
const bExpr2 = ctx['state'];
|
||||
let d1 = [ctx['onInput'], ctx];
|
||||
const bExpr1 = ctx['state'];
|
||||
let d2 = ctx['state']['text'];
|
||||
let d3 = [(ev) => { bExpr2['text'] = ev.target.value; }];
|
||||
let d3 = [(ev) => { bExpr1['text'] = ev.target.value; }];
|
||||
return block1([d1, d2, d3]);
|
||||
}
|
||||
}"
|
||||
@@ -136,21 +135,18 @@ exports[`t-model directive can also define t-on directive on same event, part 2
|
||||
let block1 = createBlock(\`<div><input type=\\"radio\\" id=\\"one\\" value=\\"One\\" block-handler-0=\\"click\\" block-attribute-1=\\"checked\\" block-handler-2=\\"click\\"/><input type=\\"radio\\" id=\\"two\\" value=\\"Two\\" block-handler-3=\\"click\\" block-attribute-4=\\"checked\\" block-handler-5=\\"click\\"/><input type=\\"radio\\" id=\\"three\\" value=\\"Three\\" block-handler-6=\\"click\\" block-attribute-7=\\"checked\\" block-handler-8=\\"click\\"/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['onClick'];
|
||||
let d1 = [v1, ctx];
|
||||
const bExpr2 = ctx['state'];
|
||||
let d1 = [ctx['onClick'], ctx];
|
||||
const bExpr1 = ctx['state'];
|
||||
let d2 = ctx['state']['choice'] === 'One';
|
||||
let d3 = [(ev) => { bExpr2['choice'] = ev.target.value; }];
|
||||
const v3 = ctx['onClick'];
|
||||
let d4 = [v3, ctx];
|
||||
const bExpr4 = ctx['state'];
|
||||
let d3 = [(ev) => { bExpr1['choice'] = ev.target.value; }];
|
||||
let d4 = [ctx['onClick'], ctx];
|
||||
const bExpr2 = ctx['state'];
|
||||
let d5 = ctx['state']['choice'] === 'Two';
|
||||
let d6 = [(ev) => { bExpr4['choice'] = ev.target.value; }];
|
||||
const v5 = ctx['onClick'];
|
||||
let d7 = [v5, ctx];
|
||||
const bExpr6 = ctx['state'];
|
||||
let d6 = [(ev) => { bExpr2['choice'] = ev.target.value; }];
|
||||
let d7 = [ctx['onClick'], ctx];
|
||||
const bExpr3 = ctx['state'];
|
||||
let d8 = ctx['state']['choice'] === 'Three';
|
||||
let d9 = [(ev) => { bExpr6['choice'] = ev.target.value; }];
|
||||
let d9 = [(ev) => { bExpr3['choice'] = ev.target.value; }];
|
||||
return block1([d1, d2, d3, d4, d5, d6, d7, d8, d9]);
|
||||
}
|
||||
}"
|
||||
|
||||
@@ -130,8 +130,7 @@ exports[`t-on t-on on destroyed components 1`] = `
|
||||
let block1 = createBlock(\`<div block-handler-0=\\"click\\"/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['onClick'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['onClick'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
|
||||
@@ -72,4 +72,25 @@ describe("event handling", () => {
|
||||
expect(onClickArgs![0]).toBe(1);
|
||||
expect(onClickArgs![1]).toBeInstanceOf(MouseEvent);
|
||||
});
|
||||
|
||||
test("objects from scope are properly captured by t-on", async () => {
|
||||
let onClickArgs: [number, MouseEvent] | null = null;
|
||||
class Parent extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-foreach="items" t-as="item" t-key="item">
|
||||
<div class="item" t-on-click="ev => onClick(item.val, ev)"/>
|
||||
</t>
|
||||
</div>`;
|
||||
items = [{ val: 1 }, { val: 2 }, { val: 3 }, { val: 4 }];
|
||||
onClick(n: number, ev: MouseEvent) {
|
||||
onClickArgs = [n, ev];
|
||||
}
|
||||
}
|
||||
await mount(Parent, fixture);
|
||||
expect(onClickArgs).toBeNull();
|
||||
(<HTMLElement>fixture.querySelector(".item")).click();
|
||||
expect(onClickArgs![0]).toBe(1);
|
||||
expect(onClickArgs![1]).toBeInstanceOf(MouseEvent);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,4 +102,29 @@ describe("basics", () => {
|
||||
await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span><p>43</p><p>43</p></span></div>");
|
||||
});
|
||||
|
||||
test("arrow functions as prop correctly capture their scope", async () => {
|
||||
class Child extends Component {
|
||||
static template = xml`<button t-on-click="props.onClick"/>`;
|
||||
}
|
||||
|
||||
let onClickArgs: [number, MouseEvent] | null = null;
|
||||
class Parent extends Component {
|
||||
static template = xml`
|
||||
<t t-foreach="items" t-as="item" t-key="item.val">
|
||||
<Child onClick="ev => onClick(item.val, ev)"/>
|
||||
</t>
|
||||
`;
|
||||
static components = { Child };
|
||||
items = [{ val: 1 }, { val: 2 }, { val: 3 }, { val: 4 }];
|
||||
onClick(n: number, ev: MouseEvent) {
|
||||
onClickArgs = [n, ev];
|
||||
}
|
||||
}
|
||||
await mount(Parent, fixture);
|
||||
expect(onClickArgs).toBeNull();
|
||||
(<HTMLElement>fixture.querySelector("button")).click();
|
||||
expect(onClickArgs![0]).toBe(1);
|
||||
expect(onClickArgs![1]).toBeInstanceOf(MouseEvent);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,8 +9,7 @@ exports[`Portal Portal composed with t-slot 1`] = `
|
||||
let block1 = createBlock(\`<div block-handler-0=\\"custom\\"><span id=\\"childSpan\\">child2</span></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
const v1 = ctx['onCustom'];
|
||||
let d1 = [v1, ctx];
|
||||
let d1 = [ctx['onCustom'], ctx];
|
||||
return block1([d1]);
|
||||
}
|
||||
}"
|
||||
|
||||
Reference in New Issue
Block a user