From 28f7b44f9edb4a64b52f265017b0542ba42fef5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Thu, 5 Sep 2019 08:45:55 +0200 Subject: [PATCH] [IMP] component: make t-model works with sub state keys closes #273 --- src/qweb/extensions.ts | 19 +++---- .../__snapshots__/component.test.ts.snap | 49 ++++++++++++++----- tests/component/component.test.ts | 24 +++++++++ 3 files changed, 70 insertions(+), 22 deletions(-) diff --git a/src/qweb/extensions.ts b/src/qweb/extensions.ts index adeaf18d..99ece994 100644 --- a/src/qweb/extensions.ts +++ b/src/qweb/extensions.ts @@ -252,28 +252,29 @@ QWeb.addDirective({ const type = node.getAttribute("type"); let handler; let event = fullName.includes(".lazy") ? "change" : "input"; + const expr = ctx.formatExpression(`state.${value}`); if (node.tagName === "select") { - ctx.addLine(`p${nodeID}.props = {value: context.state['${value}']};`); - addNodeHook("create", `n.elm.value=context.state['${value}'];`); + ctx.addLine(`p${nodeID}.props = {value: ${expr}};`); + addNodeHook("create", `n.elm.value=${expr};`); event = "change"; - handler = `(ev) => {context.state['${value}'] = ev.target.value}`; + handler = `(ev) => {${expr} = ev.target.value}`; } else if (type === "checkbox") { - ctx.addLine(`p${nodeID}.props = {checked: context.state['${value}']};`); - handler = `(ev) => {context.state['${value}'] = ev.target.checked}`; + ctx.addLine(`p${nodeID}.props = {checked: ${expr}};`); + handler = `(ev) => {${expr} = ev.target.checked}`; } else if (type === "radio") { const nodeValue = node.getAttribute("value")!; - ctx.addLine(`p${nodeID}.props = {checked:context.state['${value}'] === '${nodeValue}'};`); - handler = `(ev) => {context.state['${value}'] = ev.target.value}`; + ctx.addLine(`p${nodeID}.props = {checked:${expr} === '${nodeValue}'};`); + handler = `(ev) => {${expr} = ev.target.value}`; event = "click"; } else { - ctx.addLine(`p${nodeID}.props = {value: context.state['${value}']};`); + ctx.addLine(`p${nodeID}.props = {value: ${expr}};`); const trimCode = fullName.includes(".trim") ? ".trim()" : ""; let valueCode = `ev.target.value${trimCode}`; if (fullName.includes(".number")) { ctx.rootContext.shouldDefineUtils = true; valueCode = `utils.toNumber(${valueCode})`; } - handler = `(ev) => {context.state['${value}'] = ${valueCode}}`; + handler = `(ev) => {${expr} = ${valueCode}}`; } ctx.addLine( `extra.handlers['${event}' + ${nodeID}] = extra.handlers['${event}' + ${nodeID}] || (${handler});` diff --git a/tests/component/__snapshots__/component.test.ts.snap b/tests/component/__snapshots__/component.test.ts.snap index 75cfc541..8a946d8d 100644 --- a/tests/component/__snapshots__/component.test.ts.snap +++ b/tests/component/__snapshots__/component.test.ts.snap @@ -1114,8 +1114,8 @@ exports[`t-model directive .lazy modifier 1`] = ` let c2 = [], p2 = {key:2,on:{}}; var vn2 = h('input', p2, c2); c1.push(vn2); - p2.props = {value: context.state['text']}; - extra.handlers['change' + 2] = extra.handlers['change' + 2] || ((ev) => {context.state['text'] = ev.target.value}); + p2.props = {value: context['state'].text}; + extra.handlers['change' + 2] = extra.handlers['change' + 2] || ((ev) => {context['state'].text = ev.target.value}); p2.on['change'] = extra.handlers['change' + 2]; let c3 = [], p3 = {key:3}; var vn3 = h('span', p3, c3); @@ -1137,8 +1137,8 @@ exports[`t-model directive basic use, on an input 1`] = ` let c2 = [], p2 = {key:2,on:{}}; var vn2 = h('input', p2, c2); c1.push(vn2); - p2.props = {value: context.state['text']}; - extra.handlers['input' + 2] = extra.handlers['input' + 2] || ((ev) => {context.state['text'] = ev.target.value}); + p2.props = {value: context['state'].text}; + extra.handlers['input' + 2] = extra.handlers['input' + 2] || ((ev) => {context['state'].text = ev.target.value}); p2.on['input'] = extra.handlers['input' + 2]; let c3 = [], p3 = {key:3}; var vn3 = h('span', p3, c3); @@ -1160,12 +1160,12 @@ exports[`t-model directive on a select 1`] = ` let c2 = [], p2 = {key:2,on:{}}; var vn2 = h('select', p2, c2); c1.push(vn2); - p2.props = {value: context.state['color']}; - extra.handlers['change' + 2] = extra.handlers['change' + 2] || ((ev) => {context.state['color'] = ev.target.value}); + p2.props = {value: context['state'].color}; + extra.handlers['change' + 2] = extra.handlers['change' + 2] || ((ev) => {context['state'].color = ev.target.value}); p2.on['change'] = extra.handlers['change' + 2]; p2.hook = { create: (_, n) => { - n.elm.value=context.state['color']; + n.elm.value=context['state'].color; }, }; var _3 = ''; @@ -1195,6 +1195,29 @@ exports[`t-model directive on a select 1`] = ` }" `; +exports[`t-model directive on a sub state key 1`] = ` +"function anonymous(context,extra +) { + var h = this.h; + let c1 = [], p1 = {key:1}; + var vn1 = h('div', p1, c1); + let c2 = [], p2 = {key:2,on:{}}; + var vn2 = h('input', p2, c2); + c1.push(vn2); + p2.props = {value: context['state'].something.text}; + extra.handlers['input' + 2] = extra.handlers['input' + 2] || ((ev) => {context['state'].something.text = ev.target.value}); + p2.on['input'] = extra.handlers['input' + 2]; + let c3 = [], p3 = {key:3}; + var vn3 = h('span', p3, c3); + c1.push(vn3); + var _4 = context['state'].something.text; + if (_4 || _4 === 0) { + c3.push({text: _4}); + } + return vn1; +}" +`; + exports[`t-model directive on an input type=radio 1`] = ` "function anonymous(context,extra ) { @@ -1207,8 +1230,8 @@ exports[`t-model directive on an input type=radio 1`] = ` let c5 = [], p5 = {key:5,attrs:{type: _2,id: _3,value: _4},on:{}}; var vn5 = h('input', p5, c5); c1.push(vn5); - p5.props = {checked:context.state['choice'] === 'One'}; - extra.handlers['click' + 5] = extra.handlers['click' + 5] || ((ev) => {context.state['choice'] = ev.target.value}); + p5.props = {checked:context['state'].choice === 'One'}; + extra.handlers['click' + 5] = extra.handlers['click' + 5] || ((ev) => {context['state'].choice = ev.target.value}); p5.on['click'] = extra.handlers['click' + 5]; var _6 = 'radio'; var _7 = 'two'; @@ -1216,8 +1239,8 @@ exports[`t-model directive on an input type=radio 1`] = ` let c9 = [], p9 = {key:9,attrs:{type: _6,id: _7,value: _8},on:{}}; var vn9 = h('input', p9, c9); c1.push(vn9); - p9.props = {checked:context.state['choice'] === 'Two'}; - extra.handlers['click' + 9] = extra.handlers['click' + 9] || ((ev) => {context.state['choice'] = ev.target.value}); + p9.props = {checked:context['state'].choice === 'Two'}; + extra.handlers['click' + 9] = extra.handlers['click' + 9] || ((ev) => {context['state'].choice = ev.target.value}); p9.on['click'] = extra.handlers['click' + 9]; let c10 = [], p10 = {key:10}; var vn10 = h('span', p10, c10); @@ -1241,8 +1264,8 @@ exports[`t-model directive on an input, type=checkbox 1`] = ` let c3 = [], p3 = {key:3,attrs:{type: _2},on:{}}; var vn3 = h('input', p3, c3); c1.push(vn3); - p3.props = {checked: context.state['flag']}; - extra.handlers['input' + 3] = extra.handlers['input' + 3] || ((ev) => {context.state['flag'] = ev.target.checked}); + p3.props = {checked: context['state'].flag}; + extra.handlers['input' + 3] = extra.handlers['input' + 3] || ((ev) => {context['state'].flag = ev.target.checked}); p3.on['input'] = extra.handlers['input' + 3]; let c4 = [], p4 = {key:4}; var vn4 = h('span', p4, c4); diff --git a/tests/component/component.test.ts b/tests/component/component.test.ts index 52930c1c..dac65d42 100644 --- a/tests/component/component.test.ts +++ b/tests/component/component.test.ts @@ -3603,6 +3603,30 @@ describe("t-model directive", () => { expect(select.value).toBe("red"); }); + test("on a sub state key", async () => { + env.qweb.addTemplates(` + +
+ + +
+
`); + + class SomeComponent extends Widget { + state = { something: {text: "" }}; + } + const comp = new SomeComponent(env); + await comp.mount(fixture); + + expect(fixture.innerHTML).toBe("
"); + + const input = fixture.querySelector("input")!; + await editInput(input, "test"); + expect(comp.state.something.text).toBe("test"); + expect(fixture.innerHTML).toBe("
test
"); + expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot(); + }); + test(".lazy modifier", async () => { env.qweb.addTemplates(`