[FIX] web: useChildRef: handle conditional children

The useChildRef (used with the useForwardRefToParent hook) allows
to retrieve, from a parent component, a t-ref from a child.

Consider the following scenario:
 - have a parent calling useChildRef
 - have a conditional (with t-if) child calling useForwardRefToParent
 - in the initial rendering, the child is displayed
  -> ref.el points to the correct html element
 - re-render s.t. the child is no longer displayed
  -> ref.el is null, because the referenced element doesn't exist
 - re-render s.t. the child is displayed again

Before this commit, at this point, ref.el will always be null,
even though the corresponding element comes back into the DOM. This
commit fixes the issue by correctly updating the ref, each time the
useForwardRefToParent hook is called.

closes odoo/odoo#100937

X-original-commit: 78d39c79993d1d63c189164f8104e5a3c90f2efd
Signed-off-by: Géry Debongnie <ged@odoo.com>
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Aaron Bohy
2022-09-23 08:26:01 +02:00
parent c82fdaa538
commit 668378b0f5
2 changed files with 67 additions and 2 deletions
+3 -1
View File
@@ -216,7 +216,9 @@ export function useService(serviceName) {
*/
export function useChildRef() {
let defined = false;
return function ref(value) {
let value;
return function ref(v) {
value = v;
if (defined) {
return;
}
@@ -1,7 +1,7 @@
/** @odoo-module **/
import { uiService } from "@web/core/ui/ui_service";
import { useAutofocus, useBus, useListener, useService } from "@web/core/utils/hooks";
import { useAutofocus, useBus, useChildRef, useForwardRefToParent, useListener, useService } from "@web/core/utils/hooks";
import { registry } from "@web/core/registry";
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
import {
@@ -423,5 +423,68 @@ QUnit.module("utils", () => {
assert.rejects(comp.functionService.call("boundThis"), "Component is destroyed");
assert.strictEqual(nbCalls, 8);
});
QUnit.module("useChildRef / useForwardRefToParent");
QUnit.test("simple usecase", async function (assert) {
let childRef;
let parentRef;
class Child extends Component {
setup() {
childRef = useForwardRefToParent("someRef");
}
}
Child.template = xml`<span t-ref="someRef" class="my_span">Hello</span>`;
class Parent extends Component {
setup() {
this.someRef = useChildRef();
parentRef = this.someRef;
}
}
Parent.template = xml`<div><Child someRef="someRef"/></div>`;
Parent.components = { Child };
const env = await makeTestEnv();
const target = getFixture();
await mount(Parent, target, { env });
assert.strictEqual(childRef.el, target.querySelector(".my_span"));
assert.strictEqual(parentRef.el, target.querySelector(".my_span"));
});
QUnit.test("useForwardRefToParent in a conditional child", async function (assert) {
class Child extends Component {
setup() {
useForwardRefToParent("someRef");
}
}
Child.template = xml`<span t-ref="someRef" class="my_span">Hello</span>`;
class Parent extends Component {
setup() {
this.someRef = useChildRef();
this.state = useState({ hasChild: true });
}
}
Parent.template = xml`<div><Child t-if="state.hasChild" someRef="someRef"/></div>`;
Parent.components = { Child };
const env = await makeTestEnv();
const target = getFixture();
const parent = await mount(Parent, target, { env });
assert.containsOnce(target, ".my_span");
assert.strictEqual(parent.someRef.el, target.querySelector(".my_span"));
parent.state.hasChild = false;
await nextTick();
assert.containsNone(target, ".my_span");
assert.strictEqual(parent.someRef.el, null);
parent.state.hasChild = true;
await nextTick();
assert.containsOnce(target, ".my_span");
assert.strictEqual(parent.someRef.el, target.querySelector(".my_span"));
});
});
});