mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] compiler: allow t-model.number to work with select
Before this commit, owl parser would ignore the `.number` suffix on <select> options. I do not see a good reason for that, and it prevents some legitimate usecases. closes #1444
This commit is contained in:
committed by
Sam Degueldre
parent
44748270da
commit
e4c296a7d2
@@ -365,10 +365,8 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
const typeAttr = node.getAttribute("type");
|
const typeAttr = node.getAttribute("type");
|
||||||
const isInput = tagName === "input";
|
const isInput = tagName === "input";
|
||||||
const isSelect = tagName === "select";
|
const isSelect = tagName === "select";
|
||||||
const isTextarea = tagName === "textarea";
|
|
||||||
const isCheckboxInput = isInput && typeAttr === "checkbox";
|
const isCheckboxInput = isInput && typeAttr === "checkbox";
|
||||||
const isRadioInput = isInput && typeAttr === "radio";
|
const isRadioInput = isInput && typeAttr === "radio";
|
||||||
const isOtherInput = isInput && !isCheckboxInput && !isRadioInput;
|
|
||||||
const hasLazyMod = attr.includes(".lazy");
|
const hasLazyMod = attr.includes(".lazy");
|
||||||
const hasNumberMod = attr.includes(".number");
|
const hasNumberMod = attr.includes(".number");
|
||||||
const hasTrimMod = attr.includes(".trim");
|
const hasTrimMod = attr.includes(".trim");
|
||||||
@@ -381,8 +379,8 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
specialInitTargetAttr: isRadioInput ? "checked" : null,
|
specialInitTargetAttr: isRadioInput ? "checked" : null,
|
||||||
eventType,
|
eventType,
|
||||||
hasDynamicChildren: false,
|
hasDynamicChildren: false,
|
||||||
shouldTrim: hasTrimMod && (isOtherInput || isTextarea),
|
shouldTrim: hasTrimMod,
|
||||||
shouldNumberize: hasNumberMod && (isOtherInput || isTextarea),
|
shouldNumberize: hasNumberMod,
|
||||||
};
|
};
|
||||||
if (isSelect) {
|
if (isSelect) {
|
||||||
// don't pollute the original ctx
|
// don't pollute the original ctx
|
||||||
|
|||||||
@@ -1991,8 +1991,8 @@ describe("qweb parser", () => {
|
|||||||
baseExpr: "state",
|
baseExpr: "state",
|
||||||
expr: "'stuff'",
|
expr: "'stuff'",
|
||||||
eventType: "click",
|
eventType: "click",
|
||||||
shouldNumberize: false,
|
shouldNumberize: true,
|
||||||
shouldTrim: false,
|
shouldTrim: true,
|
||||||
targetAttr: "value",
|
targetAttr: "value",
|
||||||
hasDynamicChildren: false,
|
hasDynamicChildren: false,
|
||||||
specialInitTargetAttr: "checked",
|
specialInitTargetAttr: "checked",
|
||||||
|
|||||||
@@ -468,6 +468,36 @@ exports[`t-model directive t-model on select with static options 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-model directive t-model with dynamic number values on select options in foreach 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { toNumber, prepareList, withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<select block-handler-0=\\"change\\"><block-child-0/></select>\`);
|
||||||
|
let block3 = createBlock(\`<option block-attribute-0=\\"value\\" block-attribute-1=\\"selected\\"><block-text-2/></option>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const bExpr1 = ctx['state'];
|
||||||
|
const expr1 = 'value';
|
||||||
|
const bValue1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = toNumber(ev.target.value); }];
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['state'].options);;
|
||||||
|
for (let i1 = 0; i1 < l_block2; i1++) {
|
||||||
|
ctx[\`o\`] = v_block2[i1];
|
||||||
|
const key1 = ctx['o'].value;
|
||||||
|
let attr1 = ctx['o'].value;
|
||||||
|
let attr2 = bValue1 === ctx['o'].value;
|
||||||
|
let txt1 = ctx['o'].value;
|
||||||
|
c_block2[i1] = withKey(block3([attr1, attr2, txt1]), key1);
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([hdlr1], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`t-model directive t-model with dynamic values on select options -- 2 1`] = `
|
exports[`t-model directive t-model with dynamic values on select options -- 2 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -626,6 +626,39 @@ describe("t-model directive", () => {
|
|||||||
expect(fixture.querySelector("select")!.value).toEqual("b");
|
expect(fixture.querySelector("select")!.value).toEqual("b");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-model with dynamic number values on select options in foreach", async () => {
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<select t-model.number="state.value">
|
||||||
|
<t t-foreach="state.options" t-as="o" t-key="o.value">
|
||||||
|
<option t-att-value="o.value" t-esc="o.value"/>
|
||||||
|
</t>
|
||||||
|
</select>
|
||||||
|
`;
|
||||||
|
state: any;
|
||||||
|
setup() {
|
||||||
|
this.state = useState({
|
||||||
|
value: 2,
|
||||||
|
options: [{ value: 1 }, { value: 2 }, { value: 3 }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const comp = await mount(Test, fixture);
|
||||||
|
// check that we have a value of 2 selected
|
||||||
|
expect(fixture.querySelector("select")!.value).toEqual("2");
|
||||||
|
expect(comp.state.value).toBe(2);
|
||||||
|
|
||||||
|
// emulate a click on the option=3 element
|
||||||
|
fixture.querySelectorAll("option")[2].selected = true;
|
||||||
|
fixture.querySelector("select")!.dispatchEvent(new Event("change"));
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
// check that we have now selected the number 3 (and not the string)
|
||||||
|
expect(fixture.querySelector("select")!.value).toEqual("3");
|
||||||
|
expect(comp.state.value).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
test("t-model is applied before t-on-input", async () => {
|
test("t-model is applied before t-on-input", async () => {
|
||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
class SomeComponent extends Component {
|
class SomeComponent extends Component {
|
||||||
|
|||||||
Reference in New Issue
Block a user