[IMP] components: slots can now define default content

closes #554
This commit is contained in:
Géry Debongnie
2019-12-10 15:03:14 +01:00
committed by aab-odoo
parent 9145799ae9
commit bc455be720
4 changed files with 124 additions and 1 deletions
+13
View File
@@ -865,6 +865,19 @@ be considered the `default` slot. For example:
</div> </div>
``` ```
Slots can define a default content, in case the parent did not define them:
```xml
<div t-name="Parent">
<Child/>
</div>
<span t-name="Child">
<t t-slot="default">default content</t>
</span>
<!-- will be rendered as: <div><span>default content</span></div> -->
```
### Dynamic sub components ### Dynamic sub components
It is not common, but sometimes we need a dynamic component name. In this case, It is not common, but sometimes we need a dynamic component name. In this case,
+7 -1
View File
@@ -194,7 +194,7 @@ QWeb.addDirective({
QWeb.addDirective({ QWeb.addDirective({
name: "slot", name: "slot",
priority: 80, priority: 80,
atNodeEncounter({ ctx, value }): boolean { atNodeEncounter({ ctx, value, node, qweb }): boolean {
const slotKey = ctx.generateID(); const slotKey = ctx.generateID();
ctx.addLine( ctx.addLine(
`const slot${slotKey} = this.constructor.slots[context.__owl__.slotId + '_' + '${value}'];` `const slot${slotKey} = this.constructor.slots[context.__owl__.slotId + '_' + '${value}'];`
@@ -214,6 +214,12 @@ QWeb.addDirective({
if (!ctx.parentNode) { if (!ctx.parentNode) {
ctx.addLine(`utils.defineProxy(result, ${parentNode}[0]);`); ctx.addLine(`utils.defineProxy(result, ${parentNode}[0]);`);
} }
if (node.hasChildNodes()) {
ctx.addElse();
const nodeCopy = <Element>node.cloneNode(true);
nodeCopy.removeAttribute("t-slot");
qweb._compileNode(nodeCopy, ctx);
}
ctx.closeIf(); ctx.closeIf();
return true; return true;
} }
@@ -1395,6 +1395,23 @@ exports[`t-slot directive content is the default slot 1`] = `
}" }"
`; `;
exports[`t-slot directive dafault slots can define a default content 1`] = `
"function anonymous(context, extra
) {
// Template name: \\"__template__1\\"
var h = this.h;
let c5 = [], p5 = {key:5};
var vn5 = h('span', p5, c5);
const slot6 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
if (slot6) {
slot6.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c5, parent: extra.parent || context}));
} else {
c5.push({text: \`default content\`});
}
return vn5;
}"
`;
exports[`t-slot directive default slot work with text nodes 1`] = ` exports[`t-slot directive default slot work with text nodes 1`] = `
"function anonymous(context, extra "function anonymous(context, extra
) { ) {
@@ -1439,6 +1456,23 @@ exports[`t-slot directive multiple roots are allowed in a named slot 1`] = `
}" }"
`; `;
exports[`t-slot directive named slots can define a default content 1`] = `
"function anonymous(context, extra
) {
// Template name: \\"__template__1\\"
var h = this.h;
let c5 = [], p5 = {key:5};
var vn5 = h('span', p5, c5);
const slot6 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
if (slot6) {
slot6.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c5, parent: extra.parent || context}));
} else {
c5.push({text: \`default content\`});
}
return vn5;
}"
`;
exports[`t-slot directive refs are properly bound in slots 1`] = ` exports[`t-slot directive refs are properly bound in slots 1`] = `
"function anonymous(context, extra "function anonymous(context, extra
) { ) {
+70
View File
@@ -4032,6 +4032,76 @@ describe("t-slot directive", () => {
expect(QWeb.slots["1_footer"].toString()).toMatchSnapshot(); expect(QWeb.slots["1_footer"].toString()).toMatchSnapshot();
}); });
test("named slots can define a default content", async () => {
class Dialog extends Component<any, any> {
static template = xml`
<span>
<t t-slot="header">default content</t>
</span>`;
}
class Parent extends Component<any, any> {
static template = xml`<div><Dialog/></div>`;
static components = { Dialog };
}
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>default content</span></div>");
expect(env.qweb.templates[Dialog.template].fn.toString()).toMatchSnapshot();
});
test("dafault slots can define a default content", async () => {
class Dialog extends Component<any, any> {
static template = xml`
<span>
<t t-slot="default">default content</t>
</span>`;
}
class Parent extends Component<any, any> {
static template = xml`<div><Dialog/></div>`;
static components = { Dialog };
}
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>default content</span></div>");
expect(env.qweb.templates[Dialog.template].fn.toString()).toMatchSnapshot();
});
test("default content is not rendered if slot is provided", async () => {
class Dialog extends Component<any, any> {
static template = xml`
<span>
<t t-slot="default">default content</t>
</span>`;
}
class Parent extends Component<any, any> {
static template = xml`<div><Dialog>hey</Dialog></div>`;
static components = { Dialog };
}
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>hey</span></div>");
});
test("default content is not rendered if named slot is provided", async () => {
class Dialog extends Component<any, any> {
static template = xml`
<span>
<t t-slot="header">default content</t>
</span>`;
}
class Parent extends Component<any, any> {
static template = xml`<div><Dialog><t t-set="header">hey</t></Dialog></div>`;
static components = { Dialog };
}
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>hey</span></div>");
});
test("slots are rendered with proper context", async () => { test("slots are rendered with proper context", async () => {
env.qweb.addTemplates(` env.qweb.addTemplates(`
<templates> <templates>