[IMP] qweb: t-widget: accept dyn values

This commit is contained in:
Aaron Bohy
2019-05-28 14:30:08 +02:00
committed by Géry Debongnie
parent 26f14180e2
commit 376f801541
5 changed files with 157 additions and 22 deletions
+29 -4
View File
@@ -13,7 +13,6 @@
- [`t-if` directive](#t-if-directive) - [`t-if` directive](#t-if-directive)
- [Expression evaluation](#expression-evaluation) - [Expression evaluation](#expression-evaluation)
- [Dynamic attributes (`t-att` and `t-attf` directives)](#dynamic-attributes-t-att-and-t-attf-directives) - [Dynamic attributes (`t-att` and `t-attf` directives)](#dynamic-attributes-t-att-and-t-attf-directives)
- [`t-attf` directive (dynamic attributes)](#t-att-directive-dynamic-attributes)
- [`t-call` directive (sub templates)](#t-call-directive-sub-templates) - [`t-call` directive (sub templates)](#t-call-directive-sub-templates)
- [JS/OWL Specific Extensions](#jsowl-specific-extensions) - [JS/OWL Specific Extensions](#jsowl-specific-extensions)
@@ -453,6 +452,30 @@ constructor. This will be assigned to the `props` variable, which can be accesse
on the widget (and also, in the template). Whenever the state is updated, then on the widget (and also, in the template). Whenever the state is updated, then
the subwidget will also be updated automatically. the subwidget will also be updated automatically.
The `t-widget` directive also accepts dynamic values with string interpolation
(like the [`t-attf-`](#dynamic-attributes-t-att-and-t-attf-directives) directive):
```xml
<div t-name="ParentWidget">
<t t-widget="ChildWidget#{id}"/>
</div>
```
```js
class ParentWidget {
widgets = { ChildWidget1, ChildWidget2 };
state = { id: 1 };
}
```
Similarly to `t-attf-`, there is an alternate form of string interpolation:
```xml
<div t-name="ParentWidget">
<t t-widget="ChildWidget{{id}}"/>
</div>
```
### `t-ref` directive ### `t-ref` directive
The `t-ref` directive helps a component keep reference to some inside part of it. The `t-ref` directive helps a component keep reference to some inside part of it.
@@ -481,8 +504,9 @@ Note: if used on a component, the reference will be set in the `refs`
variable between `willPatch` and `patched`. variable between `willPatch` and `patched`.
The `t-ref` directive also accepts dynamic values with string interpolation The `t-ref` directive also accepts dynamic values with string interpolation
(like the `t-attf-` directive). For example, if we have `id` set to 44 in the (like the [`t-attf-`](#dynamic-attributes-t-att-and-t-attf-directives) and
rendering context, [`t-widget-`](#component-t-widget-t-props) directives). For example, if we have
`id` set to 44 in the rendering context,
```xml ```xml
<div t-ref="widget_#{id}"/> <div t-ref="widget_#{id}"/>
@@ -492,7 +516,8 @@ rendering context,
this.refs.widget_44; this.refs.widget_44;
``` ```
Similarly to `t-attf-`, there is an alternate form of string interpolation: Similarly to `t-attf-` and `t-widget`, there is an alternate form of string
interpolation:
```xml ```xml
<div t-ref="widget_{{id}}"/> <div t-ref="widget_{{id}}"/>
+3 -2
View File
@@ -459,13 +459,14 @@ QWeb.addDirective({
ctx.addIf(`!w${widgetID}`); ctx.addIf(`!w${widgetID}`);
// new widget // new widget
ctx.addLine(`let widgetKey${widgetID} = ${ctx.interpolate(value)};`);
ctx.addLine( ctx.addLine(
`let W${widgetID} = context.widgets && context.widgets['${value}'] || QWeb.widgets['${value}'];` `let W${widgetID} = context.widgets && context.widgets[widgetKey${widgetID}] || QWeb.widgets[widgetKey${widgetID}];`
); );
// maybe only do this in dev mode... // maybe only do this in dev mode...
ctx.addLine( ctx.addLine(
`if (!W${widgetID}) {throw new Error(\`Cannot find the definition of widget "${value}"\`)}` `if (!W${widgetID}) {throw new Error('Cannot find the definition of widget "' + widgetKey${widgetID} + '"')}`
); );
ctx.addLine(`w${widgetID} = new W${widgetID}(owner, props${widgetID});`); ctx.addLine(`w${widgetID} = new W${widgetID}(owner, props${widgetID});`);
ctx.addLine( ctx.addLine(
+6 -4
View File
@@ -20,8 +20,9 @@ exports[`animations t-transition combined with t-widget 1`] = `
w4 = false w4 = false
} }
if (!w4) { if (!w4) {
let W4 = context.widgets && context.widgets['Child'] || QWeb.widgets['Child']; let widgetKey4 = \`Child\`;
if (!W4) {throw new Error(\`Cannot find the definition of widget \\"Child\\"\`)} let W4 = context.widgets && context.widgets[widgetKey4] || QWeb.widgets[widgetKey4];
if (!W4) {throw new Error('Cannot find the definition of widget \\"' + widgetKey4 + '\\"')}
w4 = new W4(owner, props4); w4 = new W4(owner, props4);
context.__owl__.cmap[4] = w4.__owl__.id; context.__owl__.cmap[4] = w4.__owl__.id;
def3 = w4._prepare(); def3 = w4._prepare();
@@ -59,8 +60,9 @@ exports[`animations t-transition combined with t-widget and t-if 1`] = `
w4 = false w4 = false
} }
if (!w4) { if (!w4) {
let W4 = context.widgets && context.widgets['Child'] || QWeb.widgets['Child']; let widgetKey4 = \`Child\`;
if (!W4) {throw new Error(\`Cannot find the definition of widget \\"Child\\"\`)} let W4 = context.widgets && context.widgets[widgetKey4] || QWeb.widgets[widgetKey4];
if (!W4) {throw new Error('Cannot find the definition of widget \\"' + widgetKey4 + '\\"')}
w4 = new W4(owner, props4); w4 = new W4(owner, props4);
context.__owl__.cmap[4] = w4.__owl__.id; context.__owl__.cmap[4] = w4.__owl__.id;
def3 = w4._prepare(); def3 = w4._prepare();
+87 -10
View File
@@ -21,8 +21,9 @@ exports[`class and style attributes with t-widget dynamic t-att-style is properl
w4 = false w4 = false
} }
if (!w4) { if (!w4) {
let W4 = context.widgets && context.widgets['child'] || QWeb.widgets['child']; let widgetKey4 = \`child\`;
if (!W4) {throw new Error(\`Cannot find the definition of widget \\"child\\"\`)} let W4 = context.widgets && context.widgets[widgetKey4] || QWeb.widgets[widgetKey4];
if (!W4) {throw new Error('Cannot find the definition of widget \\"' + widgetKey4 + '\\"')}
w4 = new W4(owner, props4); w4 = new W4(owner, props4);
context.__owl__.cmap[4] = w4.__owl__.id; context.__owl__.cmap[4] = w4.__owl__.id;
def3 = w4._prepare(); def3 = w4._prepare();
@@ -57,8 +58,9 @@ exports[`class and style attributes with t-widget t-att-class is properly added/
w4 = false w4 = false
} }
if (!w4) { if (!w4) {
let W4 = context.widgets && context.widgets['child'] || QWeb.widgets['child']; let widgetKey4 = \`child\`;
if (!W4) {throw new Error(\`Cannot find the definition of widget \\"child\\"\`)} let W4 = context.widgets && context.widgets[widgetKey4] || QWeb.widgets[widgetKey4];
if (!W4) {throw new Error('Cannot find the definition of widget \\"' + widgetKey4 + '\\"')}
w4 = new W4(owner, props4); w4 = new W4(owner, props4);
context.__owl__.cmap[4] = w4.__owl__.id; context.__owl__.cmap[4] = w4.__owl__.id;
def3 = w4._prepare(); def3 = w4._prepare();
@@ -111,8 +113,9 @@ exports[`composition sub widgets with some state rendered in a loop 1`] = `
w7 = false w7 = false
} }
if (!w7) { if (!w7) {
let W7 = context.widgets && context.widgets['ChildWidget'] || QWeb.widgets['ChildWidget']; let widgetKey7 = \`ChildWidget\`;
if (!W7) {throw new Error(\`Cannot find the definition of widget \\"ChildWidget\\"\`)} let W7 = context.widgets && context.widgets[widgetKey7] || QWeb.widgets[widgetKey7];
if (!W7) {throw new Error('Cannot find the definition of widget \\"' + widgetKey7 + '\\"')}
w7 = new W7(owner, props7); w7 = new W7(owner, props7);
context.__owl__.cmap[key8] = w7.__owl__.id; context.__owl__.cmap[key8] = w7.__owl__.id;
def6 = w7._prepare(); def6 = w7._prepare();
@@ -127,6 +130,78 @@ exports[`composition sub widgets with some state rendered in a loop 1`] = `
}" }"
`; `;
exports[`composition t-widget with dynamic value 1`] = `
"function anonymous(context,extra
) {
let utils = this.utils;
let QWeb = this.constructor;
let owner = context;
var h = this.utils.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
//WIDGET
let _2_index = c1.length;
c1.push(null);
let def3;
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
let props4 = {};
if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode && props4 !== w4.__owl__.renderProps) {
w4.destroy();
w4 = false
}
if (!w4) {
let widgetKey4 = context['state'].widget;
let W4 = context.widgets && context.widgets[widgetKey4] || QWeb.widgets[widgetKey4];
if (!W4) {throw new Error('Cannot find the definition of widget \\"' + widgetKey4 + '\\"')}
w4 = new W4(owner, props4);
context.__owl__.cmap[4] = w4.__owl__.id;
def3 = w4._prepare();
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4._mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
} else {
def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue);
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;});
}
extra.promises.push(def3);
return vn1;
}"
`;
exports[`composition t-widget with dynamic value 2 1`] = `
"function anonymous(context,extra
) {
let utils = this.utils;
let QWeb = this.constructor;
let owner = context;
var h = this.utils.h;
let c1 = [], p1 = {key:1};
var vn1 = h('div', p1, c1);
//WIDGET
let _2_index = c1.length;
c1.push(null);
let def3;
let w4 = 4 in context.__owl__.cmap ? context.__owl__.children[context.__owl__.cmap[4]] : false;
let props4 = {};
if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode && props4 !== w4.__owl__.renderProps) {
w4.destroy();
w4 = false
}
if (!w4) {
let widgetKey4 = \`Widget\${context['state'].widget}\`;
let W4 = context.widgets && context.widgets[widgetKey4] || QWeb.widgets[widgetKey4];
if (!W4) {throw new Error('Cannot find the definition of widget \\"' + widgetKey4 + '\\"')}
w4 = new W4(owner, props4);
context.__owl__.cmap[4] = w4.__owl__.id;
def3 = w4._prepare();
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4._mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
} else {
def3 = w4._updateProps(props4, extra.forceUpdate, extra.patchQueue);
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;});
}
extra.promises.push(def3);
return vn1;
}"
`;
exports[`random stuff/miscellaneous snapshotting compiled code 1`] = ` exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
"function anonymous(context,extra "function anonymous(context,extra
) { ) {
@@ -148,8 +223,9 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
w4 = false w4 = false
} }
if (!w4) { if (!w4) {
let W4 = context.widgets && context.widgets['child'] || QWeb.widgets['child']; let widgetKey4 = \`child\`;
if (!W4) {throw new Error(\`Cannot find the definition of widget \\"child\\"\`)} let W4 = context.widgets && context.widgets[widgetKey4] || QWeb.widgets[widgetKey4];
if (!W4) {throw new Error('Cannot find the definition of widget \\"' + widgetKey4 + '\\"')}
w4 = new W4(owner, props4); w4 = new W4(owner, props4);
context.__owl__.cmap[key5] = w4.__owl__.id; context.__owl__.cmap[key5] = w4.__owl__.id;
def3 = w4._prepare(); def3 = w4._prepare();
@@ -183,8 +259,9 @@ exports[`random stuff/miscellaneous t-props should not be undefined (snapshottin
w4 = false w4 = false
} }
if (!w4) { if (!w4) {
let W4 = context.widgets && context.widgets['child'] || QWeb.widgets['child']; let widgetKey4 = \`child\`;
if (!W4) {throw new Error(\`Cannot find the definition of widget \\"child\\"\`)} let W4 = context.widgets && context.widgets[widgetKey4] || QWeb.widgets[widgetKey4];
if (!W4) {throw new Error('Cannot find the definition of widget \\"' + widgetKey4 + '\\"')}
w4 = new W4(owner, props4); w4 = new W4(owner, props4);
context.__owl__.cmap[4] = w4.__owl__.id; context.__owl__.cmap[4] = w4.__owl__.id;
def3 = w4._prepare(); def3 = w4._prepare();
+32 -2
View File
@@ -857,7 +857,7 @@ describe("composition", () => {
const widget = new ParentWidget(env); const widget = new ParentWidget(env);
await widget.mount(fixture); await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>world</div></div>"); expect(fixture.innerHTML).toBe("<div><div>world</div></div>");
delete QWeb.widgets['WidgetB']; delete QWeb.widgets["WidgetB"];
}); });
test("don't fallback to global registry if widget defined locally", async () => { test("don't fallback to global registry if widget defined locally", async () => {
@@ -871,7 +871,7 @@ describe("composition", () => {
const widget = new ParentWidget(env); const widget = new ParentWidget(env);
await widget.mount(fixture); await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>Belgium</span></div>"); expect(fixture.innerHTML).toBe("<div><span>Belgium</span></div>");
delete QWeb.widgets['WidgetB']; delete QWeb.widgets["WidgetB"];
}); });
test("throw a nice error if it cannot find widget", async () => { test("throw a nice error if it cannot find widget", async () => {
@@ -1254,6 +1254,36 @@ describe("composition", () => {
`) `)
); );
}); });
test("t-widget with dynamic value", async () => {
env.qweb.addTemplate(
"ParentWidget",
`<div><t t-widget="{{state.widget}}"/></div>`
);
class ParentWidget extends Widget {
widgets = { WidgetB };
state = { widget: "WidgetB" };
}
const widget = new ParentWidget(env);
await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>world</div></div>");
expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot();
});
test("t-widget with dynamic value 2", async () => {
env.qweb.addTemplate(
"ParentWidget",
`<div><t t-widget="Widget{{state.widget}}"/></div>`
);
class ParentWidget extends Widget {
widgets = { WidgetB };
state = { widget: "B" };
}
const widget = new ParentWidget(env);
await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>world</div></div>");
expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot();
});
}); });
describe("props evaluation (with t-props directive)", () => { describe("props evaluation (with t-props directive)", () => {