diff --git a/src/component/component.ts b/src/component/component.ts index c876b77b..cc90440f 100644 --- a/src/component/component.ts +++ b/src/component/component.ts @@ -302,7 +302,14 @@ export class Component { const position = options.position || "last-child"; const __owl__ = this.__owl__; if (__owl__.isMounted) { - return Promise.resolve(); + if (position !== "self" && this.el!.parentNode !== target) { + // in this situation, we are trying to mount a component on a different + // target. In this case, we need to unmount first, otherwise it will + // not work. + this.unmount(); + } else { + return Promise.resolve(); + } } if (!(target instanceof HTMLElement || target instanceof DocumentFragment)) { let message = `Component '${this.constructor.name}' cannot be mounted: the target is not a valid DOM node.`; diff --git a/tests/component/un_mounting.test.ts b/tests/component/un_mounting.test.ts index ce45ec9e..9ac8b44d 100644 --- a/tests/component/un_mounting.test.ts +++ b/tests/component/un_mounting.test.ts @@ -376,4 +376,34 @@ describe("unmounting and remounting", () => { expect(fixture.innerHTML).toBe(""); expect(Child.prototype.__render).toBeCalledTimes(1); }); + + test("widget can be mounted on different target", async () => { + const steps: string[] = []; + class MyWidget extends Component { + static template = xml`
Hey
`; + async willStart() { + steps.push("willstart"); + } + mounted() { + steps.push("mounted"); + } + willUnmount() { + steps.push("willunmount"); + } + patched() { + throw new Error("patched should not be called"); + } + } + const div = document.createElement("div"); + const span = document.createElement("span"); + fixture.appendChild(div); + fixture.appendChild(span); + const w = new MyWidget(); + await w.mount(div); + + expect(fixture.innerHTML).toBe("
Hey
"); + + await w.mount(span); + expect(fixture.innerHTML).toBe("
Hey
"); + }); });