mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
add loadtemplates method to qweb
This commit is contained in:
@@ -8,7 +8,7 @@ import { VNode } from "../../../libs/snabbdom/src/vnode";
|
|||||||
export type EvalContext = { [key: string]: any };
|
export type EvalContext = { [key: string]: any };
|
||||||
export type RawTemplate = string;
|
export type RawTemplate = string;
|
||||||
export type CompiledTemplate<T> = (context: EvalContext, extra: any) => T;
|
export type CompiledTemplate<T> = (context: EvalContext, extra: any) => T;
|
||||||
type ParsedTemplate = Document;
|
type ProcessedTemplate = Element;
|
||||||
|
|
||||||
const RESERVED_WORDS = "true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,typeof,eval,void,Math,RegExp,Array,Object,Date".split(
|
const RESERVED_WORDS = "true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,typeof,eval,void,Math,RegExp,Array,Object,Date".split(
|
||||||
","
|
","
|
||||||
@@ -101,8 +101,7 @@ export class Context {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
export class QWeb {
|
export class QWeb {
|
||||||
rawTemplates: { [name: string]: RawTemplate } = {};
|
processedTemplates: { [name: string]: ProcessedTemplate } = {};
|
||||||
parsedTemplates: { [name: string]: ParsedTemplate } = {};
|
|
||||||
templates: { [name: string]: CompiledTemplate<VNode> } = {};
|
templates: { [name: string]: CompiledTemplate<VNode> } = {};
|
||||||
h = h;
|
h = h;
|
||||||
exprCache: { [key: string]: string } = {};
|
exprCache: { [key: string]: string } = {};
|
||||||
@@ -134,10 +133,9 @@ export class QWeb {
|
|||||||
* immediately compiled.
|
* immediately compiled.
|
||||||
*/
|
*/
|
||||||
addTemplate(name: string, template: RawTemplate) {
|
addTemplate(name: string, template: RawTemplate) {
|
||||||
if (name in this.rawTemplates) {
|
if (name in this.processedTemplates) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.rawTemplates[name] = template;
|
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(template, "text/xml");
|
const doc = parser.parseFromString(template, "text/xml");
|
||||||
if (!doc.firstChild) {
|
if (!doc.firstChild) {
|
||||||
@@ -146,7 +144,14 @@ export class QWeb {
|
|||||||
if (doc.getElementsByTagName("parsererror").length) {
|
if (doc.getElementsByTagName("parsererror").length) {
|
||||||
throw new Error("Invalid XML in template");
|
throw new Error("Invalid XML in template");
|
||||||
}
|
}
|
||||||
let tbranch = doc.querySelectorAll("[t-elif], [t-else]");
|
let elem = doc.firstChild as Element;
|
||||||
|
this._processTemplate(elem);
|
||||||
|
|
||||||
|
this.processedTemplates[name] = elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
_processTemplate(elem: Element) {
|
||||||
|
let tbranch = elem.querySelectorAll("[t-elif], [t-else]");
|
||||||
for (let i = 0, ilen = tbranch.length; i < ilen; i++) {
|
for (let i = 0, ilen = tbranch.length; i < ilen; i++) {
|
||||||
let node = tbranch[i];
|
let node = tbranch[i];
|
||||||
let prevElem = node.previousElementSibling!;
|
let prevElem = node.previousElementSibling!;
|
||||||
@@ -185,17 +190,24 @@ export class QWeb {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.parsedTemplates[name] = doc;
|
|
||||||
}
|
}
|
||||||
|
loadTemplates(xmlstr: string) {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(xmlstr, "text/xml");
|
||||||
|
const templates = doc.getElementsByTagName("templates")[0];
|
||||||
|
for (let elem of <any>templates.children) {
|
||||||
|
const name = elem.getAttribute("t-name");
|
||||||
|
this._processTemplate(elem);
|
||||||
|
this.processedTemplates[name] = elem;
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Render a template
|
* Render a template
|
||||||
*
|
*
|
||||||
* @param {string} name the template should already have been added
|
* @param {string} name the template should already have been added
|
||||||
*/
|
*/
|
||||||
render(name: string, context: EvalContext = {}, extra: any = null): VNode {
|
render(name: string, context: EvalContext = {}, extra: any = null): VNode {
|
||||||
if (!(name in this.rawTemplates)) {
|
if (!(name in this.processedTemplates)) {
|
||||||
throw new Error(`Template ${name} does not exist`);
|
throw new Error(`Template ${name} does not exist`);
|
||||||
}
|
}
|
||||||
const template = this.templates[name] || this._compile(name);
|
const template = this.templates[name] || this._compile(name);
|
||||||
@@ -207,9 +219,8 @@ export class QWeb {
|
|||||||
return this.templates[name];
|
return this.templates[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
const doc = this.parsedTemplates[name];
|
const mainNode = this.processedTemplates[name];
|
||||||
const ctx = new Context();
|
const ctx = new Context();
|
||||||
const mainNode = doc.firstChild!;
|
|
||||||
this._compileNode(mainNode, ctx);
|
this._compileNode(mainNode, ctx);
|
||||||
|
|
||||||
if (ctx.shouldProtectContext) {
|
if (ctx.shouldProtectContext) {
|
||||||
@@ -232,8 +243,9 @@ export class QWeb {
|
|||||||
) as CompiledTemplate<VNode>;
|
) as CompiledTemplate<VNode>;
|
||||||
if ((<Element>mainNode).attributes.hasOwnProperty("t-debug")) {
|
if ((<Element>mainNode).attributes.hasOwnProperty("t-debug")) {
|
||||||
console.log(
|
console.log(
|
||||||
`Template: ${this.rawTemplates[name]}\nCompiled code:\n` +
|
`Template: ${
|
||||||
template.toString()
|
this.processedTemplates[name].outerHTML
|
||||||
|
}\nCompiled code:\n` + template.toString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
template = template.bind(this);
|
template = template.bind(this);
|
||||||
@@ -619,8 +631,11 @@ const callDirective: Directive = {
|
|||||||
throw new Error("Invalid tag for t-call directive (should be 't')");
|
throw new Error("Invalid tag for t-call directive (should be 't')");
|
||||||
}
|
}
|
||||||
const subTemplate = node.getAttribute("t-call")!;
|
const subTemplate = node.getAttribute("t-call")!;
|
||||||
const nodeTemplate = qweb.parsedTemplates[subTemplate];
|
const nodeTemplate = qweb.processedTemplates[subTemplate];
|
||||||
const nodeCopy = <Element>node.cloneNode(true);
|
if (!nodeTemplate) {
|
||||||
|
throw new Error(`Cannot find template "${subTemplate}" (t-call)`);
|
||||||
|
}
|
||||||
|
const nodeCopy = node.cloneNode(true) as Element;
|
||||||
nodeCopy.removeAttribute("t-call");
|
nodeCopy.removeAttribute("t-call");
|
||||||
|
|
||||||
// extract variables from nodecopy
|
// extract variables from nodecopy
|
||||||
@@ -629,7 +644,7 @@ const callDirective: Directive = {
|
|||||||
const vars = Object.assign({}, ctx.variables, tempCtx.variables);
|
const vars = Object.assign({}, ctx.variables, tempCtx.variables);
|
||||||
const subCtx = ctx.withCaller(nodeCopy).withVariables(vars);
|
const subCtx = ctx.withCaller(nodeCopy).withVariables(vars);
|
||||||
|
|
||||||
qweb._compileNode(nodeTemplate.firstChild!, subCtx);
|
qweb._compileNode(nodeTemplate, subCtx);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -679,3 +679,19 @@ describe("t-ref", () => {
|
|||||||
expect(refs.myspan.tagName).toBe("SPAN");
|
expect(refs.myspan.tagName).toBe("SPAN");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("loading templates", () => {
|
||||||
|
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>`;
|
||||||
|
qweb.loadTemplates(data);
|
||||||
|
const result = renderToString(qweb, "main");
|
||||||
|
expect(result).toBe("<ul><li>ok</li><li>foo</li></ul>");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user