mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
committed by
Géry Debongnie
parent
af4f372506
commit
7e721a96b5
@@ -290,6 +290,7 @@ class Parent extends Component {
|
|||||||
// here, if component is mounted, refs are active:
|
// here, if component is mounted, refs are active:
|
||||||
// - this.divRef.el is the div HTMLElement
|
// - this.divRef.el is the div HTMLElement
|
||||||
// - this.subRef.comp is the instance of the sub component
|
// - this.subRef.comp is the instance of the sub component
|
||||||
|
// - this.subRef.el is the root HTML node of the sub component (i.e. this.subRef.comp.el)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -297,8 +298,11 @@ class Parent extends Component {
|
|||||||
As shown by the example above, html elements are accessed by using the `el`
|
As shown by the example above, html elements are accessed by using the `el`
|
||||||
key, and components references are accessed with `comp`.
|
key, and components references are accessed with `comp`.
|
||||||
|
|
||||||
Note: if used on a component, the reference will be set in the `refs`
|
Notes:
|
||||||
variable between `willPatch` and `patched`.
|
|
||||||
|
- if used on a component, the reference will be set in the `refs`
|
||||||
|
variable between `willPatch` and `patched`,
|
||||||
|
- on a component, accessing `ref.el` will get the root node of the component.
|
||||||
|
|
||||||
The `t-ref` directive also accepts dynamic values with string interpolation
|
The `t-ref` directive also accepts dynamic values with string interpolation
|
||||||
(like the [`t-attf-`](qweb.md#dynamic-attributes) and
|
(like the [`t-attf-`](qweb.md#dynamic-attributes) and
|
||||||
|
|||||||
+6
-1
@@ -104,7 +104,12 @@ export function useRef(name: string): Ref {
|
|||||||
return {
|
return {
|
||||||
get el(): HTMLElement | null {
|
get el(): HTMLElement | null {
|
||||||
const val = __owl__.refs && __owl__.refs[name];
|
const val = __owl__.refs && __owl__.refs[name];
|
||||||
return val instanceof HTMLElement ? val : null;
|
if (val instanceof HTMLElement) {
|
||||||
|
return val;
|
||||||
|
} else if (val instanceof Component) {
|
||||||
|
return val.el;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
get comp(): Component<any, any> | null {
|
get comp(): Component<any, any> | null {
|
||||||
const val = __owl__.refs && __owl__.refs[name];
|
const val = __owl__.refs && __owl__.refs[name];
|
||||||
|
|||||||
@@ -1078,94 +1078,6 @@ describe("composition", () => {
|
|||||||
console.error = consoleError;
|
console.error = consoleError;
|
||||||
});
|
});
|
||||||
|
|
||||||
test("t-refs on widget are components", async () => {
|
|
||||||
class WidgetC extends Widget {
|
|
||||||
static template = xml`<div>Hello<WidgetB t-ref="mywidgetb" /></div>`;
|
|
||||||
static components = { WidgetB };
|
|
||||||
widget = useRef("mywidgetb");
|
|
||||||
}
|
|
||||||
|
|
||||||
const widget = new WidgetC();
|
|
||||||
await widget.mount(fixture);
|
|
||||||
expect(widget.widget.comp).toBeInstanceOf(WidgetB);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-refs are bound at proper timing", async () => {
|
|
||||||
expect.assertions(2);
|
|
||||||
class ParentWidget extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<t t-foreach="state.list" t-as="elem" t-ref="child" t-key="elem" t-component="Widget"/>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
static components = { Widget };
|
|
||||||
state = useState({ list: <any>[] });
|
|
||||||
child = useRef("child");
|
|
||||||
willPatch() {
|
|
||||||
expect(this.child.comp).toBeNull();
|
|
||||||
}
|
|
||||||
patched() {
|
|
||||||
expect(this.child.comp).not.toBeNull();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = new ParentWidget();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
parent.state.list.push(1);
|
|
||||||
await nextTick();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-refs are bound at proper timing (2)", async () => {
|
|
||||||
expect.assertions(10);
|
|
||||||
env.qweb.addTemplate(
|
|
||||||
"ParentWidget",
|
|
||||||
`
|
|
||||||
<div>
|
|
||||||
<t t-if="state.child1" t-ref="child1" t-component="Widget"/>
|
|
||||||
<t t-if="state.child2" t-ref="child2" t-component="Widget"/>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
class ParentWidget extends Widget {
|
|
||||||
static components = { Widget };
|
|
||||||
state = useState({ child1: true, child2: false });
|
|
||||||
child1 = useRef("child1");
|
|
||||||
child2 = useRef("child2");
|
|
||||||
count = 0;
|
|
||||||
mounted() {
|
|
||||||
expect(this.child1.comp).toBeDefined();
|
|
||||||
expect(this.child2.comp).toBeNull();
|
|
||||||
}
|
|
||||||
willPatch() {
|
|
||||||
if (this.count === 0) {
|
|
||||||
expect(this.child1.comp).toBeDefined();
|
|
||||||
expect(this.child2.comp).toBeNull();
|
|
||||||
}
|
|
||||||
if (this.count === 1) {
|
|
||||||
expect(this.child1.comp).toBeDefined();
|
|
||||||
expect(this.child2.comp).toBeDefined();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
patched() {
|
|
||||||
if (this.count === 0) {
|
|
||||||
expect(this.child1.comp).toBeDefined();
|
|
||||||
expect(this.child2.comp).toBeDefined();
|
|
||||||
}
|
|
||||||
if (this.count === 1) {
|
|
||||||
expect(this.child1.comp).toBeNull();
|
|
||||||
expect(this.child2.comp).toBeDefined();
|
|
||||||
}
|
|
||||||
this.count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = new ParentWidget();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
parent.state.child2 = true;
|
|
||||||
await nextTick();
|
|
||||||
parent.state.child1 = false;
|
|
||||||
await nextTick();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("modifying a sub widget", async () => {
|
test("modifying a sub widget", async () => {
|
||||||
env.qweb.addTemplate("ParentWidget", `<div><t t-component="Counter"/></div>`);
|
env.qweb.addTemplate("ParentWidget", `<div><t t-component="Counter"/></div>`);
|
||||||
class ParentWidget extends Widget {
|
class ParentWidget extends Widget {
|
||||||
|
|||||||
@@ -228,8 +228,11 @@ describe("hooks", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const counter = new Counter();
|
const counter = new Counter();
|
||||||
|
expect(counter.button.el).toBe(null);
|
||||||
await counter.mount(fixture);
|
await counter.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><button>0</button></div>");
|
expect(fixture.innerHTML).toBe("<div><button>0</button></div>");
|
||||||
|
expect(counter.button.el).not.toBe(null);
|
||||||
|
expect(counter.button.el).toBe(fixture.querySelector("button"));
|
||||||
counter.increment();
|
counter.increment();
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe("<div><button>1</button></div>");
|
expect(fixture.innerHTML).toBe("<div><button>1</button></div>");
|
||||||
@@ -256,6 +259,104 @@ describe("hooks", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div></div>");
|
expect(fixture.innerHTML).toBe("<div></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-refs on widget are components", async () => {
|
||||||
|
class WidgetB extends Component<any, any> {
|
||||||
|
static template = xml`<div>b</div>`;
|
||||||
|
}
|
||||||
|
class WidgetC extends Component<any, any> {
|
||||||
|
static template = xml`<div class="outer-div">Hello<WidgetB t-ref="mywidgetb" /></div>`;
|
||||||
|
static components = { WidgetB };
|
||||||
|
ref = useRef("mywidgetb");
|
||||||
|
}
|
||||||
|
|
||||||
|
const widget = new WidgetC();
|
||||||
|
expect(widget.ref.comp).toBe(null);
|
||||||
|
expect(widget.ref.el).toBe(null);
|
||||||
|
await widget.mount(fixture);
|
||||||
|
expect(widget.ref.comp).toBeInstanceOf(WidgetB);
|
||||||
|
expect(widget.ref.el).toEqual(fixture.querySelector(".outer-div > div"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-refs are bound at proper timing", async () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
class Widget extends Component<any, any> {
|
||||||
|
static template = xml`<div>widget</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParentWidget extends Component<any, any> {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-foreach="state.list" t-as="elem" t-ref="child" t-key="elem" t-component="Widget"/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
static components = { Widget };
|
||||||
|
state = useState({ list: <any>[] });
|
||||||
|
child = useRef("child");
|
||||||
|
willPatch() {
|
||||||
|
expect(this.child.comp).toBeNull();
|
||||||
|
}
|
||||||
|
patched() {
|
||||||
|
expect(this.child.comp).not.toBeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = new ParentWidget();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
parent.state.list.push(1);
|
||||||
|
await nextTick();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-refs are bound at proper timing (2)", async () => {
|
||||||
|
expect.assertions(10);
|
||||||
|
class Widget extends Component<any, any> {
|
||||||
|
static template = xml`<div>widget</div>`;
|
||||||
|
}
|
||||||
|
class ParentWidget extends Component<any, any> {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-if="state.child1" t-ref="child1" t-component="Widget"/>
|
||||||
|
<t t-if="state.child2" t-ref="child2" t-component="Widget"/>
|
||||||
|
</div>`;
|
||||||
|
static components = { Widget };
|
||||||
|
state = useState({ child1: true, child2: false });
|
||||||
|
child1 = useRef("child1");
|
||||||
|
child2 = useRef("child2");
|
||||||
|
count = 0;
|
||||||
|
mounted() {
|
||||||
|
expect(this.child1.comp).toBeDefined();
|
||||||
|
expect(this.child2.comp).toBeNull();
|
||||||
|
}
|
||||||
|
willPatch() {
|
||||||
|
if (this.count === 0) {
|
||||||
|
expect(this.child1.comp).toBeDefined();
|
||||||
|
expect(this.child2.comp).toBeNull();
|
||||||
|
}
|
||||||
|
if (this.count === 1) {
|
||||||
|
expect(this.child1.comp).toBeDefined();
|
||||||
|
expect(this.child2.comp).toBeDefined();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patched() {
|
||||||
|
if (this.count === 0) {
|
||||||
|
expect(this.child1.comp).toBeDefined();
|
||||||
|
expect(this.child2.comp).toBeDefined();
|
||||||
|
}
|
||||||
|
if (this.count === 1) {
|
||||||
|
expect(this.child1.comp).toBeNull();
|
||||||
|
expect(this.child2.comp).toBeDefined();
|
||||||
|
}
|
||||||
|
this.count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = new ParentWidget();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
parent.state.child2 = true;
|
||||||
|
await nextTick();
|
||||||
|
parent.state.child1 = false;
|
||||||
|
await nextTick();
|
||||||
|
});
|
||||||
|
|
||||||
test("can use onPatched, onWillPatch", async () => {
|
test("can use onPatched, onWillPatch", async () => {
|
||||||
const steps: string[] = [];
|
const steps: string[] = [];
|
||||||
function useMyHook() {
|
function useMyHook() {
|
||||||
|
|||||||
Reference in New Issue
Block a user