diff --git a/src/component/component.ts b/src/component/component.ts index 24cb1a351..dabf88970 100644 --- a/src/component/component.ts +++ b/src/component/component.ts @@ -44,6 +44,15 @@ interface MountOptions { position?: MountPosition; } +export const enum STATUS { + CREATED, + WILLSTARTED, // willstart has been called + RENDERED, // first render is completed (so, vnode is now defined) + MOUNTED, // is ready, and in DOM. It has a valid el + UNMOUNTED, // has a valid el, but is not in DOM + DESTROYED, +} + /** * This is mostly an internal detail of implementation. The Meta interface is * useful to typecheck and describe the internal keys used by Owl to manage the @@ -56,8 +65,7 @@ interface Internal { depth: number; vnode: VNode | null; pvnode: VNode | null; - isMounted: boolean; - isDestroyed: boolean; + status: STATUS; // parent and children keys are obviously useful to setup the parent-children // relationship. @@ -164,16 +172,18 @@ export class Component { this.env.browser = browser; } this.env.qweb.on("update", this, () => { - if (this.__owl__.isMounted) { - this.render(true); - } - if (this.__owl__.isDestroyed) { - // this is unlikely to happen, but if a root widget is destroyed, - // we want to remove our subscription. The usual way to do that - // would be to perform some check in the destroy method, but since - // it is very performance sensitive, and since this is a rare event, - // we simply do it lazily - this.env.qweb.off("update", this); + switch (this.__owl__.status) { + case STATUS.MOUNTED: + this.render(true); + break; + case STATUS.DESTROYED: + // this is unlikely to happen, but if a root widget is destroyed, + // we want to remove our subscription. The usual way to do that + // would be to perform some check in the destroy method, but since + // it is very performance sensitive, and since this is a rare event, + // we simply do it lazily + this.env.qweb.off("update", this); + break; } }); depth = 0; @@ -186,8 +196,7 @@ export class Component { depth: depth, vnode: null, pvnode: null, - isMounted: false, - isDestroyed: false, + status: STATUS.CREATED, parent: parent || null, children: {}, cmap: {}, @@ -317,49 +326,49 @@ export class Component { * Note that a component can be mounted an unmounted several times */ async mount(target: HTMLElement | DocumentFragment, options: MountOptions = {}): Promise { + if (!(target instanceof HTMLElement || target instanceof DocumentFragment)) { + let message = `Component '${this.constructor.name}' cannot be mounted: the target is not a valid DOM node.`; + message += `\nMaybe the DOM is not ready yet? (in that case, you can use owl.utils.whenReady)`; + throw new Error(message); + } const position = options.position || "last-child"; const __owl__ = this.__owl__; - if (__owl__.isMounted) { - 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(); + const currentFiber = __owl__.currentFiber; + + switch (__owl__.status) { + case STATUS.CREATED: { + const fiber = new Fiber(null, this, true, target, position); + fiber.shouldPatch = false; + this.__prepareAndRender(fiber, () => {}); + return scheduler.addFiber(fiber); } - } - if (__owl__.isDestroyed) { - throw new Error("Cannot mount a destroyed component"); - } - if (__owl__.currentFiber) { - const currentFiber = __owl__.currentFiber; - if (!currentFiber.target && !currentFiber.position) { - // this means we have a pending rendering, but it was a render operation, - // not a mount operation. We can simply update the fiber with the target - // and the position + case STATUS.WILLSTARTED: + case STATUS.RENDERED: currentFiber.target = target; currentFiber.position = position; return scheduler.addFiber(currentFiber); - } else if (currentFiber.target === target && currentFiber.position === position) { - return scheduler.addFiber(currentFiber); - } else { - scheduler.rejectFiber(currentFiber, "Mounting operation cancelled"); + + case STATUS.UNMOUNTED: { + const fiber = new Fiber(null, this, true, target, position); + fiber.shouldPatch = false; + this.__render(fiber); + return scheduler.addFiber(fiber); } + + case STATUS.MOUNTED: { + if (position !== "self" && this.el!.parentNode !== target) { + const fiber = new Fiber(null, this, true, target, position); + fiber.shouldPatch = false; + this.__render(fiber); + return scheduler.addFiber(fiber); + } else { + return Promise.resolve(); + } + } + + case STATUS.DESTROYED: + throw new Error("Cannot mount a destroyed component"); } - if (!(target instanceof HTMLElement || target instanceof DocumentFragment)) { - let message = `Component '${this.constructor.name}' cannot be mounted: the target is not a valid DOM node.`; - message += `\nMaybe the DOM is not ready yet? (in that case, you can use owl.utils.whenReady)`; - throw new Error(message); - } - const fiber = new Fiber(null, this, true, target, position); - fiber.shouldPatch = false; - if (!__owl__.vnode) { - this.__prepareAndRender(fiber, () => {}); - } else { - this.__render(fiber); - } - return scheduler.addFiber(fiber); } /** @@ -367,7 +376,7 @@ export class Component { * to call willUnmount calls and remove the component from the DOM. */ unmount() { - if (this.__owl__.isMounted) { + if (this.__owl__.status === STATUS.MOUNTED) { this.__callWillUnmount(); this.el!.remove(); } @@ -394,11 +403,11 @@ export class Component { // if we aren't mounted at this point, it implies that there is a // currentFiber that is already rendered (isRendered is true), so we are // about to be mounted - const isMounted = __owl__.isMounted; + const status = __owl__.status; const fiber = new Fiber(null, this, force, null, null); Promise.resolve().then(() => { - if (__owl__.isMounted || !isMounted) { - if (fiber.isCompleted) { + if (__owl__.status === STATUS.MOUNTED || status !== STATUS.MOUNTED) { + if (fiber.isCompleted || fiber.isRendered) { return; } this.__render(fiber); @@ -424,7 +433,7 @@ export class Component { */ destroy() { const __owl__ = this.__owl__; - if (!__owl__.isDestroyed) { + if (__owl__.status !== STATUS.DESTROYED) { const el = this.el; this.__destroy(__owl__.parent); if (el) { @@ -469,13 +478,12 @@ export class Component { */ __destroy(parent: Component | null) { const __owl__ = this.__owl__; - const isMounted = __owl__.isMounted; - if (isMounted) { + if (__owl__.status === STATUS.MOUNTED) { if (__owl__.willUnmountCB) { __owl__.willUnmountCB(); } this.willUnmount(); - __owl__.isMounted = false; + __owl__.status = STATUS.UNMOUNTED; } const children = __owl__.children; for (let key in children) { @@ -486,7 +494,7 @@ export class Component { delete parent.__owl__.children[id]; __owl__.parent = null; } - __owl__.isDestroyed = true; + __owl__.status = STATUS.DESTROYED; delete __owl__.vnode; if (__owl__.currentFiber) { __owl__.currentFiber.isCompleted = true; @@ -496,7 +504,7 @@ export class Component { __callMounted() { const __owl__ = this.__owl__; - __owl__.isMounted = true; + __owl__.status = STATUS.MOUNTED; __owl__.currentFiber = null; this.mounted(); if (__owl__.mountedCB) { @@ -510,7 +518,7 @@ export class Component { __owl__.willUnmountCB(); } this.willUnmount(); - __owl__.isMounted = false; + __owl__.status = STATUS.UNMOUNTED; if (__owl__.currentFiber) { __owl__.currentFiber.isCompleted = true; __owl__.currentFiber.root.counter = 0; @@ -518,7 +526,7 @@ export class Component { const children = __owl__.children; for (let id in children) { const comp = children[id]; - if (comp.__owl__.isMounted) { + if (comp.__owl__.status === STATUS.MOUNTED) { comp.__callWillUnmount(); } } @@ -639,18 +647,25 @@ export class Component { } return p._template; } + async __prepareAndRender(fiber: Fiber, cb: CallableFunction) { try { - await Promise.all([this.willStart(), this.__owl__.willStartCB && this.__owl__.willStartCB()]); + const proms = Promise.all([ + this.willStart(), + this.__owl__.willStartCB && this.__owl__.willStartCB(), + ]); + this.__owl__.status = STATUS.WILLSTARTED; + await proms; + if (this.__owl__.status === STATUS.DESTROYED) { + return Promise.resolve(); + } } catch (e) { fiber.handleError(e); return Promise.resolve(); } - if (this.__owl__.isDestroyed) { - return Promise.resolve(); - } if (!fiber.isCompleted) { this.__render(fiber); + this.__owl__.status = STATUS.RENDERED; cb(); } } @@ -673,7 +688,7 @@ export class Component { for (let childKey in __owl__.children) { const child = __owl__.children[childKey]; const childOwl = child.__owl__; - if (!childOwl.isMounted && childOwl.parentLastFiberId < fiber.id) { + if (childOwl.status !== STATUS.MOUNTED && childOwl.parentLastFiberId < fiber.id) { // we only do here a "soft" destroy, meaning that we leave the child // dom node alone, without removing it. Most of the time, it does not // matter, because the child component is already unmounted. However, @@ -720,17 +735,6 @@ export class Component { } } - /** - * Only called by qweb t-component directive (when t-keepalive is set) - */ - __remount() { - const __owl__ = this.__owl__; - if (!__owl__.isMounted) { - __owl__.isMounted = true; - this.mounted(); - } - } - /** * Apply default props (only top level). * diff --git a/src/component/directive.ts b/src/component/directive.ts index 7d8e7fb72..ff4c28ac0 100644 --- a/src/component/directive.ts +++ b/src/component/directive.ts @@ -1,6 +1,7 @@ import { QWeb } from "../qweb/index"; import { INTERP_REGEXP } from "../qweb/compilation_context"; import { makeHandlerCode, MODS_CODE } from "../qweb/extensions"; +import { STATUS } from "./component"; //------------------------------------------------------------------------------ // t-component @@ -361,7 +362,7 @@ QWeb.addDirective({ // need to update component let styleCode = ""; if (tattStyle) { - styleCode = `.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};w${componentID}.el.style=${tattStyle};});`; + styleCode = `.then(()=>{if (w${componentID}.__owl__.status === ${STATUS.DESTROYED}) {return};w${componentID}.el.style=${tattStyle};});`; } ctx.addLine( `w${componentID}.__updateProps(props${componentID}, extra.fiber, ${scope})${styleCode};` diff --git a/src/component/fiber.ts b/src/component/fiber.ts index 9433f95ff..c6b47bdc3 100644 --- a/src/component/fiber.ts +++ b/src/component/fiber.ts @@ -1,5 +1,5 @@ import { h, VNode } from "../vdom/index"; -import { Component, MountPosition } from "./component"; +import { Component, MountPosition, STATUS } from "./component"; import { scheduler } from "./scheduler"; /** @@ -107,6 +107,8 @@ export class Fiber { */ _reuseFiber(oldFiber: Fiber) { oldFiber.cancel(); // cancel children fibers + oldFiber.target = this.target || oldFiber.target; + oldFiber.position = this.position || oldFiber.position; oldFiber.isCompleted = false; // keep the root fiber alive oldFiber.isRendered = false; // the fiber has to be re-rendered if (oldFiber.child) { @@ -188,8 +190,8 @@ export class Fiber { complete() { let component = this.component; this.isCompleted = true; - const { isMounted, isDestroyed } = component.__owl__; - if (isDestroyed) { + const status = component.__owl__.status; + if (status === STATUS.DESTROYED) { return; } @@ -203,7 +205,7 @@ export class Fiber { const patchLen = patchQueue.length; // call willPatch hook on each fiber of patchQueue - if (isMounted) { + if (status === STATUS.MOUNTED) { for (let i = 0; i < patchLen; i++) { const fiber = patchQueue[i]; if (fiber.shouldPatch) { @@ -253,8 +255,9 @@ export class Fiber { component.__owl__.pvnode!.elm = component.__owl__.vnode!.elm; } } - if (fiber === component.__owl__.currentFiber) { - component.__owl__.currentFiber = null; + const compOwl = component.__owl__; + if (fiber === compOwl.currentFiber) { + compOwl.currentFiber = null; } } @@ -274,7 +277,7 @@ export class Fiber { } // call patched/mounted hook on each fiber of (reversed) patchQueue - if (isMounted || inDOM) { + if (status === STATUS.MOUNTED || inDOM) { for (let i = patchLen - 1; i >= 0; i--) { const fiber = patchQueue[i]; component = fiber.component; @@ -287,6 +290,12 @@ export class Fiber { component.__callMounted(); } } + } else { + for (let i = patchLen - 1; i >= 0; i--) { + const fiber = patchQueue[i]; + component = fiber.component; + component.__owl__.status = STATUS.UNMOUNTED; + } } } diff --git a/src/qweb/extensions.ts b/src/qweb/extensions.ts index cea02edb8..dc0f4fc5a 100644 --- a/src/qweb/extensions.ts +++ b/src/qweb/extensions.ts @@ -1,3 +1,4 @@ +import { STATUS } from "../component/component"; import { VNode } from "../vdom/index"; import { INTERP_REGEXP } from "./compilation_context"; import { QWeb } from "./qweb"; @@ -76,7 +77,7 @@ export function makeHandlerCode( code = ctx.captureExpression(value); } const modCode = mods.map((mod) => modcodes[mod]).join(""); - let handler = `function (e) {if (context.__owl__.isDestroyed){return}${modCode}${code}}`; + let handler = `function (e) {if (context.__owl__.status === ${STATUS.DESTROYED}){return}${modCode}${code}}`; if (putInCache) { const key = ctx.generateTemplateKey(event); ctx.addLine(`extra.handlers[${key}] = extra.handlers[${key}] || ${handler};`); diff --git a/tests/component/__snapshots__/class_style.test.ts.snap b/tests/component/__snapshots__/class_style.test.ts.snap index 619db63b4..7aa536462 100644 --- a/tests/component/__snapshots__/class_style.test.ts.snap +++ b/tests/component/__snapshots__/class_style.test.ts.snap @@ -20,7 +20,7 @@ exports[`class and style attributes with t-component dynamic t-att-style is prop w2 = false; } if (w2) { - w2.__updateProps(props2, extra.fiber, undefined).then(()=>{if (w2.__owl__.isDestroyed) {return};w2.el.style=_4;});; + w2.__updateProps(props2, extra.fiber, undefined).then(()=>{if (w2.__owl__.status === 5) {return};w2.el.style=_4;});; let pvnode = w2.__owl__.pvnode; c1.push(pvnode); } else { diff --git a/tests/component/__snapshots__/component.test.ts.snap b/tests/component/__snapshots__/component.test.ts.snap index 7eee873e9..a78b1ced5 100644 --- a/tests/component/__snapshots__/component.test.ts.snap +++ b/tests/component/__snapshots__/component.test.ts.snap @@ -444,7 +444,7 @@ exports[`composition t-ref on a node, and t-on-click 2`] = ` if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap['__3__'] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('click', function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['doSomething'](e);});}});}); + let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('click', function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['doSomething'](e);});}});}); let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -579,7 +579,7 @@ exports[`other directives with t-component t-on expression captured in t-foreach c6.push(vn7); const otherState_8 = scope['otherState']; const iter_8 = scope.iter; - p7.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}otherState_8.vals.push(iter_8+'_'+iter_8)}; + p7.on['click'] = function (e) {if (context.__owl__.status === 5){return}otherState_8.vals.push(iter_8+'_'+iter_8)}; c7.push({text: \`expr\`}); utils.getScope(scope, 'iter').iter = scope.iter+1; } @@ -630,7 +630,7 @@ exports[`other directives with t-component t-on expression in t-foreach 1`] = ` c6.push(vn9); const otherState_10 = scope['otherState']; const val_10 = scope['val']; - p9.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}otherState_10.vals.push(val_10)}; + p9.on['click'] = function (e) {if (context.__owl__.status === 5){return}otherState_10.vals.push(val_10)}; c9.push({text: \`Expr\`}); } scope = _origScope5; @@ -684,7 +684,7 @@ exports[`other directives with t-component t-on expression in t-foreach with t-s const otherState_10 = scope['otherState']; const val_10 = scope['val']; const bossa_10 = scope.bossa; - p9.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}otherState_10.vals.push(val_10+'_'+bossa_10)}; + p9.on['click'] = function (e) {if (context.__owl__.status === 5){return}otherState_10.vals.push(val_10+'_'+bossa_10)}; c9.push({text: \`Expr\`}); } scope = _origScope5; @@ -734,7 +734,7 @@ exports[`other directives with t-component t-on method call in t-foreach 1`] = ` let vn9 = h('button', p9, c9); c6.push(vn9); let args10 = [scope['val']]; - p9.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['addVal'](...args10, e);}; + p9.on['click'] = function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['addVal'](...args10, e);}; c9.push({text: \`meth call\`}); } scope = _origScope5; @@ -770,7 +770,7 @@ exports[`other directives with t-component t-on with .capture modifier 1`] = ` if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap['__3__'] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('click', function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['capture'](e);}, true);}});}); + let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('click', function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['capture'](e);}, true);}});}); let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -812,7 +812,7 @@ exports[`other directives with t-component t-on with getter as handler 1`] = ` if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} w3 = new W3(parent, props3); parent.__owl__.cmap['__4__'] = w3.__owl__.id; - let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['handler'](e);});}});}); + let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['handler'](e);});}});}); let pvnode = h('dummy', {key: '__4__', hook: {remove() {},destroy(vn) {w3.destroy();}}}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; @@ -851,7 +851,7 @@ exports[`other directives with t-component t-on with handler bound to argument 1 if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap['__3__'] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['onEv'](...args4, e);});}});}); + let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['onEv'](...args4, e);});}});}); let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -890,7 +890,7 @@ exports[`other directives with t-component t-on with handler bound to empty obje if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap['__3__'] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['onEv'](...args4, e);});}});}); + let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['onEv'](...args4, e);});}});}); let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -929,7 +929,7 @@ exports[`other directives with t-component t-on with handler bound to empty obje if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap['__3__'] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['onEv'](...args4, e);});}});}); + let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['onEv'](...args4, e);});}});}); let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -968,7 +968,7 @@ exports[`other directives with t-component t-on with handler bound to object 1`] if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap['__3__'] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['onEv'](...args4, e);});}});}); + let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['onEv'](...args4, e);});}});}); let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -1011,7 +1011,7 @@ exports[`other directives with t-component t-on with inline statement 1`] = ` if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} w3 = new W3(parent, props3); parent.__owl__.cmap['__4__'] = w3.__owl__.id; - let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.isDestroyed){return}state_5.counter++});}});}); + let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}state_5.counter++});}});}); let pvnode = h('dummy', {key: '__4__', hook: {remove() {},destroy(vn) {w3.destroy();}}}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; @@ -1049,7 +1049,7 @@ exports[`other directives with t-component t-on with no handler (only modifiers) if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap['__3__'] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['onEv'](e);});}});}); + let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['onEv'](e);});}});}); let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -1087,7 +1087,7 @@ exports[`other directives with t-component t-on with prevent and self modifiers if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap['__3__'] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.isDestroyed){return}e.preventDefault();if (e.target !== vn.elm) {return}utils.getComponent(context)['onEv'](e);});}});}); + let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}e.preventDefault();if (e.target !== vn.elm) {return}utils.getComponent(context)['onEv'](e);});}});}); let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -1125,7 +1125,7 @@ exports[`other directives with t-component t-on with self and prevent modifiers if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap['__3__'] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.isDestroyed){return}if (e.target !== vn.elm) {return}e.preventDefault();utils.getComponent(context)['onEv'](e);});}});}); + let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}if (e.target !== vn.elm) {return}e.preventDefault();utils.getComponent(context)['onEv'](e);});}});}); let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -1163,7 +1163,7 @@ exports[`other directives with t-component t-on with self modifier 1`] = ` if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap['__3__'] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['onEv1'](e);});vn.elm.addEventListener('ev-2', function (e) {if (context.__owl__.isDestroyed){return}if (e.target !== vn.elm) {return}utils.getComponent(context)['onEv2'](e);});}});}); + let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['onEv1'](e);});vn.elm.addEventListener('ev-2', function (e) {if (context.__owl__.status === 5){return}if (e.target !== vn.elm) {return}utils.getComponent(context)['onEv2'](e);});}});}); let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -1201,7 +1201,7 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap['__3__'] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {if (context.__owl__.isDestroyed){return}e.stopPropagation();utils.getComponent(context)['onEv1'](e);});vn.elm.addEventListener('ev-2', function (e) {if (context.__owl__.isDestroyed){return}e.preventDefault();utils.getComponent(context)['onEv2'](e);});vn.elm.addEventListener('ev-3', function (e) {if (context.__owl__.isDestroyed){return}e.stopPropagation();e.preventDefault();utils.getComponent(context)['onEv3'](e);});}});}); + let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {if (context.__owl__.status === 5){return}e.stopPropagation();utils.getComponent(context)['onEv1'](e);});vn.elm.addEventListener('ev-2', function (e) {if (context.__owl__.status === 5){return}e.preventDefault();utils.getComponent(context)['onEv2'](e);});vn.elm.addEventListener('ev-3', function (e) {if (context.__owl__.status === 5){return}e.stopPropagation();e.preventDefault();utils.getComponent(context)['onEv3'](e);});}});}); let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -1555,7 +1555,7 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')} w6 = new W6(parent, props6); parent.__owl__.cmap[k7] = w6.__owl__.id; - let fiber = w6.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['onEv'](...args8, e);});}});}); + let fiber = w6.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['onEv'](...args8, e);});}});}); let pvnode = h('dummy', {key: k7, hook: {remove() {},destroy(vn) {w6.destroy();}}}); c1.push(pvnode); w6.__owl__.pvnode = pvnode; @@ -1580,7 +1580,7 @@ exports[`t-call handlers are properly bound through a t-call 1`] = ` let vn3 = h('p', p3, c3); c2.push(vn3); let k4 = \`click__4__\${key0}__\`; - extra.handlers[k4] = extra.handlers[k4] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['update'](e);}; + extra.handlers[k4] = extra.handlers[k4] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['update'](e);}; p3.on['click'] = extra.handlers[k4]; c3.push({text: \`lucas\`}); }" @@ -1599,7 +1599,7 @@ exports[`t-call handlers with arguments are properly bound through a t-call 1`] let vn3 = h('p', p3, c3); c2.push(vn3); let args4 = [scope['a']]; - p3.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['update'](...args4, e);}; + p3.on['click'] = function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['update'](...args4, e);}; c3.push({text: \`lucas\`}); }" `; diff --git a/tests/component/__snapshots__/slots.test.ts.snap b/tests/component/__snapshots__/slots.test.ts.snap index 118ac0f20..0ab51d8a4 100644 --- a/tests/component/__snapshots__/slots.test.ts.snap +++ b/tests/component/__snapshots__/slots.test.ts.snap @@ -229,7 +229,7 @@ exports[`t-slot directive dynamic t-slot call 1`] = ` let h = this.h; let c10 = [], p10 = {key:10,on:{}}; let vn10 = h('button', p10, c10); - extra.handlers['click__11__'] = extra.handlers['click__11__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['toggle'](e);}; + extra.handlers['click__11__'] = extra.handlers['click__11__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['toggle'](e);}; p10.on['click'] = extra.handlers['click__11__']; const slot12 = this.constructor.slots[context.__owl__.slotId + '_' + (scope['current'].slot)]; if (slot12) { @@ -301,7 +301,7 @@ exports[`t-slot directive refs are properly bound in slots 1`] = ` let c9 = [], p9 = {key:9,on:{}}; let vn9 = h('button', p9, c9); c8.push(vn9); - extra.handlers['click__10__'] = extra.handlers['click__10__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['doSomething'](e);}; + extra.handlers['click__10__'] = extra.handlers['click__10__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['doSomething'](e);}; p9.on['click'] = extra.handlers['click__10__']; const ref11 = \`myButton\`; p9.hook = { @@ -326,7 +326,7 @@ exports[`t-slot directive slots are rendered with proper context 1`] = ` let c9 = [], p9 = {key:9,on:{}}; let vn9 = h('button', p9, c9); c8.push(vn9); - extra.handlers['click__10__'] = extra.handlers['click__10__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['doSomething'](e);}; + extra.handlers['click__10__'] = extra.handlers['click__10__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['doSomething'](e);}; p9.on['click'] = extra.handlers['click__10__']; c9.push({text: \`do something\`}); }" diff --git a/tests/component/async.test.ts b/tests/component/async.test.ts index 469f0eeb6..1425c5580 100644 --- a/tests/component/async.test.ts +++ b/tests/component/async.test.ts @@ -1,4 +1,4 @@ -import { Component, Env } from "../../src/component/component"; +import { Component, Env, STATUS } from "../../src/component/component"; import { useState } from "../../src/hooks"; import { xml } from "../../src/tags"; import { makeDeferred, makeTestEnv, makeTestFixture, nextMicroTick, nextTick } from "../helpers"; @@ -40,14 +40,14 @@ describe("async rendering", () => { } } const w = new W(); + expect(w.__owl__.status).toBe(STATUS.CREATED); w.mount(fixture); - expect(w.__owl__.isDestroyed).toBe(false); - expect(w.__owl__.isMounted).toBe(false); + expect(w.__owl__.status).toBe(STATUS.WILLSTARTED); w.destroy(); + expect(w.__owl__.status).toBe(STATUS.DESTROYED); def.resolve(); await nextTick(); - expect(w.__owl__.isDestroyed).toBe(true); - expect(w.__owl__.isMounted).toBe(false); + expect(w.__owl__.status).toBe(STATUS.DESTROYED); }); test("destroying/recreating a subwidget with different props (if start is not over)", async () => { diff --git a/tests/component/component.test.ts b/tests/component/component.test.ts index af2ade973..e493ee203 100644 --- a/tests/component/component.test.ts +++ b/tests/component/component.test.ts @@ -1,4 +1,4 @@ -import { Component, Env, mount } from "../../src/component/component"; +import { Component, Env, mount, STATUS } from "../../src/component/component"; import { EventBus } from "../../src/core/event_bus"; import { useRef, useState } from "../../src/hooks"; import { QWeb } from "../../src/qweb/qweb"; @@ -1035,8 +1035,7 @@ describe("destroy method", () => { expect(document.contains(widget.el)).toBe(true); widget.destroy(); expect(document.contains(widget.el)).toBe(false); - expect(widget.__owl__.isMounted).toBe(false); - expect(widget.__owl__.isDestroyed).toBe(true); + expect(widget.__owl__.status).toBe(STATUS.DESTROYED); }); test("destroying a parent also destroys its children", async () => { @@ -1045,9 +1044,9 @@ describe("destroy method", () => { const child = children(parent)[0]; - expect(child.__owl__.isDestroyed).toBe(false); + expect(child.__owl__.status).toBe(STATUS.MOUNTED); parent.destroy(); - expect(child.__owl__.isDestroyed).toBe(true); + expect(child.__owl__.status).toBe(STATUS.DESTROYED); }); test("destroy remove the parent/children link", async () => { @@ -1073,17 +1072,15 @@ describe("destroy method", () => { } expect(fixture.innerHTML).toBe(""); const widget = new DelayedWidget(); + expect(widget.__owl__.status).toBe(STATUS.CREATED); widget.mount(fixture); - expect(widget.__owl__.isMounted).toBe(false); - expect(widget.__owl__.isDestroyed).toBe(false); + expect(widget.__owl__.status).toBe(STATUS.WILLSTARTED); widget.destroy(); - expect(widget.__owl__.isMounted).toBe(false); - expect(widget.__owl__.isDestroyed).toBe(true); + expect(widget.__owl__.status).toBe(STATUS.DESTROYED); def.resolve(); await nextTick(); - expect(widget.__owl__.isMounted).toBe(false); - expect(widget.__owl__.isDestroyed).toBe(true); + expect(widget.__owl__.status).toBe(STATUS.DESTROYED); expect(widget.__owl__.vnode).toBe(undefined); expect(fixture.innerHTML).toBe(""); expect(isRendered).toBe(false); @@ -1539,7 +1536,7 @@ describe("composition", () => { parent.state.flag = true; await nextTick(); expect(children(parent)[0]).toBe(child); - expect(child.__owl__.isDestroyed).toBe(false); + expect(child.__owl__.status).toBe(STATUS.MOUNTED); expect(normalize(fixture.innerHTML)).toBe( normalize(`
@@ -2287,7 +2284,7 @@ describe("other directives with t-component", () => { el.click(); expect(steps).toEqual(["click"]); parent.unmount(); - expect(child.__owl__.isMounted).toBe(false); + expect(child.__owl__.status).toBe(STATUS.UNMOUNTED); el.click(); expect(steps).toEqual(["click", "click"]); }); @@ -2361,7 +2358,7 @@ describe("other directives with t-component", () => { expect(steps).toEqual(["click"]); parent.state.flag = false; await nextTick(); - expect(child.__owl__.isDestroyed).toBe(true); + expect(child.__owl__.status).toBe(STATUS.DESTROYED); el.click(); expect(steps).toEqual(["click"]); }); @@ -2396,7 +2393,7 @@ describe("other directives with t-component", () => { expect(steps).toEqual(["click"]); parent.state.flag = false; await nextTick(); - expect(child.__owl__.isDestroyed).toBe(true); + expect(child.__owl__.status).toBe(STATUS.DESTROYED); el.click(); expect(steps).toEqual(["click"]); }); diff --git a/tests/component/error_handling.test.ts b/tests/component/error_handling.test.ts index bfae7bf8a..191c939f6 100644 --- a/tests/component/error_handling.test.ts +++ b/tests/component/error_handling.test.ts @@ -1,4 +1,4 @@ -import { Component, Env } from "../../src/component/component"; +import { Component, Env, STATUS } from "../../src/component/component"; import { useState } from "../../src/hooks"; import { xml } from "../../src/tags"; import { makeTestEnv, makeTestFixture, nextTick } from "../helpers"; @@ -108,7 +108,7 @@ describe("component error handling (catchError)", () => { expect(console.error).toBeCalledTimes(0); console.error = consoleError; - expect(app.__owl__.isDestroyed).toBe(true); + expect(app.__owl__.status).toBe(STATUS.DESTROYED); expect(handler).toBeCalledTimes(1); }); diff --git a/tests/component/un_mounting.test.ts b/tests/component/un_mounting.test.ts index da399a8da..1ad872a50 100644 --- a/tests/component/un_mounting.test.ts +++ b/tests/component/un_mounting.test.ts @@ -404,6 +404,40 @@ describe("unmounting and remounting", () => { expect(fixture.innerHTML).toBe("
P2C2
"); }); + test("change state while component is mounted in a fragment", async () => { + class Child1 extends Component { + static template = xml`C1`; + } + + class Child2 extends Component { + static template = xml`C2`; + } + + class Parent extends Component { + static components = { Child1, Child2 }; + static template = xml` +
+ + +
`; + child: string | false = false; + } + + const fragment = document.createDocumentFragment(); + const parent = new Parent(); + await parent.mount(fragment); + expect(parent.el.outerHTML).toBe("
"); + + parent.child = "c1"; + parent.render(); + await Promise.resolve(); + + parent.child = "c2"; + await parent.mount(fixture); + + expect(fixture.innerHTML).toBe("
C2
"); + }); + test("unmount component during a re-rendering", async () => { const def = makeDeferred(); class Child extends Component { @@ -490,17 +524,17 @@ describe("unmounting and remounting", () => { // one full tick. await nextMicroTick(); await nextMicroTick(); - expect(steps).toEqual(["1 catch"]); + expect(steps).toEqual([]); await nextTick(); expect(fixture.innerHTML).toBe("
"); def.resolve(); await nextTick(); - expect(steps).toEqual(["1 catch", "2 resolved"]); + expect(steps).toEqual(["2 resolved"]); expect(fixture.innerHTML).toBe("
Hey
"); }); - test("widget can be mounted on same target, another situation", async () => { + test("component can be mounted on same target, another situation", async () => { const def = makeDeferred(); const steps: string[] = []; @@ -528,8 +562,8 @@ describe("unmounting and remounting", () => { def.resolve(); await nextTick(); - expect(steps).toEqual(["1 resolved", "2 resolved"]); expect(fixture.innerHTML).toBe("
Hey
"); + expect(steps).toEqual(["1 resolved", "2 resolved"]); }); test("mounting a destroyed widget", async () => { diff --git a/tests/helpers.ts b/tests/helpers.ts index d93d643e9..c569329df 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -1,4 +1,4 @@ -import { Env, Component } from "../src/component/component"; +import { Env, Component, STATUS } from "../src/component/component"; import { scheduler } from "../src/component/scheduler"; import { EvalContext, QWeb } from "../src/qweb/qweb"; import { CompilationContext } from "../src/qweb/compilation_context"; @@ -92,7 +92,7 @@ export function renderToDOM( if (!context.__owl__) { // we add `__owl__` to better simulate a component as context. This is // particularly important for event handlers added with the `t-on` directive. - context.__owl__ = { isMounted: true }; + context.__owl__ = { status: STATUS.MOUNTED }; } const vnode = qweb.render(template, context, extra); diff --git a/tests/qweb/__snapshots__/qweb.test.ts.snap b/tests/qweb/__snapshots__/qweb.test.ts.snap index 7616d16c6..31c60ae92 100644 --- a/tests/qweb/__snapshots__/qweb.test.ts.snap +++ b/tests/qweb/__snapshots__/qweb.test.ts.snap @@ -2727,7 +2727,7 @@ exports[`t-on can bind event handler 1`] = ` let h = this.h; let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - extra.handlers['click__2__'] = extra.handlers['click__2__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['add'](e);}; + extra.handlers['click__2__'] = extra.handlers['click__2__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['add'](e);}; p1.on['click'] = extra.handlers['click__2__']; c1.push({text: \`Click\`}); return vn1; @@ -2744,7 +2744,7 @@ exports[`t-on can bind handlers with arguments 1`] = ` let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); let args2 = [5]; - p1.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['add'](...args2, e);}; + p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['add'](...args2, e);}; c1.push({text: \`Click\`}); return vn1; }" @@ -2760,7 +2760,7 @@ exports[`t-on can bind handlers with empty object (with non empty inner string) let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); let args2 = [{}]; - p1.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['doSomething'](...args2, e);}; + p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['doSomething'](...args2, e);}; c1.push({text: \`Click\`}); return vn1; }" @@ -2776,7 +2776,7 @@ exports[`t-on can bind handlers with empty object 1`] = ` let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); let args2 = [{}]; - p1.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['doSomething'](...args2, e);}; + p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['doSomething'](...args2, e);}; c1.push({text: \`Click\`}); return vn1; }" @@ -2815,7 +2815,7 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = ` let vn7 = h('a', p7, c7); c6.push(vn7); let args8 = [scope['action']]; - p7.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['activate'](...args8, e);}; + p7.on['click'] = function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['activate'](...args8, e);}; c7.push({text: \`link\`}); } scope = _origScope5; @@ -2833,7 +2833,7 @@ exports[`t-on can bind handlers with object arguments 1`] = ` let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); let args2 = [{val:5}]; - p1.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['add'](...args2, e);}; + p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['add'](...args2, e);}; c1.push({text: \`Click\`}); return vn1; }" @@ -2847,9 +2847,9 @@ exports[`t-on can bind two event handlers 1`] = ` let h = this.h; let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - extra.handlers['click__2__'] = extra.handlers['click__2__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['handleClick'](e);}; + extra.handlers['click__2__'] = extra.handlers['click__2__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['handleClick'](e);}; p1.on['click'] = extra.handlers['click__2__']; - extra.handlers['dblclick__3__'] = extra.handlers['dblclick__3__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['handleDblClick'](e);}; + extra.handlers['dblclick__3__'] = extra.handlers['dblclick__3__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['handleDblClick'](e);}; p1.on['dblclick'] = extra.handlers['dblclick__3__']; c1.push({text: \`Click\`}); return vn1; @@ -2864,7 +2864,7 @@ exports[`t-on handler is bound to proper owner 1`] = ` let h = this.h; let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); - extra.handlers['click__2__'] = extra.handlers['click__2__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['add'](e);}; + extra.handlers['click__2__'] = extra.handlers['click__2__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['add'](e);}; p1.on['click'] = extra.handlers['click__2__']; c1.push({text: \`Click\`}); return vn1; @@ -2883,7 +2883,7 @@ exports[`t-on t-on combined with t-esc 1`] = ` let c2 = [], p2 = {key:2,on:{}}; let vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['onClick'](e);}; + extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['onClick'](e);}; p2.on['click'] = extra.handlers['click__3__']; let _4 = scope['text']; if (_4 != null) { @@ -2905,7 +2905,7 @@ exports[`t-on t-on combined with t-raw 1`] = ` let c2 = [], p2 = {key:2,on:{}}; let vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['onClick'](e);}; + extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['onClick'](e);}; p2.on['click'] = extra.handlers['click__3__']; let _4 = scope['html']; if (_4 != null) { @@ -2923,12 +2923,12 @@ exports[`t-on t-on with .capture modifier 1`] = ` let h = this.h; let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('div', p1, c1); - extra.handlers['!click__2__'] = extra.handlers['!click__2__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['onCapture'](e);}; + extra.handlers['!click__2__'] = extra.handlers['!click__2__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['onCapture'](e);}; p1.on['!click'] = extra.handlers['!click__2__']; let c3 = [], p3 = {key:3,on:{}}; let vn3 = h('button', p3, c3); c1.push(vn3); - extra.handlers['click__4__'] = extra.handlers['click__4__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['doSomething'](e);}; + extra.handlers['click__4__'] = extra.handlers['click__4__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['doSomething'](e);}; p3.on['click'] = extra.handlers['click__4__']; c3.push({text: \`Button\`}); return vn1; @@ -2946,7 +2946,7 @@ exports[`t-on t-on with empty handler (only modifiers) 1`] = ` let c2 = [], p2 = {key:2,on:{}}; let vn2 = h('button', p2, c2); c1.push(vn2); - p2.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}e.preventDefault();}; + p2.on['click'] = function (e) {if (context.__owl__.status === 5){return}e.preventDefault();}; c2.push({text: \`Button\`}); return vn1; }" @@ -2961,7 +2961,7 @@ exports[`t-on t-on with inline statement (function call) 1`] = ` let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); const state_2 = scope['state']; - p1.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}state_2.incrementCounter(2)}; + p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}state_2.incrementCounter(2)}; c1.push({text: \`Click\`}); return vn1; }" @@ -2976,7 +2976,7 @@ exports[`t-on t-on with inline statement 1`] = ` let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); const state_2 = scope['state']; - p1.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}state_2.counter++}; + p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}state_2.counter++}; c1.push({text: \`Click\`}); return vn1; }" @@ -2991,7 +2991,7 @@ exports[`t-on t-on with inline statement, part 2 1`] = ` let c1 = [], p1 = {key:1,on:{}}; let vn1 = h('button', p1, c1); const state_2 = scope['state']; - p1.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}state_2.flag=!state_2.flag}; + p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}state_2.flag=!state_2.flag}; c1.push({text: \`Toggle\`}); return vn1; }" @@ -3007,7 +3007,7 @@ exports[`t-on t-on with inline statement, part 3 1`] = ` let vn1 = h('button', p1, c1); const state_2 = scope['state']; const someFunction_2 = scope['someFunction']; - p1.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}state_2.n=someFunction_2(3)}; + p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}state_2.n=someFunction_2(3)}; c1.push({text: \`Toggle\`}); return vn1; }" @@ -3024,7 +3024,7 @@ exports[`t-on t-on with prevent and self modifiers (order matters) 1`] = ` let c2 = [], p2 = {key:2,on:{}}; let vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.isDestroyed){return}e.preventDefault();if (e.target !== this.elm) {return}utils.getComponent(context)['onClick'](e);}; + extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.status === 5){return}e.preventDefault();if (e.target !== this.elm) {return}utils.getComponent(context)['onClick'](e);}; p2.on['click'] = extra.handlers['click__3__']; let c4 = [], p4 = {key:4}; let vn4 = h('span', p4, c4); @@ -3045,19 +3045,19 @@ exports[`t-on t-on with prevent and/or stop modifiers 1`] = ` let c2 = [], p2 = {key:2,on:{}}; let vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.isDestroyed){return}e.preventDefault();utils.getComponent(context)['onClickPrevented'](e);}; + extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.status === 5){return}e.preventDefault();utils.getComponent(context)['onClickPrevented'](e);}; p2.on['click'] = extra.handlers['click__3__']; c2.push({text: \`Button 1\`}); let c4 = [], p4 = {key:4,on:{}}; let vn4 = h('button', p4, c4); c1.push(vn4); - extra.handlers['click__5__'] = extra.handlers['click__5__'] || function (e) {if (context.__owl__.isDestroyed){return}e.stopPropagation();utils.getComponent(context)['onClickStopped'](e);}; + extra.handlers['click__5__'] = extra.handlers['click__5__'] || function (e) {if (context.__owl__.status === 5){return}e.stopPropagation();utils.getComponent(context)['onClickStopped'](e);}; p4.on['click'] = extra.handlers['click__5__']; c4.push({text: \`Button 2\`}); let c6 = [], p6 = {key:6,on:{}}; let vn6 = h('button', p6, c6); c1.push(vn6); - extra.handlers['click__7__'] = extra.handlers['click__7__'] || function (e) {if (context.__owl__.isDestroyed){return}e.preventDefault();e.stopPropagation();utils.getComponent(context)['onClickPreventedAndStopped'](e);}; + extra.handlers['click__7__'] = extra.handlers['click__7__'] || function (e) {if (context.__owl__.status === 5){return}e.preventDefault();e.stopPropagation();utils.getComponent(context)['onClickPreventedAndStopped'](e);}; p6.on['click'] = extra.handlers['click__7__']; c6.push({text: \`Button 3\`}); return vn1; @@ -3097,7 +3097,7 @@ exports[`t-on t-on with prevent modifier in t-foreach 1`] = ` let vn7 = h('a', p7, c7); c1.push(vn7); let args8 = [scope['project'].id]; - p7.on['click'] = function (e) {if (context.__owl__.isDestroyed){return}e.preventDefault();utils.getComponent(context)['onEdit'](...args8, e);}; + p7.on['click'] = function (e) {if (context.__owl__.status === 5){return}e.preventDefault();utils.getComponent(context)['onEdit'](...args8, e);}; c7.push({text: \` Edit \`}); let _9 = scope['project'].name; if (_9 != null) { @@ -3121,7 +3121,7 @@ exports[`t-on t-on with self and prevent modifiers (order matters) 1`] = ` let c2 = [], p2 = {key:2,on:{}}; let vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.isDestroyed){return}if (e.target !== this.elm) {return}e.preventDefault();utils.getComponent(context)['onClick'](e);}; + extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.status === 5){return}if (e.target !== this.elm) {return}e.preventDefault();utils.getComponent(context)['onClick'](e);}; p2.on['click'] = extra.handlers['click__3__']; let c4 = [], p4 = {key:4}; let vn4 = h('span', p4, c4); @@ -3142,7 +3142,7 @@ exports[`t-on t-on with self modifier 1`] = ` let c2 = [], p2 = {key:2,on:{}}; let vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['onClick'](e);}; + extra.handlers['click__3__'] = extra.handlers['click__3__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['onClick'](e);}; p2.on['click'] = extra.handlers['click__3__']; let c4 = [], p4 = {key:4}; let vn4 = h('span', p4, c4); @@ -3151,7 +3151,7 @@ exports[`t-on t-on with self modifier 1`] = ` let c5 = [], p5 = {key:5,on:{}}; let vn5 = h('button', p5, c5); c1.push(vn5); - extra.handlers['click__6__'] = extra.handlers['click__6__'] || function (e) {if (context.__owl__.isDestroyed){return}if (e.target !== this.elm) {return}utils.getComponent(context)['onClickSelf'](e);}; + extra.handlers['click__6__'] = extra.handlers['click__6__'] || function (e) {if (context.__owl__.status === 5){return}if (e.target !== this.elm) {return}utils.getComponent(context)['onClickSelf'](e);}; p5.on['click'] = extra.handlers['click__6__']; let c7 = [], p7 = {key:7}; let vn7 = h('span', p7, c7); diff --git a/tests/router/__snapshots__/link.test.ts.snap b/tests/router/__snapshots__/link.test.ts.snap index e395f51d5..fb09c0b71 100644 --- a/tests/router/__snapshots__/link.test.ts.snap +++ b/tests/router/__snapshots__/link.test.ts.snap @@ -11,7 +11,7 @@ exports[`Link component can render simple cases 1`] = ` let _6 = scope['href']; let c7 = [], p7 = {key:7,attrs:{href: _6},class:_5,on:{}}; let vn7 = h('a', p7, c7); - extra.handlers['click__8__'] = extra.handlers['click__8__'] || function (e) {if (context.__owl__.isDestroyed){return}utils.getComponent(context)['navigate'](e);}; + extra.handlers['click__8__'] = extra.handlers['click__8__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['navigate'](e);}; p7.on['click'] = extra.handlers['click__8__']; const slot9 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; if (slot9) { diff --git a/tools/debug.js b/tools/debug.js index 3d38902c7..d5751d7e9 100644 --- a/tools/debug.js +++ b/tools/debug.js @@ -101,7 +101,7 @@ component.render = function(...args) { const __owl__ = component.__owl__; let msg = `render`; - if (!__owl__.isMounted && !__owl__.currentFiber) { + if (__owl__.status !== 3 /* mounted */ && !__owl__.currentFiber) { msg += ` (warning: component is not mounted)`; } log(msg);