mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
@@ -865,6 +865,19 @@ be considered the `default` slot. For example:
|
||||
</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
|
||||
|
||||
It is not common, but sometimes we need a dynamic component name. In this case,
|
||||
|
||||
@@ -194,7 +194,7 @@ QWeb.addDirective({
|
||||
QWeb.addDirective({
|
||||
name: "slot",
|
||||
priority: 80,
|
||||
atNodeEncounter({ ctx, value }): boolean {
|
||||
atNodeEncounter({ ctx, value, node, qweb }): boolean {
|
||||
const slotKey = ctx.generateID();
|
||||
ctx.addLine(
|
||||
`const slot${slotKey} = this.constructor.slots[context.__owl__.slotId + '_' + '${value}'];`
|
||||
@@ -214,6 +214,12 @@ QWeb.addDirective({
|
||||
if (!ctx.parentNode) {
|
||||
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();
|
||||
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`] = `
|
||||
"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`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
|
||||
@@ -4032,6 +4032,76 @@ describe("t-slot directive", () => {
|
||||
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 () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
|
||||
Reference in New Issue
Block a user