From d8c37a29422823caa6ab3546361ad72bd6209796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Sun, 27 Jan 2019 21:50:22 +0100 Subject: [PATCH] handle situation when widget is destroyed before willStart --- web/static/src/ts/core/Widget.ts | 19 ++++++++++----- web/static/tests/core/widget.test.ts | 36 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/web/static/src/ts/core/Widget.ts b/web/static/src/ts/core/Widget.ts index 8de8df30..d9f20658 100644 --- a/web/static/src/ts/core/Widget.ts +++ b/web/static/src/ts/core/Widget.ts @@ -80,9 +80,13 @@ export class Widget { // Public //-------------------------------------------------------------------------- - async mount(target: HTMLElement): Promise { + async mount(target: HTMLElement): Promise { await this._start(); - const vnode = await this.render(); + await this.render(); + if (!this.el) { + // widget was destroyed before we get here... + return; + } target.appendChild(this.el!); if (document.body.contains(target)) { @@ -93,7 +97,6 @@ export class Widget { } }); } - return vnode; } destroy() { @@ -134,18 +137,22 @@ export class Widget { // Private //-------------------------------------------------------------------------- - async render(): Promise { + async render(): Promise { + if (this.__widget__.isDestroyed) { + return; + } const vnode = await this._render(); this.__widget__.vnode = patch( this.__widget__.vnode || document.createElement(vnode.sel!), vnode ); - return this.__widget__.vnode; } private async _start(): Promise { await this.willStart(); - this.__widget__.isStarted = true; + if (!this.__widget__.isDestroyed) { + this.__widget__.isStarted = true; + } } private async _render(): Promise { diff --git a/web/static/tests/core/widget.test.ts b/web/static/tests/core/widget.test.ts index d5c6082f..46596340 100644 --- a/web/static/tests/core/widget.test.ts +++ b/web/static/tests/core/widget.test.ts @@ -422,6 +422,42 @@ describe("destroy method", () => { expect(child.__widget__.parent).toBe(null); expect(children(parent).length).toBe(0); }); + + test("destroying a widget before willStart is done", async () => { + let resolve; + let isRendered = false; + let p: Promise = new Promise(function(r) { + resolve = r; + }); + class DelayedWidget extends Widget { + willStart() { + return p; + } + } + expect(fixture.innerHTML).toBe(""); + const widget = new DelayedWidget(env); + widget.mount(fixture); + expect(widget.__widget__.isStarted).toBe(false); + expect(widget.__widget__.isMounted).toBe(false); + expect(widget.__widget__.isDestroyed).toBe(false); + widget.destroy(); + expect(widget.__widget__.isMounted).toBe(false); + expect(widget.__widget__.isStarted).toBe(false); + expect(widget.__widget__.isDestroyed).toBe(true); + resolve(); + // Note: should we abandon await and not have to do this? + // TODO: talk to vsc + await nextTick(); + await nextTick(); + await nextTick(); + + expect(widget.__widget__.isStarted).toBe(false); + expect(widget.__widget__.isMounted).toBe(false); + expect(widget.__widget__.isDestroyed).toBe(true); + expect(widget.__widget__.vnode).toBe(null); + expect(fixture.innerHTML).toBe(""); + expect(isRendered).toBe(false); + }); }); describe("composition", () => {