mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] templates: can load multiple at once and also from XMLDocument
This commit is contained in:
+12
-3
@@ -5,10 +5,10 @@ import { UTILS } from "./template_helpers";
|
||||
|
||||
const bdom = { text, createBlock, list, multi, html, toggler, component };
|
||||
|
||||
export const globalTemplates: { [key: string]: string } = {};
|
||||
export const globalTemplates: { [key: string]: string | Node } = {};
|
||||
|
||||
export class TemplateSet {
|
||||
rawTemplates: { [name: string]: string } = Object.create(globalTemplates);
|
||||
rawTemplates: typeof globalTemplates = Object.create(globalTemplates);
|
||||
templates: { [name: string]: Template } = {};
|
||||
translateFn?: (s: string) => string;
|
||||
translatableAttributes?: string[];
|
||||
@@ -25,13 +25,22 @@ export class TemplateSet {
|
||||
this.utils = Object.assign({}, UTILS, { getTemplate, call });
|
||||
}
|
||||
|
||||
addTemplate(name: string, template: string, options: { allowDuplicate?: boolean } = {}) {
|
||||
addTemplate(name: string, template: string | Node, options: { allowDuplicate?: boolean } = {}) {
|
||||
if (name in this.rawTemplates && !options.allowDuplicate) {
|
||||
throw new Error(`Template ${name} already defined`);
|
||||
}
|
||||
this.rawTemplates[name] = template;
|
||||
}
|
||||
|
||||
addTemplates(xml: string | Document, options: { allowDuplicate?: boolean } = {}) {
|
||||
xml = xml instanceof Document ? xml : new DOMParser().parseFromString(xml, "text/xml");
|
||||
for (const template of xml.querySelectorAll("[t-name]")) {
|
||||
const name = template.getAttribute("t-name")!;
|
||||
template.removeAttribute("t-name");
|
||||
this.addTemplate(name, template, options);
|
||||
}
|
||||
}
|
||||
|
||||
getTemplate(name: string): Template {
|
||||
if (!(name in this.templates)) {
|
||||
const rawTemplate = this.rawTemplates[name];
|
||||
|
||||
@@ -9,13 +9,17 @@ export type TemplateFunction = (blocks: any, utils: any) => Template;
|
||||
interface CompileOptions extends Config {
|
||||
name?: string;
|
||||
}
|
||||
export function compile(template: string, options: CompileOptions = {}): TemplateFunction {
|
||||
let nextId = 1;
|
||||
export function compile(template: string | Node, options: CompileOptions = {}): TemplateFunction {
|
||||
// parsing
|
||||
const ast = parse(template);
|
||||
|
||||
// some work
|
||||
const hasSafeContext = !template.includes("t-set") && !template.includes("t-call");
|
||||
const name = options.name || (template.length > 250 ? template.slice(0, 250) + "..." : template);
|
||||
const hasSafeContext =
|
||||
template instanceof Node
|
||||
? !(template instanceof Element) || template.querySelector("[t-set], [t-call]") === null
|
||||
: !template.includes("t-set") && !template.includes("t-call");
|
||||
const name = options.name || `template_${nextId++}`;
|
||||
|
||||
// code generation
|
||||
const codeGenerator = new CodeGenerator(name, ast, { ...options, hasSafeContext });
|
||||
|
||||
@@ -173,11 +173,10 @@ interface ParsingContext {
|
||||
inPreTag: boolean;
|
||||
}
|
||||
|
||||
export function parse(xml: string): AST {
|
||||
const template = `<t>${xml}</t>`;
|
||||
const doc = parseXML(template);
|
||||
export function parse(xml: string | Node): AST {
|
||||
const node = xml instanceof Element ? xml : parseXML(`<t>${xml}</t>`).firstChild!;
|
||||
const ctx = { inPreTag: false };
|
||||
const ast = parseNode(doc.firstChild!, ctx);
|
||||
const ast = parseNode(node, ctx);
|
||||
if (!ast) {
|
||||
return { type: ASTType.Text, value: "" };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`loading templates can initialize qweb with a string 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div>jupiler</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`loading templates can initialize qweb with an XMLDocument 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div>jupiler</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`loading templates can load a few templates from a xml string 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
|
||||
|
||||
let block2 = createBlock(\`<li>ok</li>\`);
|
||||
let block3 = createBlock(\`<li>foo</li>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2 = block2();
|
||||
let b3 = block3();
|
||||
return multi([b2, b3]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`loading templates can load a few templates from a xml string 2`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<ul/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`loading templates can load a few templates from an XMLDocument 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
|
||||
|
||||
let block2 = createBlock(\`<li>ok</li>\`);
|
||||
let block3 = createBlock(\`<li>foo</li>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2 = block2();
|
||||
let b3 = block3();
|
||||
return multi([b2, b3]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`loading templates can load a few templates from an XMLDocument 2`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<ul/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -138,51 +138,3 @@ describe("simple templates, mostly static", () => {
|
||||
expect(renderToString(template, { v: "from context" })).toBe("text from context");
|
||||
});
|
||||
});
|
||||
|
||||
describe("loading templates", () => {
|
||||
test.skip("can initialize qweb with a string", () => {
|
||||
/* const templates = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<div t-name="hey">jupiler</div>
|
||||
</templates>`;
|
||||
const qweb = new QWeb({ templates });
|
||||
expect(renderToString(qweb, "hey")).toBe("<div>jupiler</div>");*/
|
||||
});
|
||||
|
||||
test.skip("can load a few templates from a xml string", () => {
|
||||
/*const data = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="items"><li>ok</li><li>foo</li></t>
|
||||
|
||||
<ul t-name="main"><t t-call="items"/></ul>
|
||||
</templates>`;
|
||||
qweb.addTemplates(data);
|
||||
const result = renderToString(qweb, "main");
|
||||
expect(result).toBe("<ul><li>ok</li><li>foo</li></ul>");*/
|
||||
});
|
||||
|
||||
test.skip("does not crash if string does not have templates", () => {
|
||||
/*const data = "";
|
||||
qweb.addTemplates(data);
|
||||
expect(Object.keys(qweb.templates)).toEqual([]);*/
|
||||
});
|
||||
});
|
||||
|
||||
describe("global template registration", () => {
|
||||
test.skip("can register template globally", () => {
|
||||
// expect.assertions(5);
|
||||
// let qweb = new QWeb();
|
||||
// try {
|
||||
// qweb.render("mytemplate");
|
||||
// } catch (e) {
|
||||
// expect(e.message).toMatch("Template mytemplate does not exist");
|
||||
// }
|
||||
// expect(qweb.templates.mytemplate).toBeUndefined();
|
||||
// QWeb.registerTemplate("mytemplate", "<div>global</div>");
|
||||
// expect(qweb.templates.mytemplate).toBeDefined();
|
||||
// const vnode = qweb.render("mytemplate");
|
||||
// expect(vnode.sel).toBe("div");
|
||||
// expect((vnode as any).children[0].text).toBe("global");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { snapshotEverything, TestContext } from "../helpers";
|
||||
|
||||
snapshotEverything();
|
||||
|
||||
describe("loading templates", () => {
|
||||
test("can initialize qweb with a string", () => {
|
||||
const templates = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<div t-name="hey">jupiler</div>
|
||||
</templates>`;
|
||||
const context = new TestContext();
|
||||
context.addTemplates(templates);
|
||||
expect(context.renderToString("hey")).toBe("<div>jupiler</div>");
|
||||
});
|
||||
|
||||
test("can initialize qweb with an XMLDocument", () => {
|
||||
const data = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<div t-name="hey">jupiler</div>
|
||||
</templates>`;
|
||||
const xml = new DOMParser().parseFromString(data, "text/xml");
|
||||
const context = new TestContext();
|
||||
context.addTemplates(xml);
|
||||
expect(context.renderToString("hey")).toBe("<div>jupiler</div>");
|
||||
});
|
||||
|
||||
test("can load a few templates from a xml string", () => {
|
||||
const data = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="items"><li>ok</li><li>foo</li></t>
|
||||
|
||||
<ul t-name="main"><t t-call="items"/></ul>
|
||||
</templates>`;
|
||||
const context = new TestContext();
|
||||
context.addTemplates(data);
|
||||
const result = context.renderToString("main");
|
||||
expect(result).toBe("<ul><li>ok</li><li>foo</li></ul>");
|
||||
});
|
||||
|
||||
test("can load a few templates from an XMLDocument", () => {
|
||||
const data = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="items"><li>ok</li><li>foo</li></t>
|
||||
|
||||
<ul t-name="main"><t t-call="items"/></ul>
|
||||
</templates>`;
|
||||
const xml = new DOMParser().parseFromString(data, "text/xml");
|
||||
const context = new TestContext();
|
||||
context.addTemplates(xml);
|
||||
const result = context.renderToString("main");
|
||||
expect(result).toBe("<ul><li>ok</li><li>foo</li></ul>");
|
||||
});
|
||||
|
||||
test("does not crash if string does not have templates", () => {
|
||||
const data = "";
|
||||
const context = new TestContext();
|
||||
context.addTemplates(data);
|
||||
expect(Object.keys(context.rawTemplates)).toEqual([]);
|
||||
});
|
||||
|
||||
test("does not crash if XMLDocument does not have templates", () => {
|
||||
const data = "";
|
||||
const xml = new DOMParser().parseFromString(data, "text/xml");
|
||||
const context = new TestContext();
|
||||
context.addTemplates(xml);
|
||||
expect(Object.keys(context.rawTemplates)).toEqual([]);
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -38,7 +38,7 @@ export function makeTestFixture() {
|
||||
return fixture;
|
||||
}
|
||||
|
||||
export function snapshotTemplateCode(template: string, options?: CodeGenOptions) {
|
||||
export function snapshotTemplateCode(template: string | Node, options?: CodeGenOptions) {
|
||||
expect(compileTemplate(template, options).toString()).toMatchSnapshot();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user