diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index ebe5f0e4..72068c59 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -217,24 +217,7 @@ function parseTNode(node: Element, ctx: ParsingContext): AST | null {
if (node.tagName !== "t") {
return null;
}
- const children: AST[] = [];
- for (let child of node.childNodes) {
- const ast = parseNode(child, ctx);
- if (ast) {
- children.push(ast);
- }
- }
- switch (children.length) {
- case 0:
- return null;
- case 1:
- return children[0];
- default:
- return {
- type: ASTType.Multi,
- content: children,
- };
- }
+ return parseChildNodes(node, ctx);
}
// -----------------------------------------------------------------------------
@@ -299,7 +282,6 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
return null;
}
ctx = Object.assign({}, ctx);
- const children: AST[] = [];
if (tagName === "pre") {
ctx.inPreTag = true;
}
@@ -309,12 +291,7 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
const ref = node.getAttribute("t-ref");
node.removeAttribute("t-ref");
- for (let child of node.childNodes) {
- const ast = parseNode(child, ctx);
- if (ast) {
- children.push(ast);
- }
- }
+ const children = parseChildren(node, ctx);
const nodeAttrsNames = node.getAttributeNames();
const attrs: ASTDomNode["attrs"] = {};
@@ -600,13 +577,7 @@ function parseTCall(node: Element, ctx: ParsingContext): AST | null {
};
}
}
- const body: AST[] = [];
- for (let child of node.childNodes) {
- const ast = parseNode(child, ctx);
- if (ast) {
- body.push(ast);
- }
- }
+ const body = parseChildren(node, ctx);
return {
type: ASTType.TCall,
@@ -687,13 +658,7 @@ function parseTSetNode(node: Element, ctx: ParsingContext): AST | null {
const defaultValue = node.innerHTML === node.textContent ? node.textContent || null : null;
let body: AST[] | null = null;
if (node.textContent !== node.innerHTML) {
- body = [];
- for (let child of node.childNodes) {
- let childAst = parseNode(child, ctx);
- if (childAst) {
- body.push(childAst);
- }
- }
+ body = parseChildren(node, ctx);
}
return { type: ASTType.TSet, name, value, defaultValue, body };
}
@@ -844,14 +809,30 @@ function parseTTranslation(node: Element, ctx: ParsingContext): AST | null {
// helpers
// -----------------------------------------------------------------------------
-function parseChildNodes(node: Element, ctx: ParsingContext): AST | null {
+/**
+ * Parse all the child nodes of a given node and return a list of ast elements
+ */
+function parseChildren(node: Node, ctx: ParsingContext): AST[] {
const children: AST[] = [];
for (let child of node.childNodes) {
const childAst = parseNode(child, ctx);
if (childAst) {
- children.push(childAst);
+ if (childAst.type === ASTType.Multi) {
+ children.push(...childAst.content);
+ } else {
+ children.push(childAst);
+ }
}
}
+ return children;
+}
+
+/**
+ * Parse all the child nodes of a given node and return an ast if possible.
+ * In the case there are multiple children, they are wrapped in a astmulti.
+ */
+function parseChildNodes(node: Node, ctx: ParsingContext): AST | null {
+ const children = parseChildren(node, ctx);
switch (children.length) {
case 0:
return null;
diff --git a/tests/blockdom/multi.test.ts b/tests/blockdom/multi.test.ts
index efd2c253..2ec3a694 100644
--- a/tests/blockdom/multi.test.ts
+++ b/tests/blockdom/multi.test.ts
@@ -76,4 +76,11 @@ describe("multi blocks", () => {
mount(text(multi([text("a"), text("b")]) as any), fixture);
expect(fixture.innerHTML).toBe("ab");
});
+
+ test("multi inside a block", async () => {
+ const block = createBlock("
");
+ const tree = block([], [multi([text("foo"), text("bar")])]);
+ mount(tree, fixture);
+ expect(fixture.innerHTML).toBe("foobar
");
+ });
});
diff --git a/tests/compiler/__snapshots__/simple_templates.test.ts.snap b/tests/compiler/__snapshots__/simple_templates.test.ts.snap
index e9115904..ef9ad4e2 100644
--- a/tests/compiler/__snapshots__/simple_templates.test.ts.snap
+++ b/tests/compiler/__snapshots__/simple_templates.test.ts.snap
@@ -308,6 +308,39 @@ exports[`simple templates, mostly static t-esc in dom node, variations 2`] = `
}"
`;
+exports[`simple templates, mostly static template with multiple t tag with multiple content 1`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
+
+ let block1 = createBlock(\`Loading
\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ let d1 = ctx['a'];
+ let d2 = ctx['b'];
+ let d3 = ctx['c'];
+ return block1([d1, d2, d3]);
+ }
+}"
+`;
+
+exports[`simple templates, mostly static template with t tag with multiple content 1`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
+
+ let block1 = createBlock(\`Loading
\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ let b2;
+ if (false) {
+ b2 = text(\`\`);
+ }
+ return block1([], [b2]);
+ }
+}"
+`;
+
exports[`simple templates, mostly static two t-escs next to each other 1`] = `
"function anonymous(bdom, helpers
) {
diff --git a/tests/compiler/parser.test.ts b/tests/compiler/parser.test.ts
index 366d044b..3eb9437e 100644
--- a/tests/compiler/parser.test.ts
+++ b/tests/compiler/parser.test.ts
@@ -149,6 +149,93 @@ describe("qweb parser", () => {
});
});
+ test("dom node with t multi inside", async () => {
+ const template = `Loading
`;
+ expect(parse(template)).toEqual({
+ type: ASTType.DomNode,
+ tag: "div",
+ dynamicTag: null,
+ attrs: {},
+ on: {},
+ ref: null,
+ model: null,
+ ns: null,
+ content: [
+ { type: ASTType.Text, value: "Loading" },
+ { type: ASTType.TEsc, expr: "abc", defaultValue: "" },
+ ],
+ });
+ });
+
+ test("dom node with multiple t multi inside", async () => {
+ const template = `
+
+
+
+
+ Loading
+
+
`;
+ expect(parse(template)).toEqual({
+ type: ASTType.DomNode,
+ tag: "div",
+ dynamicTag: null,
+ attrs: {},
+ on: {},
+ ref: null,
+ model: null,
+ ns: null,
+ content: [
+ { type: ASTType.TEsc, expr: "a", defaultValue: "" },
+ { type: ASTType.TEsc, expr: "b", defaultValue: "" },
+ { type: ASTType.Text, value: "Loading" },
+ { type: ASTType.TEsc, expr: "c", defaultValue: "" },
+ ],
+ });
+ });
+
+ test("dom node with t multi inside", async () => {
+ const template = `Loading
`;
+ expect(parse(template)).toEqual({
+ type: ASTType.DomNode,
+ tag: "div",
+ dynamicTag: null,
+ attrs: {},
+ on: {},
+ ref: null,
+ model: null,
+ ns: null,
+ content: [
+ { type: ASTType.Text, value: "Loading" },
+ { type: ASTType.TEsc, expr: "abc", defaultValue: "" },
+ ],
+ });
+ });
+
+ test("dom node with two t multi inside", async () => {
+ const template = `
+
+
+
+
`;
+ expect(parse(template)).toEqual({
+ type: ASTType.DomNode,
+ tag: "div",
+ dynamicTag: null,
+ attrs: {},
+ on: {},
+ ref: null,
+ model: null,
+ ns: null,
+ content: [
+ { type: ASTType.TEsc, expr: "a", defaultValue: "" },
+ { type: ASTType.TEsc, expr: "b", defaultValue: "" },
+ { type: ASTType.TEsc, expr: "c", defaultValue: "" },
+ { type: ASTType.TEsc, expr: "d", defaultValue: "" },
+ ],
+ });
+ });
+
test("dom node next to text node", async () => {
expect(parse("some text")).toEqual({
type: ASTType.Multi,
diff --git a/tests/compiler/simple_templates.test.ts b/tests/compiler/simple_templates.test.ts
index cb6cfd4e..3e62da8e 100644
--- a/tests/compiler/simple_templates.test.ts
+++ b/tests/compiler/simple_templates.test.ts
@@ -137,4 +137,21 @@ describe("simple templates, mostly static", () => {
const template = '';
expect(renderToString(template, { v: "from context" })).toBe("text from context");
});
+
+ test("template with t tag with multiple content", () => {
+ const template = `Loading
`;
+ expect(renderToString(template)).toBe("Loading
");
+ });
+
+ test("template with multiple t tag with multiple content", () => {
+ const template = `
+
+
+
+
+ Loading
+
+
`;
+ expect(renderToString(template, { a: "a", b: "b", c: "c" })).toBe("abLoadingc
");
+ });
});