diff --git a/README.md b/README.md
index b566a3dd..80ae0f97 100644
--- a/README.md
+++ b/README.md
@@ -36,11 +36,6 @@ Here is a short example to illustrate interactive widgets:
```javascript
class ClickCounter extends owl.Component {
- inlineTemplate = `
- `;
-
state = { value: 0 };
increment() {
@@ -48,7 +43,12 @@ class ClickCounter extends owl.Component {
}
}
-const qweb = new owl.QWeb();
+const TEMPLATES = `
+ `;
+
+const qweb = new owl.QWeb(TEMPLATES);
const counter = new ClickCounter({ qweb });
counter.mount(document.body);
```
diff --git a/doc/component.md b/doc/component.md
index 2f65e86d..386213b9 100644
--- a/doc/component.md
+++ b/doc/component.md
@@ -30,7 +30,7 @@ OWL components are the building blocks for user interface. They are designed to
and follow the QWeb specification. This is a requirement for Odoo.
OWL components are defined as a subclass of Component. The rendering is
-exclusively done by a [QWeb](qweb.md) template (either defined inline or preloaded in QWeb).
+exclusively done by a [QWeb](qweb.md) template (which needs to be preloaded in QWeb).
Rendering a component generates a virtual dom representation
of the widget, which is then patched to the DOM, in order to apply the changes in an efficient way.
@@ -61,7 +61,7 @@ Note that this code is written in ESNext style, so it will only run on the
latest browsers without a transpilation step.
This example show how a component should be defined: it simply subclasses the
-Component class. If no `template` key (or `inlineTemplate`), is defined, then
+Component class. If no `template` key is defined, then
Owl will use the component's name as template name. Here,
a state object is defined. It is not mandatory to use the state object, but it
is certainly encouraged. The state object is [observed](observer.md), and any
@@ -112,7 +112,7 @@ between a valid css class added by the component, or some custom code, and a
class that need to be removed. This is why we only support the explicit syntax
with a class object:
-```js
+```xml
```
@@ -124,7 +124,7 @@ parent to its children. The environment needs to have a QWeb instance, which
will be used to render the component template.
Be aware that the name of the component may be significant: if a component does
-not define a `template` or `inlineTemplate` key, then Owl will lookup in QWeb to
+not define a `template` key, then Owl will lookup in QWeb to
find a template with the component name (or one of its ancestor).
### Properties
@@ -137,9 +137,6 @@ find a template with the component name (or one of its ancestor).
- **`template`** (string, optional): if given, this is the name of the QWeb template that will render
the component.
-- **`inlineTemplate`** (string, optional): a string that represents a xml template. If set,
- this will be loaded into QWeb and used instead of the `template` property.
-
- **`state`** (Object): this is the location of the component's state, if there is
any. After the willStart method, the `state` property is observed, and each
change will cause the widget to rerender itself.
diff --git a/src/component.ts b/src/component.ts
index ab2d3b8a..3614f015 100644
--- a/src/component.ts
+++ b/src/component.ts
@@ -43,7 +43,7 @@ export interface Meta {
mountedHandlers: { [key: number]: Function };
}
-// If a component does not define explicitely a template (or inlineTemplate)
+// If a component does not define explicitely a template
// key, it needs to find a template with its name (or a parent's). This is
// qweb dependant, so we need a place to store this information indexed by
// qweb instances.
@@ -61,7 +61,6 @@ export class Component<
> extends EventBus {
readonly __owl__: Meta;
template?: string;
- inlineTemplate?: string;
get el(): HTMLElement | null {
return this.__owl__.vnode ? (this).__owl__.vnode.elm : null;
@@ -388,41 +387,29 @@ export class Component<
const qweb = this.env.qweb;
if (!this.template) {
- if (this.inlineTemplate) {
- this.env.qweb.addTemplate(
- this.inlineTemplate,
- this.inlineTemplate,
- true
- );
-
- // we write on the proto, so any new component of this class will get
- // automatically the template key properly setup.
- (this).__proto__.template = this.inlineTemplate;
+ let tmap = TEMPLATE_MAP[qweb.id];
+ if (!tmap) {
+ tmap = {};
+ TEMPLATE_MAP[qweb.id] = tmap;
+ }
+ let p = (this).constructor;
+ let name: string = p.name;
+ let template = tmap[name];
+ if (template) {
+ this.template = template;
} else {
- let tmap = TEMPLATE_MAP[qweb.id];
- if (!tmap) {
- tmap = {};
- TEMPLATE_MAP[qweb.id] = tmap;
+ while (
+ (template = p.name) &&
+ !(template in qweb.templates) &&
+ p !== Component
+ ) {
+ p = p.__proto__;
}
- let p = (this).constructor;
- let name: string = p.name;
- let template = tmap[name];
- if (template) {
- this.template = template;
+ if (p === Component) {
+ this.template = "default";
} else {
- while (
- (template = p.name) &&
- !(template in qweb.templates) &&
- p !== Component
- ) {
- p = p.__proto__;
- }
- if (p === Component) {
- this.template = "default";
- } else {
- tmap[name] = template;
- this.template = template;
- }
+ tmap[name] = template;
+ this.template = template;
}
}
}
diff --git a/src/qweb_core.ts b/src/qweb_core.ts
index 3d65c736..11d63fe4 100644
--- a/src/qweb_core.ts
+++ b/src/qweb_core.ts
@@ -152,14 +152,7 @@ export class QWeb {
* Add a template to the internal template map. Note that it is not
* immediately compiled.
*/
- addTemplate(
- name: string,
- xmlString: string,
- allowDuplicates: boolean = false
- ) {
- if (name in this.templates && allowDuplicates) {
- return;
- }
+ addTemplate(name: string, xmlString: string) {
const doc = parseXML(xmlString);
if (!doc.firstChild) {
throw new Error("Invalid template (should not be empty)");
diff --git a/src/store.ts b/src/store.ts
index 78d98894..35d1e462 100644
--- a/src/store.ts
+++ b/src/store.ts
@@ -72,7 +72,7 @@ export class Store extends EventBus {
const func: (...any) => any = entry[1];
this.getters[name] = payload => {
return func({ state: this.state, getters: this.getters }, payload);
- }
+ };
}
}
@@ -154,6 +154,8 @@ interface EnvWithStore extends Env {
store: Store;
}
+let nextID = 1;
+
export function connect(mapStateToProps, options: any = {}) {
let hashFunction = options.hashFunction || null;
@@ -184,7 +186,7 @@ export function connect(mapStateToProps, options: any = {}) {
return function(
Comp: Constructor>
) {
- return class extends Comp {
+ const Result = class extends Comp {
constructor(parent, props?: any) {
const env = parent instanceof Component ? parent.env : parent;
const ownProps = Object.assign({}, props || {});
@@ -269,5 +271,13 @@ export function connect(mapStateToProps, options: any = {}) {
return super._updateProps(mergedProps, forceUpdate, patchQueue);
}
};
+
+ // we assign here a unique name to the resulting anonymous class.
+ // this is necessary for Owl to be able to properly deduce templates.
+ // Otherwise, all connected components would have the same name, and then
+ // each component after the first will necessarily have the same template.
+ let name = `ConnectedComponent${nextID++}`;
+ Object.defineProperty(Result, "name", { value: name });
+ return Result;
};
}
diff --git a/tests/component.test.ts b/tests/component.test.ts
index e84ffda8..cabb4713 100644
--- a/tests/component.test.ts
+++ b/tests/component.test.ts
@@ -24,11 +24,11 @@ beforeEach(() => {
fixture = makeTestFixture();
env = makeTestWEnv();
env.qweb.addTemplate(
- "counter",
+ "Counter",
`
`;
willPatch() {
steps.push("childchild:willPatch");
}
@@ -329,8 +342,9 @@ describe("lifecycle hooks", () => {
// the t-else part in the template is important. This is
// necessary to have a situation that could confuse the vdom
// patching algorithm
- class ParentWidget extends Widget {
- inlineTemplate = `
+ env.qweb.addTemplate(
+ "ParentWidget",
+ `
`;
widgets = { child: Child };
state = { flag: true };
}
- class Child extends Widget {
- inlineTemplate = "hey";
- }
+ env.qweb.addTemplate("Child", "hey");
+ class Child extends Widget {}
const widget = new ParentWidget(env);
await widget.mount(fixture);
@@ -1371,18 +1454,20 @@ describe("other directives with t-widget", () => {
});
test("t-else works with t-widget", async () => {
- class ParentWidget extends Widget {
- inlineTemplate = `
+ env.qweb.addTemplate(
+ "ParentWidget",
+ `
somediv
-
`;
+ `
+ );
+ class ParentWidget extends Widget {
widgets = { child: Child };
state = { flag: true };
}
- class Child extends Widget {
- inlineTemplate = "hey";
- }
+ env.qweb.addTemplate("Child", "hey");
+ class Child extends Widget {}
const widget = new ParentWidget(env);
await widget.mount(fixture);
@@ -1395,18 +1480,20 @@ describe("other directives with t-widget", () => {
});
test("t-elif works with t-widget", async () => {
- class ParentWidget extends Widget {
- inlineTemplate = `
+ env.qweb.addTemplate(
+ "ParentWidget",
+ `
somediv
-
`;
+ `
+ );
+ class ParentWidget extends Widget {
widgets = { child: Child };
state = { flag: true };
}
- class Child extends Widget {
- inlineTemplate = "hey";
- }
+ env.qweb.addTemplate("Child", "hey");
+ class Child extends Widget {}
const widget = new ParentWidget(env);
await widget.mount(fixture);
@@ -1419,18 +1506,20 @@ describe("other directives with t-widget", () => {
});
test("t-else with empty string works with t-widget", async () => {
- class ParentWidget extends Widget {
- inlineTemplate = `
+ env.qweb.addTemplate(
+ "ParentWidget",
+ `
somediv
-
`;
+ `
+ );
+ class ParentWidget extends Widget {
widgets = { child: Child };
state = { flag: true };
}
- class Child extends Widget {
- inlineTemplate = "hey";
- }
+ env.qweb.addTemplate("Child", "hey");
+ class Child extends Widget {}
const widget = new ParentWidget(env);
await widget.mount(fixture);
@@ -1448,8 +1537,11 @@ describe("random stuff/miscellaneous", () => {
// this test makes sure that the foreach directive does not pollute sub
// context with the inLoop variable, which is then used in the t-widget
// directive as a key
+ env.qweb.addTemplate(
+ "Test",
+ `
txt
`
+ );
class Test extends Widget {
- inlineTemplate = `
txt
`;
widgets = { widget: Widget };
}
const widget = new Test(env);
@@ -1461,15 +1553,20 @@ describe("random stuff/miscellaneous", () => {
// in this situation, we protect against a bug that occurred: because of the
// interplay between widgets and vnodes, a sub widget vnode was patched
// twice.
+ env.qweb.addTemplate(
+ "Parent",
+ `
`);
class Child extends Widget {
- inlineTemplate = `
`;
widgets = { SubChild };
mounted() {
// from now on, each rendering in child widget will be delayed (see
@@ -1879,9 +1986,12 @@ describe("async rendering", () => {
}
}
+ env.qweb.addTemplate(
+ "Parent",
+ `
+