From e5e77905305a5917c63107b8d8fe2af3458e4ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Wed, 16 Sep 2020 22:00:31 +0200 Subject: [PATCH] [FIX] qweb: handle input value attribute as a property Sometimes, HTML is slightly more subtle than what I initially expect. Rendering some html is simple, we have tags and attributes. However, once we add behaviour, then the situation is more complex: is an input with an INITIAL value of "abc", but the attribute does not actually represent the CURRENT value of the input, which may be different if the user did change it. This is basically the difference between "attribute" and "property". So, when rendering html with owl, we sometimes want to actually set the property (current value), instead of the html attribute. This commit make sure that this is the case for inputs with the "value" attribute. closes #722 --- src/qweb/qweb.ts | 9 ++++++--- .../__snapshots__/component.test.ts.snap | 4 ++-- tests/qweb/qweb.test.ts | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/qweb/qweb.ts b/src/qweb/qweb.ts index cc5e54f7..3a9565ff 100644 --- a/src/qweb/qweb.ts +++ b/src/qweb/qweb.ts @@ -668,7 +668,7 @@ export class QWeb extends EventBus { const props: string[] = []; const tattrs: number[] = []; - function handleBooleanProps(key, val) { + function handleProperties(key, val) { let isProp = false; if (node.nodeName === "input" && key === "checked") { let type = (node).getAttribute("type"); @@ -676,6 +676,9 @@ export class QWeb extends EventBus { isProp = true; } } + if (node.nodeName === "input" && key === "value") { + isProp = true; + } if (node.nodeName === "option" && key === "selected") { isProp = true; } @@ -722,7 +725,7 @@ export class QWeb extends EventBus { name = '"' + name + '"'; } attrs.push(`${name}: _${attID}`); - handleBooleanProps(name, attID); + handleProperties(name, attID); } } @@ -759,7 +762,7 @@ export class QWeb extends EventBus { } ctx.addLine(`let _${attID} = ${formattedValue};`); attrs.push(`${attName}: _${attID}`); - handleBooleanProps(attName, attID); + handleProperties(attName, attID); } } diff --git a/tests/component/__snapshots__/component.test.ts.snap b/tests/component/__snapshots__/component.test.ts.snap index e8c1689a..28108bf8 100644 --- a/tests/component/__snapshots__/component.test.ts.snap +++ b/tests/component/__snapshots__/component.test.ts.snap @@ -1841,7 +1841,7 @@ exports[`t-model directive on an input type=radio 1`] = ` let _2 = 'radio'; let _3 = 'one'; let _4 = 'One'; - let c5 = [], p5 = {key:5,attrs:{type: _2,id: _3,value: _4},on:{}}; + let c5 = [], p5 = {key:5,attrs:{type: _2,id: _3,value: _4},props:{value: _4},on:{}}; let vn5 = h('input', p5, c5); c1.push(vn5); let expr5 = scope['state']; @@ -1851,7 +1851,7 @@ exports[`t-model directive on an input type=radio 1`] = ` let _7 = 'radio'; let _8 = 'two'; let _9 = 'Two'; - let c10 = [], p10 = {key:10,attrs:{type: _7,id: _8,value: _9},on:{}}; + let c10 = [], p10 = {key:10,attrs:{type: _7,id: _8,value: _9},props:{value: _9},on:{}}; let vn10 = h('input', p10, c10); c1.push(vn10); let expr10 = scope['state']; diff --git a/tests/qweb/qweb.test.ts b/tests/qweb/qweb.test.ts index 356deaf3..cb468785 100644 --- a/tests/qweb/qweb.test.ts +++ b/tests/qweb/qweb.test.ts @@ -1876,6 +1876,25 @@ describe("special cases for some boolean html attributes/properties", () => { ); renderToString(qweb, "test", { flag: true }); }); + + test("input with t-att-value", () => { + // render input with initial value + qweb.addTemplate("test", ``); + const vnode1 = qweb.render("test", { v: "zucchini" }); + const vnode2 = patch(document.createElement("input"), vnode1); + let elm = vnode2.elm as HTMLInputElement; + expect(elm.value).toBe("zucchini"); + + // change value manually in input, to simulate user input + elm.value = "tomato"; + expect(elm.value).toBe("tomato"); + + // rerender with a different value, and patch actual dom, to check that + // input value was properly reset by owl + const vnode3 = qweb.render("test", { v: "potato" }); + patch(vnode2, vnode3); + expect(elm.value).toBe("potato"); + }); }); describe("whitespace handling", () => {