mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] slots: prevent crash when using same slot in different locations
Before this commit, a crash could occur when a component with no props is defined in a slot, and that slot is conditionally displayed in multiple locations. The reason for that is that the key provided to the callSlot function was identical, so from the perspective of the component function, it was not possible to make the difference between a component located in either places. With this commit, we make sure that a unique key is used when a slot is reused in a template (or if it is dynamic, because in that case, we have no idea at compile time if it will be unique or not) closes #1246
This commit is contained in:
committed by
Sam Degueldre
parent
9cb74d619b
commit
3883cec079
@@ -1819,4 +1819,102 @@ describe("slots", () => {
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<button>inc</button>[B][C][A][sub1] [sub2334]");
|
||||
});
|
||||
|
||||
test("slot in multiple locations", async () => {
|
||||
class Child extends Component {
|
||||
static template = xml`<div>child</div>`;
|
||||
}
|
||||
|
||||
class Slotter extends Component {
|
||||
static components = { Child };
|
||||
static template = xml`
|
||||
<t t-if="props.location === 1">
|
||||
<p><t t-slot="default"/></p>
|
||||
</t>
|
||||
<t t-if="props.location === 2">
|
||||
<t t-slot="default"/>
|
||||
</t>
|
||||
`;
|
||||
}
|
||||
|
||||
class Parent extends Component {
|
||||
static components = { Child, Slotter };
|
||||
static template = xml`
|
||||
<Slotter location="state.location">
|
||||
hello <Child/>
|
||||
</Slotter>`;
|
||||
state = useState({ location: 1 });
|
||||
}
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<p> hello <div>child</div></p>");
|
||||
parent.state.location = 2;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(" hello <div>child</div>");
|
||||
});
|
||||
|
||||
test("dynamic slot in multiple locations", async () => {
|
||||
class Child extends Component {
|
||||
static template = xml`<div>child</div>`;
|
||||
}
|
||||
|
||||
class Slotter extends Component {
|
||||
static components = { Child };
|
||||
static template = xml`
|
||||
<t t-if="props.location === 1">
|
||||
<p><t t-slot="{{'coffee'}}"/></p>
|
||||
</t>
|
||||
<t t-if="props.location === 2">
|
||||
<t t-slot="{{'coffee'}}"/>
|
||||
</t>
|
||||
`;
|
||||
}
|
||||
|
||||
class Parent extends Component {
|
||||
static components = { Child, Slotter };
|
||||
static template = xml`
|
||||
<Slotter location="state.location">
|
||||
<t t-set-slot="coffee">hello <Child/></t>
|
||||
</Slotter>`;
|
||||
state = useState({ location: 1 });
|
||||
}
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<p>hello <div>child</div></p>");
|
||||
parent.state.location = 2;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("hello <div>child</div>");
|
||||
});
|
||||
|
||||
test("slot in t-foreach locations", async () => {
|
||||
class Child extends Component {
|
||||
static template = xml`<div>child</div>`;
|
||||
}
|
||||
|
||||
class Slotter extends Component {
|
||||
static components = { Child };
|
||||
static template = xml`
|
||||
<t t-foreach="props.list" t-as="elem" t-key="elem_index">
|
||||
<p><t t-esc="elem"/><t t-slot="default"/></p>
|
||||
</t>
|
||||
`;
|
||||
}
|
||||
|
||||
class Parent extends Component {
|
||||
static components = { Child, Slotter };
|
||||
static template = xml`
|
||||
<Slotter list="state.list">
|
||||
hello <Child/>
|
||||
</Slotter>`;
|
||||
state = useState({ list: [1] });
|
||||
}
|
||||
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<p>1 hello <div>child</div></p>");
|
||||
parent.state.list.push(2);
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<p>1 hello <div>child</div></p><p>2 hello <div>child</div></p>"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user