[IMP] t-on directive: allow empty handler

Closes #377
This commit is contained in:
Aaron Bohy
2019-10-25 10:58:18 +02:00
committed by Géry Debongnie
parent 0ea1091692
commit 2beb12678e
7 changed files with 131 additions and 14 deletions
+9
View File
@@ -719,6 +719,15 @@ the order may matter. For instance `t-on-click.prevent.self` will prevent all
clicks while `t-on-click.self.prevent` will only prevent clicks on the element
itself.
Finally, empty handlers are tolerated as they could be defined only to apply
modifiers. For example,
```xml
<button t-on-click.stop="">Do something</button>
```
This will simply stop the propagation of the event.
### Form Input Bindings
It is very common to need to be able to read the value out of an html `input` (or
+4 -2
View File
@@ -335,8 +335,10 @@ QWeb.addDirective({
return T_COMPONENT_MODS_CODE[mod];
})
.join("");
handler += `const fn = owner['${handlerValue}'];`;
handler += `if (fn) { fn.call(${params}, e); } else { owner.${handlerValue}; }`;
if (handlerValue) {
handler += `const fn = owner['${handlerValue}'];`;
handler += `if (fn) { fn.call(${params}, e); } else { owner.${handlerValue}; }`;
}
handler += `}`;
return `vn.elm.addEventListener('${eventName}', ${handler});`;
})
+17 -12
View File
@@ -47,19 +47,24 @@ QWeb.addDirective({
return MODS_CODE[mod];
})
.join("");
if (!extraArgs) {
handler += `const fn = context['${handlerName}'];`;
handler += `if (fn) { fn.call(${params}, e); } else { context.${handlerName}; }`;
handler += `}`;
ctx.addLine(
`extra.handlers['${eventName}' + ${nodeID}] = extra.handlers['${eventName}' + ${nodeID}] || ${handler};`
);
ctx.addLine(`p${nodeID}.on['${eventName}'] = extra.handlers['${eventName}' + ${nodeID}];`);
if (handlerName) {
if (!extraArgs) {
handler += `const fn = context['${handlerName}'];`;
handler += `if (fn) { fn.call(${params}, e); } else { context.${handlerName}; }`;
handler += `}`;
ctx.addLine(
`extra.handlers['${eventName}' + ${nodeID}] = extra.handlers['${eventName}' + ${nodeID}] || ${handler};`
);
ctx.addLine(`p${nodeID}.on['${eventName}'] = extra.handlers['${eventName}' + ${nodeID}];`);
} else {
const handlerKey = `handler${ctx.generateID()}`;
ctx.addLine(`const ${handlerKey} = context['${handlerName}'] && context['${handlerName}'].bind(${params});`);
handler += `if (${handlerKey}) { ${handlerKey}(e); } else { context.${value}; }`;
handler += `}`;
ctx.addLine(`p${nodeID}.on['${eventName}'] = ${handler};`);
}
} else {
const handlerKey = `handler${ctx.generateID()}`;
ctx.addLine(`const ${handlerKey} = context['${handlerName}'] && context['${handlerName}'].bind(${params});`);
handler += `if (${handlerKey}) { ${handlerKey}(e); } else { context.${value}; }`;
handler += `}`;
handler += "}";
ctx.addLine(`p${nodeID}.on['${eventName}'] = ${handler};`);
}
}
@@ -1058,6 +1058,48 @@ exports[`other directives with t-component t-on with inline statement 1`] = `
}"
`;
exports[`other directives with t-component t-on with no handler (only modifiers) 1`] = `
"function anonymous(context,extra
) {
let utils = this.constructor.utils;
let QWeb = this.constructor;
let parent = context;
let owner = context;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
//COMPONENT
let def3;
let templateId4 = \`__5__\`;
let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false;
let _2_index = c1.length;
c1.push(null);
let props4 = {};
if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) {
if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) {
def3 = w4.__owl__.currentFiber.promise;
} else {
w4.destroy();
w4 = false;
}
}
if (!w4) {
let componentKey4 = \`ComponentA\`;
let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['ComponentA'];
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
w4 = new W4(parent, props4);
parent.__owl__.cmap[templateId4] = w4.__owl__.id;
def3 = w4.__prepare(extra.fiber, undefined, undefined);
def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};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 {
def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined);
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;});
}
extra.promises.push(def3);
return vn1;
}"
`;
exports[`other directives with t-component t-on with prevent and self modifiers (order matters) 1`] = `
"function anonymous(context,extra
) {
+25
View File
@@ -2064,6 +2064,31 @@ describe("other directives with t-component", () => {
expect(fixture.innerHTML).toBe('<div>1<span></span></div>');
});
test("t-on with no handler (only modifiers)", async () => {
expect.assertions(2);
class ComponentB extends Component<any, any> {
static template = xml`<span></span>`;
}
class ComponentA extends Component<any, any> {
static template = xml`<p><ComponentB t-on-ev.prevent=""/></p>`;
static components = { ComponentB };
}
class Parent extends Component<any, any> {
static template = xml`<div><ComponentA t-on-ev="onEv"/></div>`;
static components = { ComponentA };
onEv(ev) {
expect(ev.defaultPrevented).toBe(true);
}
}
const parent = new Parent(env);
await parent.mount(fixture);
let componentB = children(children(parent)[0])[0];
componentB.trigger("ev");
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
});
test("t-if works with t-component", async () => {
env.qweb.addTemplate("ParentWidget", `<div><t t-component="child" t-if="state.flag"/></div>`);
class Child extends Widget {}
@@ -1789,6 +1789,22 @@ exports[`t-on t-on combined with t-raw 1`] = `
}"
`;
exports[`t-on t-on with empty handler (only modifiers) 1`] = `
"function anonymous(context,extra
) {
let owner = context;
var h = this.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
let c2 = [], p2 = {key:2,on:{}};
var vn2 = h('button', p2, c2);
c1.push(vn2);
p2.on['click'] = function (e) {e.preventDefault();};
c2.push({text: \`Button\`});
return vn1;
}"
`;
exports[`t-on t-on with inline statement (function call) 1`] = `
"function anonymous(context,extra
) {
+18
View File
@@ -1213,6 +1213,24 @@ describe("t-on", () => {
expect(steps).toEqual([1, 2]);
});
test("t-on with empty handler (only modifiers)", () => {
expect.assertions(2);
qweb.addTemplate(
"test",
`<div>
<button t-on-click.prevent="">Button</button>
</div>`
);
const node = renderToDOM(qweb, "test", {}, { handlers: [] });
node.addEventListener('click', (e) => {
expect(e.defaultPrevented).toBe(true);
});
const button = (<HTMLElement>node).getElementsByTagName("button")[0];
button.click();
});
test("t-on combined with t-esc", async () => {
expect.assertions(3);
qweb.addTemplate("test", `<div><button t-on-click="onClick" t-esc="text"/></div>`);