mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] compiler: allow to declare default slot scope on component
This commit is contained in:
committed by
Géry Debongnie
parent
c5e197ddc1
commit
9424312573
+20
-10
@@ -87,8 +87,8 @@ grandparent of the slot content).
|
|||||||
|
|
||||||
## Default Slot
|
## Default Slot
|
||||||
|
|
||||||
The first element inside the component which is not a named slot will
|
All elements inside the component which are not a named slot will be treated as
|
||||||
be considered the `default` slot. For example:
|
part of the content of the `default` slot. For example:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<div t-name="Parent">
|
<div t-name="Parent">
|
||||||
@@ -110,6 +110,7 @@ One can mix default slot and named slots:
|
|||||||
default content
|
default content
|
||||||
<t t-set-slot="footer">
|
<t t-set-slot="footer">
|
||||||
content for footer slot here
|
content for footer slot here
|
||||||
|
</t>
|
||||||
</Child>
|
</Child>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
@@ -211,28 +212,37 @@ use this `Notebook` component:
|
|||||||
|
|
||||||
## Slot scopes
|
## Slot scopes
|
||||||
|
|
||||||
For other kind of advanced use cases, the content of a slot may depends on some
|
For other kinds of advanced use cases, the content of a slot may depends on some
|
||||||
specific information specific to the generic component. This is the opposite
|
information specific to the generic component. This is the opposite of the slot
|
||||||
of the slot params.
|
params.
|
||||||
|
|
||||||
To solve this kind of problems, one can use the `t-slot-scope` directive along
|
To solve this kind of problems, one can use the `t-slot-scope` directive along
|
||||||
with the `t-set-slot`. This defines the name of a variable that can access
|
with the `t-set-slot`. This defines the name of a variable that can access
|
||||||
everything given by the child component:
|
everything given by the child component:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<div>
|
<MyComponent>
|
||||||
<t t-set-slot="foo" t-slot-scope="scope">
|
<t t-set-slot="foo" t-slot-scope="scope">
|
||||||
content
|
content
|
||||||
<t t-esc="scope.bool"/>
|
<t t-esc="scope.bool"/>
|
||||||
<t t-esc="scope.num"/>
|
<t t-esc="scope.num"/>
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</MyComponent>
|
||||||
```
|
```
|
||||||
|
|
||||||
And the child component that includes the slot can provide values like this:
|
And the child component that includes the slot can provide values like this:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<div>
|
<t t-slot="foo" bool="other_var" num="5">
|
||||||
<t t-slot="foo" bool="other_var" num="5">
|
```
|
||||||
</div>
|
|
||||||
|
In the case of the default slot, you may declare the slot scope directly on the
|
||||||
|
component itself:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<MyComponent t-slot-scope="scope">
|
||||||
|
content
|
||||||
|
<t t-esc="scope.bool"/>
|
||||||
|
<t t-esc="scope.num"/>
|
||||||
|
</MyComponent>
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -677,6 +677,9 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
const dynamicProps = node.getAttribute("t-props");
|
const dynamicProps = node.getAttribute("t-props");
|
||||||
node.removeAttribute("t-props");
|
node.removeAttribute("t-props");
|
||||||
|
|
||||||
|
const defaultSlotScope = node.getAttribute("t-slot-scope");
|
||||||
|
node.removeAttribute("t-slot-scope");
|
||||||
|
|
||||||
const props: ASTComponent["props"] = {};
|
const props: ASTComponent["props"] = {};
|
||||||
for (let name of node.getAttributeNames()) {
|
for (let name of node.getAttributeNames()) {
|
||||||
const value = node.getAttribute(name)!;
|
const value = node.getAttribute(name)!;
|
||||||
@@ -742,6 +745,9 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
const defaultContent = parseChildNodes(clone, ctx);
|
const defaultContent = parseChildNodes(clone, ctx);
|
||||||
if (defaultContent) {
|
if (defaultContent) {
|
||||||
slots.default = { content: defaultContent };
|
slots.default = { content: defaultContent };
|
||||||
|
if (defaultSlotScope) {
|
||||||
|
slots.default.scope = defaultSlotScope;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, slots };
|
return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, slots };
|
||||||
|
|||||||
@@ -424,6 +424,42 @@ exports[`slots default slot next to named slot, with default content 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`slots default slot with slot scope: shorthand syntax 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
function slot1(ctx, node, key = \\"\\") {
|
||||||
|
let b2,b3;
|
||||||
|
if (ctx['slotScope'].bool) {
|
||||||
|
b2 = text(\`some text\`);
|
||||||
|
} else {
|
||||||
|
b3 = text(\`other text\`);
|
||||||
|
}
|
||||||
|
return multi([b2, b3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`slots default slot with slot scope: shorthand syntax 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { callSlot } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2 = callSlot(ctx, node, key, 'default', false, {bool: ctx['state'].bool});
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`slots default slot work with text nodes (variation) 1`] = `
|
exports[`slots default slot work with text nodes (variation) 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -1137,44 +1173,6 @@ exports[`slots simple default slot 2`] = `
|
|||||||
|
|
||||||
exports[`slots simple default slot with params 1`] = `
|
exports[`slots simple default slot with params 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
|
||||||
let { capture } = helpers;
|
|
||||||
|
|
||||||
function slot1(ctx, node, key = \\"\\") {
|
|
||||||
let b2,b3;
|
|
||||||
if (ctx['slotScope'].bool) {
|
|
||||||
b2 = text(\`some text\`);
|
|
||||||
} else {
|
|
||||||
b3 = text(\`other text\`);
|
|
||||||
}
|
|
||||||
return multi([b2, b3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
const ctx1 = capture(ctx);
|
|
||||||
return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx1, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`slots simple default slot with params 2`] = `
|
|
||||||
"function anonymous(bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
|
||||||
let { callSlot } = helpers;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let b2 = callSlot(ctx, node, key, 'default', false, {bool: ctx['state'].bool});
|
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`slots simple default slot with params 3`] = `
|
|
||||||
"function anonymous(bdom, helpers
|
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
@@ -1194,7 +1192,7 @@ exports[`slots simple default slot with params 3`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`slots simple default slot with params 4`] = `
|
exports[`slots simple default slot with params 2`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
@@ -1236,6 +1234,44 @@ exports[`slots simple default slot, variation 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`slots simple slot with slot scope 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { capture } = helpers;
|
||||||
|
|
||||||
|
function slot1(ctx, node, key = \\"\\") {
|
||||||
|
let b2,b3;
|
||||||
|
if (ctx['slotScope'].bool) {
|
||||||
|
b2 = text(\`some text\`);
|
||||||
|
} else {
|
||||||
|
b3 = text(\`other text\`);
|
||||||
|
}
|
||||||
|
return multi([b2, b3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const ctx1 = capture(ctx);
|
||||||
|
return component(\`Child\`, {slots: {'slotName': {__render: slot1, __ctx: ctx1, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`slots simple slot with slot scope 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { callSlot } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2 = callSlot(ctx, node, key, 'slotName', false, {bool: ctx['state'].bool});
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`slots slot and (inline) t-call 1`] = `
|
exports[`slots slot and (inline) t-call 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -45,7 +45,36 @@ describe("slots", () => {
|
|||||||
expect(fixture.innerHTML).toBe("some text");
|
expect(fixture.innerHTML).toBe("some text");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("simple default slot with params", async () => {
|
test("simple slot with slot scope", async () => {
|
||||||
|
let child: any;
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<span><t t-slot="slotName" bool="state.bool"/></span>`;
|
||||||
|
state = useState({ bool: true });
|
||||||
|
setup() {
|
||||||
|
child = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<Child>
|
||||||
|
<t t-set-slot="slotName" t-slot-scope="slotScope">
|
||||||
|
<t t-if="slotScope.bool">some text</t>
|
||||||
|
<t t-else="slotScope.bool">other text</t>
|
||||||
|
</t>
|
||||||
|
</Child>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<span>some text</span>");
|
||||||
|
|
||||||
|
child.state.bool = false;
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<span>other text</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("default slot with slot scope: shorthand syntax", async () => {
|
||||||
let child: any;
|
let child: any;
|
||||||
class Child extends Component {
|
class Child extends Component {
|
||||||
static template = xml`<span><t t-slot="default" bool="state.bool"/></span>`;
|
static template = xml`<span><t t-slot="default" bool="state.bool"/></span>`;
|
||||||
@@ -57,11 +86,9 @@ describe("slots", () => {
|
|||||||
|
|
||||||
class Parent extends Component {
|
class Parent extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
<Child>
|
<Child t-slot-scope="slotScope">
|
||||||
<t t-set-slot="default" t-slot-scope="slotScope">
|
<t t-if="slotScope.bool">some text</t>
|
||||||
<t t-if="slotScope.bool">some text</t>
|
<t t-else="slotScope.bool">other text</t>
|
||||||
<t t-else="slotScope.bool">other text</t>
|
|
||||||
</t>
|
|
||||||
</Child>`;
|
</Child>`;
|
||||||
static components = { Child };
|
static components = { Child };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user