diff --git a/doc/reference/app.md b/doc/reference/app.md index 31a4d0591..14c7d04df 100644 --- a/doc/reference/app.md +++ b/doc/reference/app.md @@ -61,6 +61,8 @@ The `config` object is an object with some of the following keys: templates (see [translations](translations.md)) - **`templates (string | xml document)`**: all the templates that will be used by the components created by the application. +- **`getTemplate ((s: string) => Element | Function | string | void)`**: a function that will be called by owl when it + needs a template. If undefined is returned, owl looks into the app templates. - **`warnIfNoStaticProps (boolean, default=false)`**: if true, Owl will log a warning whenever it encounters a component that does not provide a [static props description](props.md#props-validation). diff --git a/src/runtime/template_set.ts b/src/runtime/template_set.ts index 5cee95867..cdb7e16b4 100644 --- a/src/runtime/template_set.ts +++ b/src/runtime/template_set.ts @@ -13,6 +13,7 @@ export interface TemplateSetConfig { translatableAttributes?: string[]; translateFn?: (s: string) => string; templates?: string | Document | Record; + getTemplate?: (s: string) => Element | Function | string | void; } export class TemplateSet { @@ -22,6 +23,7 @@ export class TemplateSet { dev: boolean; rawTemplates: typeof globalTemplates = Object.create(globalTemplates); templates: { [name: string]: Template } = {}; + getRawTemplate?: (s: string) => Element | Function | string | void; translateFn?: (s: string) => string; translatableAttributes?: string[]; Portal = Portal; @@ -39,6 +41,7 @@ export class TemplateSet { } } } + this.getRawTemplate = config.getTemplate; } addTemplate(name: string, template: string | Element) { @@ -77,7 +80,7 @@ export class TemplateSet { getTemplate(name: string): Template { if (!(name in this.templates)) { - const rawTemplate = this.rawTemplates[name]; + const rawTemplate = this.getRawTemplate?.(name) || this.rawTemplates[name]; if (rawTemplate === undefined) { let extraInfo = ""; try { diff --git a/tests/compiler/__snapshots__/template_set.test.ts.snap b/tests/compiler/__snapshots__/template_set.test.ts.snap index 370787a8c..396f6e268 100644 --- a/tests/compiler/__snapshots__/template_set.test.ts.snap +++ b/tests/compiler/__snapshots__/template_set.test.ts.snap @@ -101,3 +101,55 @@ exports[`loading templates can load a few templates from an XMLDocument 2`] = ` } }" `; + +exports[`loading templates getTemplate: element returned (2) 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`
Hello World!
\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + +exports[`loading templates getTemplate: element returned 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`
Hello World!
\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + +exports[`loading templates getTemplate: template string returned 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`
Hello World!
\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + +exports[`loading templates getTemplate: undefined returned 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`
Hello World!
\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; diff --git a/tests/compiler/template_set.test.ts b/tests/compiler/template_set.test.ts index 731cb7f36..273355660 100644 --- a/tests/compiler/template_set.test.ts +++ b/tests/compiler/template_set.test.ts @@ -78,4 +78,62 @@ describe("loading templates", () => { context.addTemplates(xml); expect(Object.keys(context.rawTemplates)).toEqual([]); }); + + test("getTemplate: element returned", () => { + const context = new TestContext({ + getTemplate: (name) => { + if (name === "main") { + const data = `
Hello World!
`; + const xml = new DOMParser().parseFromString(data, "text/xml"); + return xml.firstChild as Element; + } + return; + }, + }); + const result = context.renderToString("main"); + expect(result).toBe("
Hello World!
"); + }); + + test("getTemplate: element returned (2)", () => { + const context = new TestContext({ + getTemplate: (name) => { + if (name === "main") { + const doc = new Document(); + const div = doc.createElement("div"); + div.append(doc.createTextNode("Hello World!")); + return div; + } + return; + }, + }); + const result = context.renderToString("main"); + expect(result).toBe("
Hello World!
"); + }); + + test("getTemplate: template string returned", () => { + const context = new TestContext({ + getTemplate: (name) => { + if (name === "main") { + return `
Hello World!
`; + } + return; + }, + }); + const result = context.renderToString("main"); + expect(result).toBe("
Hello World!
"); + }); + + test("getTemplate: undefined returned", () => { + const context = new TestContext({ + getTemplate: () => {}, + }); + const data = ` + +
Hello World!
+
`; + const xml = new DOMParser().parseFromString(data, "text/xml"); + context.addTemplates(xml); + const result = context.renderToString("main"); + expect(result).toBe("
Hello World!
"); + }); });