[IMP] component: allow component name in templates

closes #186
This commit is contained in:
Géry Debongnie
2019-06-18 09:24:52 +02:00
parent d1cf6b1b8d
commit 35c1de26b8
11 changed files with 139 additions and 123 deletions
+27 -33
View File
@@ -337,15 +337,14 @@ This is the opposite method of `mounted`.
### Composition
The example above shows a QWeb template with a `t-on-click` directive. Widget
templates are standard [QWeb](qweb.md) templates, but with an extra directive:
`t-widget`. With the `t-widget` directive, widget templates can declare sub
widgets:
The example above shows a QWeb template with a sub component. In a template,
components are declared with a tagname corresponding to the class name. It has
to be capitalized.
```xml
<div t-name="ParentWidget">
<span>some text</span>
<t t-widget="MyWidget" info="13"/>
<MyWidget info="13" />
</div>
```
@@ -357,22 +356,18 @@ class ParentWidget extends owl.Component {
```
In this example, the `ParentWidget`'s template creates a widget `MyWidget` just
after the span. The `info` key will be added to the subwidget's props. Each
props is a string which represents a javascript (QWeb) expression, so it is
after the span. The `info` key will be added to the subwidget's `props`. Each
`props` is a string which represents a javascript (QWeb) expression, so it is
dynamic. If it is necessary to give a string, this can be done by quoting it:
`someString="'somevalue'"`. See the
[QWeb](qweb.md) documentation for more information on the `t-widget` directive.
`someString="'somevalue'"`.
Note that the rendering context for the template is the widget itself. This means
that the template can access `state`, `props`, `env`, or any methods defined in the widget.
The `t-widget` directive is the key to a declarative component
system. It allows a template to define where and how a sub widget is created
and/or updated. For example:
```xml
<div t-name="ParentWidget">
<t t-widget="ChildWidget" count="state.val"/>
<ChildWidget count="state.val" />
</div>
```
@@ -397,12 +392,11 @@ the subwidget will also be updated automatically.
Note that there are some restrictions on prop names: `class`, `style` and any
string which starts with `t-` are not allowed.
The `t-widget` directive also accepts dynamic values with string interpolation
(like the [`t-attf-`](qweb.md#dynamic-attributes) directive):
The `t-widget` directive can also be used to accept dynamic values with string interpolation (like the [`t-attf-`](qweb.md#dynamic-attributes) directive):
```xml
<div t-name="ParentWidget">
<t t-widget="ChildWidget{{id}}"/>
<t t-widget="ChildWidget{{id}}" />
</div>
```
@@ -419,7 +413,7 @@ root widget element.
```xml
<div t-name="ParentWidget">
<t t-widget="MyWidget" class="someClass" style="font-weight:bold;" info="13"/>
<MyWidget class="someClass" style="font-weight:bold;" info="13" />
</div>
```
@@ -431,7 +425,7 @@ class that need to be removed. This is why we only support the explicit syntax
with a class object:
```xml
<t t-widget="MyWidget" t-att-class="{a: state.flagA, b: state.flagB}" />
<MyWidget t-att-class="{a: state.flagA, b: state.flagB}" />
```
### Event Handling
@@ -463,7 +457,7 @@ event.
A _business_ DOM event is triggered by a call to `trigger` on a component.
```xml
<t t-widget="MyWidget" t-on-menu-loaded="someMethod"/>
<MyWidget t-on-menu-loaded="someMethod" />
```
```js
@@ -538,8 +532,8 @@ class Form extends owl.Component {
```xml
<div>
<input t-on-input="_updateInputValue"/>
<span t-esc="state.text"/>
<input t-on-input="_updateInputValue" />
<span t-esc="state.text" />
</div>
```
@@ -559,8 +553,8 @@ class Form extends owl.Component {
```xml
<div>
<input t-model="text"/>
<span t-esc="state.text"/>
<input t-model="text" />
<span t-esc="state.text" />
</div>
```
@@ -586,7 +580,7 @@ The `t-model` directive works with `<input>`, `<input type="checkbox">`,
<label for="red">Red</label>
</span>
<span>
<input type="radio" name="color" id="blue" value="blue" t-model="color"/>
<input type="radio" name="color" id="blue" value="blue" t-model="color" />
<label for="blue">Blue</label>
</span>
</div>
@@ -604,7 +598,7 @@ Like event handling, the `t-model` directive accepts some modifiers:
For example:
```xml
<input t-model.lazy="someVal"/>
<input t-model.lazy="someVal" />
```
These modifiers can be combined. For instance, `t-model.lazy.number` will only
@@ -627,7 +621,7 @@ There are three main use cases:
```xml
<span t-foreach="todos" t-as="todo" t-key="todo.id">
<t t-esc="todo.text"/>
<t t-esc="todo.text" />
</span>
```
@@ -836,7 +830,7 @@ Like the `t-on` directive, it can work either on a DOM node, or on a component:
```xml
<div>
<div t-ref="someDiv"/>
<t t-widget="SubWidget" t-ref="someWidget"/>
<SubWidget t-ref="someWidget"/>
</div>
```
@@ -894,14 +888,14 @@ Slots are defined by the caller, with the `t-set` directive:
```xml
<div t-name="SomeWidget">
<div>some widget</div>
<t t-widget="Dialog" title="Some Dialog">
<Dialog title="Some Dialog">
<t t-set="content">
<div>hey</div>
</t>
<t t-set="footer">
<button t-on-click="doSomething">ok</button>
</t>
</t>
</Dialog>
</div>
```
@@ -933,9 +927,9 @@ be considered the `default` slot. For example:
```xml
<div t-name="Parent">
<t t-widget="Child">
<Child>
<span>some content</span>
</t>
</Child>
</div>
<div t-name="Child">
@@ -979,7 +973,7 @@ Here are a few tips on how to work with asynchronous widgets:
```xml
<div t-name="ParentWidget">
<t t-widget="SyncChild"/>
<t t-widget="AsyncChild" t-asyncroot="1"/>
<SyncChild />
<AsyncChild t-asyncroot="1"/>
</div>
```
+1 -1
View File
@@ -132,7 +132,7 @@ It's API is quite simple:
...
class ParentWidget extends owl.Component { ... }
qweb.addTemplate("ParentWidget", "<div><t t-widget='Dialog'/></div>");
qweb.addTemplate("ParentWidget", "<div><Dialog/></div>");
```
## Reference
+7
View File
@@ -346,6 +346,13 @@ export class QWeb {
return;
}
const firstLetter = node.tagName[0];
if (firstLetter === firstLetter.toUpperCase()) {
// this is a component, we modify in place the xml document to change
// <SomeComponent ... /> to <t t-widget="SomeComponent" ... />
node.setAttribute('t-widget', node.tagName);
node.nodeValue = 't';
}
const attributes = (<Element>node).attributes;
const validDirectives: {
+1 -1
View File
@@ -208,7 +208,7 @@ const T_WIDGET_MODS_CODE = Object.assign({}, MODS_CODE, {
* explanation of the code generated by the t-widget directive for the following
* situation:
* ```xml
* <t t-widget="child"
* <Child
* t-key="'somestring'"
* flag="state.flag"
* t-transition="fade"/>
+1 -1
View File
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`animations t-transition combined with t-widget 1`] = `
exports[`animations t-transition combined with component 1`] = `
"function anonymous(context,extra
) {
let utils = this.utils;
+3 -3
View File
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`async rendering delayed t-widget with t-asyncroot directive 1`] = `
exports[`async rendering delayed component with t-asyncroot directive 1`] = `
"function anonymous(context,extra
) {
let utils = this.utils;
@@ -81,7 +81,7 @@ exports[`async rendering delayed t-widget with t-asyncroot directive 1`] = `
}"
`;
exports[`async rendering fast t-widget with t-asyncroot directive 1`] = `
exports[`async rendering fast component with t-asyncroot directive 1`] = `
"function anonymous(context,extra
) {
let utils = this.utils;
@@ -650,7 +650,7 @@ exports[`other directives with t-widget t-on with prevent and self modifiers (or
}
}
if (!w4) {
let widgetKey4 = \`child\`;
let widgetKey4 = \`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);
+2 -2
View File
@@ -170,12 +170,12 @@ describe("animations", () => {
expect(spanNode.className).toBe("");
});
test("t-transition combined with t-widget", async () => {
test("t-transition combined with component", async () => {
expect.assertions(5);
env.qweb.addTemplate(
"Parent",
`<div><t t-widget="Child" t-transition="chimay"/></div>`
`<div><Child t-transition="chimay"/></div>`
);
env.qweb.addTemplate("Child", `<span>blue</span>`);
class Parent extends Widget {
+65 -50
View File
@@ -468,7 +468,7 @@ describe("lifecycle hooks", () => {
`
<div>
<div t-if="state.flag">
<t t-widget="ChildWidget" n="state.n"/>
<ChildWidget n="state.n"/>
</div>
</div>`
);
@@ -549,7 +549,7 @@ describe("lifecycle hooks", () => {
let def = makeDeferred();
env.qweb.addTemplate(
"Parent",
'<span><t t-widget="Child" n="state.n"/></span>'
'<span><Child n="state.n"/></span>'
);
class Parent extends Widget {
state = { n: 1 };
@@ -601,7 +601,7 @@ describe("lifecycle hooks", () => {
env.qweb.addTemplate(
"Parent",
'<div><t t-widget="Child" a="state.a"/></div>'
'<div><Child a="state.a"/></div>'
);
class Parent extends Widget {
state = { a: 1 };
@@ -644,7 +644,7 @@ describe("lifecycle hooks", () => {
let shouldUpdate = false;
env.qweb.addTemplate(
"Parent",
`<div><t t-widget="Child" val="state.val"/></div>`
`<div><Child val="state.val"/></div>`
);
class Parent extends Widget {
state = { val: 42 };
@@ -889,11 +889,26 @@ describe("composition", () => {
delete QWeb.widgets["WidgetB"];
});
test("can define widgets in template without t-widget", async () => {
env.qweb.addTemplates(`
<templates>
<div t-name="P"><C a="1"/></div>
<span t-name="C"><t t-esc="props.a"/></span>
</templates>`);
class C extends Widget {}
class P extends Widget {
widgets = {C};
}
const parent = new P(env);
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
});
test("throw a nice error if it cannot find widget", async () => {
expect.assertions(1);
env.qweb.addTemplate(
"Parent",
`<div><t t-widget="SomeMispelledWidget"/></div>`
`<div><SomeMispelledWidget /></div>`
);
class Parent extends Widget {
widgets = { SomeWidget: Widget };
@@ -1074,11 +1089,11 @@ describe("composition", () => {
test("sub widgets are destroyed if no longer in dom, then recreated", async () => {
env.qweb.addTemplate(
"ParentWidget",
`<div><t t-if="state.ok"><t t-widget="counter"/></t></div>`
`<div><t t-if="state.ok"><Counter /></t></div>`
);
class ParentWidget extends Widget {
state = { ok: true };
widgets = { counter: Counter };
widgets = { Counter };
}
const widget = new ParentWidget(env);
await widget.mount(fixture);
@@ -1102,11 +1117,11 @@ describe("composition", () => {
test("sub widgets with t-keepalive are not destroyed if no longer in dom", async () => {
env.qweb.addTemplate(
"ParentWidget",
`<div><t t-if="state.ok"><t t-widget="counter" t-keepalive="1"/></t></div>`
`<div><t t-if="state.ok"><Counter t-keepalive="1"/></t></div>`
);
class ParentWidget extends Widget {
state = { ok: true };
widgets = { counter: Counter };
widgets = { Counter };
}
const widget = new ParentWidget(env);
await widget.mount(fixture);
@@ -1135,7 +1150,7 @@ describe("composition", () => {
test("sub widgets dom state with t-keepalive is preserved", async () => {
env.qweb.addTemplate(
"ParentWidget",
`<div><t t-if="state.ok"><t t-widget="InputWidget" t-keepalive="1"/></t></div>`
`<div><t t-if="state.ok"><InputWidget t-keepalive="1"/></t></div>`
);
class ParentWidget extends Widget {
state = { ok: true };
@@ -1163,7 +1178,7 @@ describe("composition", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="ParentWidget">
<t t-if="state.ok"><t t-widget="ChildWidget" t-ref="child" t-keepalive="1"/></t>
<t t-if="state.ok"><ChildWidget t-ref="child" t-keepalive="1"/></t>
</div>
<span t-name="ChildWidget">Hello</span>
</templates>`);
@@ -1275,7 +1290,7 @@ describe("composition", () => {
`<div>
<h1 t-if="state.flag">hey</h1>
<h2 t-else="1">noo</h2>
<span><t t-widget="ChildWidget"/></span>
<span><ChildWidget/></span>
<t t-if="state.flag"><span>test</span></t>
</div>`
);
@@ -1723,18 +1738,18 @@ describe("other directives with t-widget", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="ParentWidget">
<t t-widget="child" t-on-ev.prevent.self="onEv"/>
<Child t-on-ev.prevent.self="onEv"/>
</div>
<div t-name="Child"><t t-widget="child"/></div>
<div t-name="Child"><GrandChild/></div>
</templates>
`);
const steps: boolean[] = [];
class ParentWidget extends Widget {
widgets = { child: Child };
widgets = { Child };
onEv() {}
}
class Child extends Widget {
widgets = { child: GrandChild };
widgets = { GrandChild };
}
class GrandChild extends Widget {}
const widget = new ParentWidget(env);
@@ -1962,7 +1977,7 @@ describe("random stuff/miscellaneous", () => {
steps.push(`${this.name}:destroy`);
}
}
env.qweb.addTemplate("A", `<div>A<t t-widget="B"/><t t-widget="C"/></div>`);
env.qweb.addTemplate("A", `<div>A<B /><C /></div>`);
class A extends TestWidget {
widgets = { B, C };
name = "A";
@@ -1978,9 +1993,9 @@ describe("random stuff/miscellaneous", () => {
env.qweb.addTemplate(
"C",
`
<div>C<t t-widget="D"/>
<t t-if="state.flag" t-widget="E"/>
<t t-else="!state.flag" t-widget="F"/>
<div>C<D />
<E t-if="state.flag" />
<F t-else="!state.flag" />
</div>`
);
class C extends TestWidget {
@@ -2102,7 +2117,7 @@ describe("async rendering", () => {
let n = 0;
env.qweb.addTemplate(
"W",
`<div><t t-if="state.val > 1"><t t-widget="Child" val="state.val"/></t></div>`
`<div><t t-if="state.val > 1"><Child val="state.val"/></t></div>`
);
class W extends Widget {
widgets = { Child };
@@ -2156,8 +2171,8 @@ describe("async rendering", () => {
"Parent",
`
<div>
<t t-if="state.flagA"><t t-widget="ChildA"/></t>
<t t-if="state.flagB"><t t-widget="ChildB"/></t>
<t t-if="state.flagA"><ChildA /></t>
<t t-if="state.flagB"><ChildB /></t>
</div>`
);
class Parent extends Widget {
@@ -2203,8 +2218,8 @@ describe("async rendering", () => {
"Parent",
`
<div>
<t t-widget="ChildA" val="state.valA"/>
<t t-if="state.flagB"><t t-widget="ChildB" val="state.valB"/></t>
<ChildA val="state.valA"/>
<t t-if="state.flagB"><ChildB val="state.valB"/></t>
</div>`
);
class Parent extends Widget {
@@ -2254,7 +2269,7 @@ describe("async rendering", () => {
<ul>
<t t-foreach="items" t-as="item">
<li t-key="'li_'+item">
<t t-widget="Child" item="item"/>
<Child item="item"/>
</li>
</t>
</ul>
@@ -2271,7 +2286,7 @@ describe("async rendering", () => {
test("properly behave when destroyed/unmounted while rendering ", async () => {
let def = Promise.resolve();
env.qweb.addTemplate("Child", `<div><t t-widget="SubChild"/></div>`);
env.qweb.addTemplate("Child", `<div><SubChild /></div>`);
class Child extends Widget {
widgets = { SubChild };
mounted() {
@@ -2298,7 +2313,7 @@ describe("async rendering", () => {
env.qweb.addTemplate(
"Parent",
`
<div><t t-if="state.flag"><t t-widget="Child" val="state.val"/></t></div>`
<div><t t-if="state.flag"><Child val="state.val"/></t></div>`
);
class Parent extends Widget {
widgets = { Child };
@@ -2331,8 +2346,8 @@ describe("async rendering", () => {
<span t-name="ChildB">b<t t-esc="props.val"/></span>
<span t-name="Parent">
<t t-if="state.flag">
<t t-widget="ChildA" val="state.valA"/>
<t t-widget="ChildB" val="state.valB"/>
<ChildA val="state.valA"/>
<ChildB val="state.valB"/>
</t>
</span>
</templates>
@@ -2368,14 +2383,14 @@ describe("async rendering", () => {
expect(destroyCount).toBe(0);
});
test("delayed t-widget with t-asyncroot directive", async () => {
test("delayed component with t-asyncroot directive", async () => {
env.qweb.addTemplates(`
<templates>
<div t-name="Parent">
<button t-on-click="updateApp">Update App State</button>
<div class="children">
<t t-widget="Child" val="state.val"/>
<t t-widget="AsyncChild" t-asyncroot="1" val="state.val"/>
<Child val="state.val"/>
<AsyncChild t-asyncroot="1" val="state.val"/>
</div>
</div>
<span t-name="Child"><t t-esc="props.val"/></span>
@@ -2423,14 +2438,14 @@ describe("async rendering", () => {
);
});
test("fast t-widget with t-asyncroot directive", async () => {
test("fast component with t-asyncroot directive", async () => {
env.qweb.addTemplates(`
<templates>
<div t-name="Parent">
<button t-on-click="updateApp">Update App State</button>
<div class="children">
<t t-widget="Child" t-asyncroot="1" val="state.val"/>
<t t-widget="AsyncChild" val="state.val"/>
<Child t-asyncroot="1" val="state.val"/>
<AsyncChild val="state.val"/>
</div>
</div>
<span t-name="Child"><t t-esc="props.val"/></span>
@@ -2484,8 +2499,8 @@ describe("async rendering", () => {
<div t-name="Parent">
<button t-on-click="updateApp">Update App State</button>
<div class="children">
<t t-widget="Child" val="state.val"/>
<t t-widget="AsyncChild" t-asyncroot="1" val="state.val"/>
<Child val="state.val"/>
<AsyncChild t-asyncroot="1" val="state.val"/>
</div>
</div>
<span t-name="Child" t-on-click="increment">
@@ -2617,9 +2632,9 @@ describe("updating environment", () => {
});
test("updating parent env does modify child env, part 2", async () => {
env.qweb.addTemplate("ParentWidget", `<div><t t-widget="child"/></div>`);
env.qweb.addTemplate("ParentWidget", `<div><Child/></div>`);
class ParentWidget extends Widget {
widgets = { child: Widget };
widgets = { Child: Widget };
}
const parent = new ParentWidget(env);
await parent.mount(fixture);
@@ -2642,7 +2657,7 @@ describe("updating environment", () => {
});
test("updating env force rerendering children", async () => {
env.qweb.addTemplate("Parent", `<div><t t-widget="Child"/></div>`);
env.qweb.addTemplate("Parent", `<div><Child /></div>`);
class Parent extends Widget {
widgets = { Child };
}
@@ -2680,7 +2695,7 @@ describe("widget and observable state", () => {
expect.assertions(1);
env.qweb.addTemplate(
"Parent",
`<div><t t-widget="Child" obj="state.obj"/></div>`
`<div><Child obj="state.obj"/></div>`
);
class Parent extends Widget {
state = { obj: { coffee: 1 } };
@@ -2822,10 +2837,10 @@ describe("t-slot directive", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="Parent">
<t t-widget="Dialog">
<Dialog>
<t t-set="header"><span>header</span></t>
<t t-set="footer"><span>footer</span></t>
</t>
</Dialog>
</div>
<div t-name="Dialog">
<div><t t-slot="header"/></div>
@@ -2852,9 +2867,9 @@ describe("t-slot directive", () => {
<templates>
<div t-name="Parent">
<span class="counter"><t t-esc="state.val"/></span>
<t t-widget="Dialog">
<Dialog>
<t t-set="footer"><button t-on-click="doSomething">do something</button></t>
</t>
</Dialog>
</div>
<span t-name="Dialog"><t t-slot="footer"/></span>
</templates>
@@ -2887,9 +2902,9 @@ describe("t-slot directive", () => {
<templates>
<div t-name="Parent">
<span class="counter"><t t-esc="state.val"/></span>
<t t-widget="Dialog">
<Dialog>
<t t-set="footer"><button t-ref="myButton" t-on-click="doSomething">do something</button></t>
</t>
</Dialog>
</div>
<span t-name="Dialog"><t t-slot="footer"/></span>
</templates>
@@ -3153,9 +3168,9 @@ describe("t-model directive", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="Parent">
<t t-widget="Dialog">
<Dialog>
<span>sts rocks</span>
</t>
</Dialog>
</div>
<div t-name="Dialog"><t t-slot="default"/></div>
</templates>
+11 -11
View File
@@ -534,8 +534,8 @@ describe("connecting a component to store", () => {
"App",
`
<div>
<t t-foreach="props.todos" t-as="todo" t-key="todo">
<t t-widget="Todo" msg="todo.msg"/>
<t t-foreach="props.todos" t-as="todo" >
<Todo msg="todo.msg" t-key="todo"/>
</t>
</div>`
);
@@ -624,8 +624,8 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="App">
<t t-foreach="props.todos" t-as="todo" t-key="todo">
<t t-widget="Todo" msg="todo.msg"/>
<t t-foreach="props.todos" t-as="todo">
<Todo msg="todo.msg" t-key="todo" />
</t>
</div>
<span t-name="Todo"><t t-esc="props.msg"/></span>
@@ -734,7 +734,7 @@ describe("connecting a component to store", () => {
"TodoList",
`<div>
<t t-foreach="props.todos" t-as="todo">
<t t-widget="ConnectedTodo" id="todo.id"/>
<ConnectedTodo id="todo.id" t-key="todo.id"/>
</t>
</div>`
);
@@ -801,7 +801,7 @@ describe("connecting a component to store", () => {
"TodoList",
`<div>
<t t-foreach="props.todos" t-as="todo">
<t t-widget="ConnectedTodo" id="todo.id"/>
<ConnectedTodo id="todo.id" t-key="todo.id"/>
</t>
</div>`
);
@@ -839,7 +839,7 @@ describe("connecting a component to store", () => {
env.qweb.addTemplate(
"App",
`<div>
<t t-widget="ConnectedBeer" id="state.beerId"/>
<ConnectedBeer id="state.beerId"/>
</div>`
);
class App extends Component<any, any, any> {
@@ -923,8 +923,8 @@ describe("connecting a component to store", () => {
env.qweb.addTemplate(
"App",
`<div>
<t t-widget="ConnectedBeer" id="state.beerId"/>
</div>`
<ConnectedBeer id="state.beerId"/>
</div>`
);
class App extends Component<any, any, any> {
widgets = { ConnectedBeer };
@@ -995,7 +995,7 @@ describe("connecting a component to store", () => {
env.qweb.addTemplate(
"App",
`<div>
<t t-widget="ConnectedBeer" id="state.beerId"/>
<ConnectedBeer id="state.beerId"/>
</div>`
);
class App extends Component<any, any, any> {
@@ -1074,7 +1074,7 @@ describe("connecting a component to store", () => {
"Parent",
`
<div>
<t t-widget="Child" key="props.current"/>
<Child key="props.current"/>
</div>
`
);
+19 -19
View File
@@ -26,8 +26,8 @@ const COMPONENTS_XML = `<templates>
</button>
<div t-name="App">
<t t-widget="Counter"/>
<t t-widget="Counter"/>
<Counter />
<Counter />
</div>
</templates>`;
@@ -88,7 +88,7 @@ const ANIMATION_XML = `<templates>
<div class="demo">
<button t-on-click="toggle('widgetFlag')">Toggle widget</button>
<div>
<t t-widget="Counter" t-if="state.widgetFlag" t-transition="fade"/>
<Counter t-if="state.widgetFlag" t-transition="fade"/>
</div>
</div>
@@ -98,7 +98,7 @@ const ANIMATION_XML = `<templates>
<button t-on-click="addNumber">Add a number</button>
<div>
<t t-foreach="state.numbers" t-as="n">
<span t-transition="fade" class="numberspan"><t t-esc="n"/></span>
<span t-transition="fade" class="numberspan" t-key="n"><t t-esc="n"/></span>
</t>
</div>
</div>
@@ -236,7 +236,7 @@ const LIFECYCLE_DEMO_XML = `<templates>
<button t-on-click="increment">Increment Parent State</button>
<button t-on-click="toggleSubWidget">Toggle SubWidget</button>
<div t-if="state.flag">
<t t-widget="DemoWidget" n="state.n"/>
<DemoWidget n="state.n"/>
</div>
</div>
</templates>`;
@@ -483,7 +483,7 @@ const TODO_APP_STORE_XML = `<templates>
<label for="toggle-all"></label>
<ul class="todo-list">
<t t-foreach="visibleTodos" t-as="todo">
<t t-widget="TodoItem" t-key="todo.id" id="todo.id" completed="todo.completed" title="todo.title"/>
<TodoItem t-key="todo.id" id="todo.id" completed="todo.completed" title="todo.title"/>
</t>
</ul>
</section>
@@ -995,17 +995,17 @@ const RESPONSIVE_XML = `<templates>
</div>
<div t-name="App" class="app" t-att-class="{mobile: env.isMobile, desktop: !env.isMobile}">
<t t-widget="Navbar"/>
<t t-widget="ControlPanel"/>
<Navbar/>
<ControlPanel/>
<div class="content-wrapper" t-if="!env.isMobile">
<div class="content">
<t t-widget="FormView"/>
<t t-widget="Chatter"/>
<FormView />
<Chatter />
</div>
</div>
<t t-else="1">
<t t-widget="FormView"/>
<t t-widget="Chatter"/>
<FormView />
<Chatter />
</t>
</div>
</templates>
@@ -1128,17 +1128,17 @@ const SLOTS_XML = `<templates>
</div>
<div t-name="App" class="main">
<t t-widget="Card" title="'Title card A'">
<Card title="'Title card A'">
<t t-set="content">Content of card 1... [<t t-esc="state.a"/>]</t>
<t t-set="footer"><button t-on-click="inc('a', 1)">Increment A</button></t>
</t>
<t t-widget="Card" title="'Title card B'">
</Card>
<Card title="'Title card B'">
<div t-set="content">
<div>Card 2... [<t t-esc="state.b"/>]</div>
<t t-widget="Counter"/>
<Counter />
</div>
<t t-set="footer"><button t-on-click="inc('b', -1)">Decrement B</button></t>
</t>
</Card>
</div>
</templates>`;
@@ -1226,8 +1226,8 @@ app.mount(document.body);
const ASYNC_COMPONENTS_XML = `<templates>
<div t-name="App" class="app">
<button t-on-click="increment">Increment</button>
<t t-widget="SlowWidget" value="state.value"/>
<t t-widget="NotificationList" t-asyncroot="1" notifications="state.notifs"/>
<SlowWidget value="state.value"/>
<NotificationList t-asyncroot="1" notifications="state.notifs"/>
</div>
<div t-name="SlowWidget" class="value" >
+2 -2
View File
@@ -24,14 +24,14 @@
<a class="btn flash" t-on-click="downloadCode" title="Download a Zip with this Code"><i class="fas fa-download"></i></a>
<a class="layout-selector flash" t-on-click="toggleLayout" title="Toggle Layout"><i class="fas" t-att-class="state.splitLayout ? 'fa-toggle-on' : 'fa-toggle-off'"></i></a>
</div>
<t t-widget="TabbedEditor"
<TabbedEditor
js="state.js"
css="!state.splitLayout and state.css"
xml="!state.splitLayout and state.js"
t-att-style="topEditorStyle"/>
<t t-if="state.splitLayout">
<div class="separator horizontal"/>
<t t-widget="TabbedEditor" t-keepalive="1"
<TabbedEditor t-keepalive="1"
js="false"
css="state.css"
xml="state.xml"