mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] qweb: add support for t-att-value on <select> tags
Before this commit, it was not possible to set the value of a select tag by using the t-att-value attribute. Doing so is not actually trivial because of the way the vdom works: it processes the node attributes before its children are created, which means that the vdom code tries to set the initial value of the select before its children are created, which means that it is ignored. To make it work, I added a node create hook which is called after the children are completely processed. closes #873
This commit is contained in:
+23
-8
@@ -617,13 +617,21 @@ export class QWeb extends EventBus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node.nodeName !== "t" || node.hasAttribute("t-tag")) {
|
if (node.nodeName !== "t" || node.hasAttribute("t-tag")) {
|
||||||
let nodeID = this._compileGenericNode(node, ctx, withHandlers);
|
|
||||||
ctx = ctx.withParent(nodeID);
|
|
||||||
let nodeHooks = {};
|
let nodeHooks = {};
|
||||||
let addNodeHook = function (hook, handler) {
|
let addNodeHook = function (hook, handler) {
|
||||||
nodeHooks[hook] = nodeHooks[hook] || [];
|
nodeHooks[hook] = nodeHooks[hook] || [];
|
||||||
nodeHooks[hook].push(handler);
|
nodeHooks[hook].push(handler);
|
||||||
};
|
};
|
||||||
|
if (node.tagName === "select" && node.hasAttribute("t-att-value")) {
|
||||||
|
const value = node.getAttribute("t-att-value");
|
||||||
|
let exprId = ctx.generateID();
|
||||||
|
ctx.addLine(`let expr${exprId} = ${ctx.formatExpression(value)};`);
|
||||||
|
let expr = `expr${exprId}`;
|
||||||
|
node.setAttribute("t-att-value", expr);
|
||||||
|
addNodeHook("create", `n.elm.value=${expr};`);
|
||||||
|
}
|
||||||
|
let nodeID = this._compileGenericNode(node, ctx, withHandlers);
|
||||||
|
ctx = ctx.withParent(nodeID);
|
||||||
|
|
||||||
for (let { directive, value, fullName } of validDirectives) {
|
for (let { directive, value, fullName } of validDirectives) {
|
||||||
if (directive.atNodeCreation) {
|
if (directive.atNodeCreation) {
|
||||||
@@ -707,14 +715,16 @@ export class QWeb extends EventBus {
|
|||||||
case "textarea":
|
case "textarea":
|
||||||
isProp = key === "readonly" || key === "disabled" || key === "value";
|
isProp = key === "readonly" || key === "disabled" || key === "value";
|
||||||
break;
|
break;
|
||||||
case "button":
|
|
||||||
case "select":
|
case "select":
|
||||||
|
isProp = key === "disabled" || key === "value";
|
||||||
|
break;
|
||||||
|
case "button":
|
||||||
case "optgroup":
|
case "optgroup":
|
||||||
isProp = key === "disabled";
|
isProp = key === "disabled";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (isProp) {
|
if (isProp) {
|
||||||
props.push(`${key}: _${val}`);
|
props.push(`${key}: ${val}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let classObj = "";
|
let classObj = "";
|
||||||
@@ -750,7 +760,7 @@ export class QWeb extends EventBus {
|
|||||||
name = '"' + name + '"';
|
name = '"' + name + '"';
|
||||||
}
|
}
|
||||||
attrs.push(`${name}: _${attID}`);
|
attrs.push(`${name}: _${attID}`);
|
||||||
handleProperties(name, attID);
|
handleProperties(name, "_" + attID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -785,9 +795,14 @@ export class QWeb extends EventBus {
|
|||||||
const attrIndex = attrs.findIndex((att) => att.startsWith(attName + ":"));
|
const attrIndex = attrs.findIndex((att) => att.startsWith(attName + ":"));
|
||||||
attrs.splice(attrIndex, 1);
|
attrs.splice(attrIndex, 1);
|
||||||
}
|
}
|
||||||
ctx.addLine(`let _${attID} = ${formattedValue};`);
|
if (node.nodeName === "select" && attName === "value") {
|
||||||
attrs.push(`${attName}: _${attID}`);
|
attrs.push(`${attName}: ${v}`);
|
||||||
handleProperties(attName, attID);
|
handleProperties(attName, v);
|
||||||
|
} else {
|
||||||
|
ctx.addLine(`let _${attID} = ${formattedValue};`);
|
||||||
|
attrs.push(`${attName}: _${attID}`);
|
||||||
|
handleProperties(attName, "_" + attID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1073,6 +1073,39 @@ exports[`special cases for some specific html attributes/properties input type=
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`special cases for some specific html attributes/properties select with t-att-value 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"test\\"
|
||||||
|
let scope = Object.create(context);
|
||||||
|
let h = this.h;
|
||||||
|
let expr1 = scope['value'];
|
||||||
|
let c3 = [], p3 = {key:3,attrs:{value: expr1},props:{value: expr1}};
|
||||||
|
let vn3 = h('select', p3, c3);
|
||||||
|
p3.hook = {
|
||||||
|
create: (_, n) => {
|
||||||
|
n.elm.value=expr1;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let _4 = 'potato';
|
||||||
|
let c5 = [], p5 = {key:5,attrs:{value: _4}};
|
||||||
|
let vn5 = h('option', p5, c5);
|
||||||
|
c3.push(vn5);
|
||||||
|
c5.push({text: \`Potato\`});
|
||||||
|
let _6 = 'tomato';
|
||||||
|
let c7 = [], p7 = {key:7,attrs:{value: _6}};
|
||||||
|
let vn7 = h('option', p7, c7);
|
||||||
|
c3.push(vn7);
|
||||||
|
c7.push({text: \`Tomato\`});
|
||||||
|
let _8 = 'onion';
|
||||||
|
let c9 = [], p9 = {key:9,attrs:{value: _8}};
|
||||||
|
let vn9 = h('option', p9, c9);
|
||||||
|
c3.push(vn9);
|
||||||
|
c9.push({text: \`Onion\`});
|
||||||
|
return vn3;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`special cases for some specific html attributes/properties various boolean html attributes 1`] = `
|
exports[`special cases for some specific html attributes/properties various boolean html attributes 1`] = `
|
||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1950,6 +1950,30 @@ describe("special cases for some specific html attributes/properties", () => {
|
|||||||
patch(vnode2, vnode3);
|
patch(vnode2, vnode3);
|
||||||
expect(elm.value).toBe("potato");
|
expect(elm.value).toBe("potato");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("select with t-att-value", () => {
|
||||||
|
const template = `
|
||||||
|
<select t-att-value="value">
|
||||||
|
<option value="potato">Potato</option>
|
||||||
|
<option value="tomato">Tomato</option>
|
||||||
|
<option value="onion">Onion</option>
|
||||||
|
</select>`;
|
||||||
|
qweb.addTemplate("test", template);
|
||||||
|
const vnode1 = qweb.render("test", { value: "tomato" });
|
||||||
|
const vnode2 = patch(document.createElement("select"), vnode1);
|
||||||
|
let elm = vnode2.elm as HTMLSelectElement;
|
||||||
|
expect(elm.value).toBe("tomato");
|
||||||
|
|
||||||
|
elm.value = "potato";
|
||||||
|
expect(elm.value).toBe("potato");
|
||||||
|
|
||||||
|
// rerender with a different value, and patch actual dom, to check that
|
||||||
|
// select value was properly reset by owl
|
||||||
|
const vnode3 = qweb.render("test", { value: "onion" });
|
||||||
|
patch(vnode2, vnode3);
|
||||||
|
expect(elm.value).toBe("onion");
|
||||||
|
expect(qweb.templates.test.fn.toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("whitespace handling", () => {
|
describe("whitespace handling", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user