mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] component: call mounted hooks in subcomponent properly
order was not correct in some cases. Also, this commit is a pretty significant optimization.
This commit is contained in:
+13
-35
@@ -191,24 +191,6 @@ export class Component<
|
|||||||
// Public
|
// Public
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach a child widget to a given html element
|
|
||||||
*
|
|
||||||
* This is most of the time not necessary, since widgets should primarily be
|
|
||||||
* created/managed with the t-widget directive in a qweb template. However,
|
|
||||||
* for the cases where we need more control, this method will do what is
|
|
||||||
* necessary to make sure all the proper hooks are called (for example,
|
|
||||||
* mounted/willUnmount)
|
|
||||||
*
|
|
||||||
* Note that this method makes a few assumptions:
|
|
||||||
* - the child widget is indeed a child of the current widget
|
|
||||||
* - the target is inside the dom of the current widget (typically a ref)
|
|
||||||
*/
|
|
||||||
attachChild(child: Component<T, any, any>, target: HTMLElement) {
|
|
||||||
target.appendChild(child.el!);
|
|
||||||
child.__mount();
|
|
||||||
}
|
|
||||||
|
|
||||||
async mount(target: HTMLElement): Promise<void> {
|
async mount(target: HTMLElement): Promise<void> {
|
||||||
const vnode = await this._prepare();
|
const vnode = await this._prepare();
|
||||||
if (this.__owl__.isDestroyed) {
|
if (this.__owl__.isDestroyed) {
|
||||||
@@ -271,14 +253,14 @@ export class Component<
|
|||||||
destroy() {
|
destroy() {
|
||||||
if (!this.__owl__.isDestroyed) {
|
if (!this.__owl__.isDestroyed) {
|
||||||
const el = this.el;
|
const el = this.el;
|
||||||
this._destroy();
|
this._destroy(this.__owl__.parent);
|
||||||
if (el) {
|
if (el) {
|
||||||
el.remove();
|
el.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_destroy() {
|
_destroy(parent) {
|
||||||
const isMounted = this.__owl__.isMounted;
|
const isMounted = this.__owl__.isMounted;
|
||||||
if (isMounted) {
|
if (isMounted) {
|
||||||
this.willUnmount();
|
this.willUnmount();
|
||||||
@@ -286,11 +268,11 @@ export class Component<
|
|||||||
}
|
}
|
||||||
const children = Object.values(this.__owl__.children);
|
const children = Object.values(this.__owl__.children);
|
||||||
for (let child of children) {
|
for (let child of children) {
|
||||||
child._destroy();
|
child._destroy(this);
|
||||||
}
|
}
|
||||||
if (this.__owl__.parent) {
|
if (parent) {
|
||||||
let id = this.__owl__.id;
|
let id = this.__owl__.id;
|
||||||
delete this.__owl__.parent.__owl__.children[id];
|
delete parent.__owl__.children[id];
|
||||||
this.__owl__.parent = null;
|
this.__owl__.parent = null;
|
||||||
}
|
}
|
||||||
this.clear();
|
this.clear();
|
||||||
@@ -408,24 +390,20 @@ export class Component<
|
|||||||
*/
|
*/
|
||||||
_mount(vnode: VNode, elm: HTMLElement): VNode {
|
_mount(vnode: VNode, elm: HTMLElement): VNode {
|
||||||
this.__owl__.vnode = patch(elm, vnode);
|
this.__owl__.vnode = patch(elm, vnode);
|
||||||
this.__mount();
|
if (
|
||||||
|
this.__owl__.parent &&
|
||||||
|
this.__owl__.parent.__owl__.isMounted &&
|
||||||
|
!this.__owl__.isMounted
|
||||||
|
) {
|
||||||
|
this._callMounted();
|
||||||
|
}
|
||||||
return this.__owl__.vnode;
|
return this.__owl__.vnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
__mount() {
|
__mount() {
|
||||||
if (this.__owl__.isMounted) {
|
if (!this.__owl__.isMounted) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._observeState();
|
|
||||||
if (this.__owl__.parent) {
|
|
||||||
if (this.__owl__.parent!.__owl__.isMounted) {
|
|
||||||
this.__owl__.isMounted = true;
|
this.__owl__.isMounted = true;
|
||||||
this.mounted();
|
this.mounted();
|
||||||
const children = this.__owl__.children;
|
|
||||||
for (let id in children) {
|
|
||||||
children[id].__mount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -216,6 +216,54 @@ describe("lifecycle hooks", () => {
|
|||||||
expect(steps).toEqual(["child:mounted", "parent:mounted"]);
|
expect(steps).toEqual(["child:mounted", "parent:mounted"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("mounted hook is called on subsubwidgets, in proper order", async () => {
|
||||||
|
const steps: any[] = [];
|
||||||
|
|
||||||
|
class ParentWidget extends Widget {
|
||||||
|
inlineTemplate = `<div><t t-if="state.flag"><t t-widget="child"/></t></div>`;
|
||||||
|
widgets = { child: ChildWidget };
|
||||||
|
state = { flag: false };
|
||||||
|
mounted() {
|
||||||
|
steps.push("parent:mounted");
|
||||||
|
}
|
||||||
|
willUnmount() {
|
||||||
|
steps.push("parent:willUnmount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ChildWidget extends Widget {
|
||||||
|
inlineTemplate = `<div><t t-widget="childchild"/></div>`;
|
||||||
|
widgets = { childchild: ChildChildWidget };
|
||||||
|
mounted() {
|
||||||
|
steps.push("child:mounted");
|
||||||
|
}
|
||||||
|
willUnmount() {
|
||||||
|
steps.push("child:willUnmount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ChildChildWidget extends Widget {
|
||||||
|
mounted() {
|
||||||
|
steps.push("childchild:mounted");
|
||||||
|
}
|
||||||
|
willUnmount() {
|
||||||
|
steps.push("childchild:willUnmount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const widget = new ParentWidget(env);
|
||||||
|
await widget.mount(fixture);
|
||||||
|
expect(steps).toEqual(["parent:mounted"]);
|
||||||
|
widget.state.flag = true;
|
||||||
|
await nextTick();
|
||||||
|
widget.destroy();
|
||||||
|
expect(steps).toEqual([
|
||||||
|
"parent:mounted",
|
||||||
|
"childchild:mounted",
|
||||||
|
"child:mounted",
|
||||||
|
"parent:willUnmount",
|
||||||
|
"child:willUnmount",
|
||||||
|
"childchild:willUnmount"
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
test("willStart, mounted on subwidget rendered after main is mounted in some other position", async () => {
|
test("willStart, mounted on subwidget rendered after main is mounted in some other position", async () => {
|
||||||
const steps: string[] = [];
|
const steps: string[] = [];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user