[FIX] compiler: properly handle readonly attribute/readOnly property

Before this commit, Owl template compiler would handle readonly
attribute as readonly properties. But this is incorrect, since the
property is actually named readOnly.

With this commit, we make sure that readonly AND readOnly are both
interpreted as the `readOnly` property. This is due to the fact that
QWeb does not discriminate between attribute and properties, so we have
to infer which is which.

closes #1362
This commit is contained in:
Géry Debongnie
2023-03-01 12:21:40 +01:00
committed by Sam Degueldre
parent f892929c80
commit cdad48d3a6
3 changed files with 85 additions and 1 deletions
+6 -1
View File
@@ -65,12 +65,13 @@ function isProp(tag: string, key: string): boolean {
key === "indeterminate" ||
key === "value" ||
key === "readonly" ||
key === "readOnly" ||
key === "disabled"
);
case "option":
return key === "selected" || key === "disabled";
case "textarea":
return key === "value" || key === "readonly" || key === "disabled";
return key === "value" || key === "readonly" || key === "readOnly" || key === "disabled";
case "select":
return key === "value" || key === "disabled";
case "button":
@@ -600,6 +601,10 @@ export class CodeGenerator {
attrName = key === "t-att" ? null : key.slice(6);
expr = compileExpr(ast.attrs[key]);
if (attrName && isProp(ast.tag, attrName)) {
if (attrName === "readonly") {
// the property has a different name than the attribute
attrName = "readOnly";
}
// we force a new string or new boolean to bypass the equality check in blockdom when patching same value
if (attrName === "value") {
// When the expression is falsy (except 0), fall back to an empty string
@@ -140,6 +140,60 @@ exports[`input, type checkbox, with t-att-checked (patching with same value 1`]
}"
`;
exports[`readonly and readOnly are both interpreted as the readOnly property 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<input readonly=\\"\\"/>\`);
return function template(ctx, node, key = \\"\\") {
return block1();
}
}"
`;
exports[`readonly and readOnly are both interpreted as the readOnly property 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<input readOnly=\\"\\"/>\`);
return function template(ctx, node, key = \\"\\") {
return block1();
}
}"
`;
exports[`readonly and readOnly are both interpreted as the readOnly property 3`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<input block-property-0=\\"readOnly\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let prop1 = new Boolean(ctx['val']);
return block1([prop1]);
}
}"
`;
exports[`readonly and readOnly are both interpreted as the readOnly property 4`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<input block-property-0=\\"readOnly\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let prop1 = new Boolean(ctx['val']);
return block1([prop1]);
}
}"
`;
exports[`select with t-att-value 1`] = `
"function anonymous(app, bdom, helpers
) {
+25
View File
@@ -186,3 +186,28 @@ test("select with t-att-value", () => {
patch(bnode1, bnode2);
expect(elm.value).toBe("onion");
});
test("readonly and readOnly are both interpreted as the readOnly property", () => {
// render input with initial value
const staticTemplates = [`<input readonly=""/>`, `<input readOnly=""/>`];
for (let template of staticTemplates) {
const fixture = makeTestFixture();
const bnode1 = renderToBdom(template);
mount(bnode1, fixture);
const input = fixture.querySelector("input")!;
expect(input.readOnly).toBe(true);
}
const dynamicTemplates = [`<input t-att-readonly="val"/>`, `<input t-att-readOnly="val"/>`];
for (let template of dynamicTemplates) {
const fixture = makeTestFixture();
const bnode1 = renderToBdom(template, { val: true });
mount(bnode1, fixture);
const input = fixture.querySelector("input")!;
expect(input.readOnly).toBe(true);
const bnode2 = renderToBdom(template, { val: false });
patch(bnode1, bnode2);
expect(input.readOnly).toBe(false);
}
});