diff --git a/src/component/component.ts b/src/component/component.ts index 1a272efe..c72879b6 100644 --- a/src/component/component.ts +++ b/src/component/component.ts @@ -60,6 +60,9 @@ interface Internal { cmap: { [key: number]: number }; currentFiber: Fiber | null; + // parentLastFiberId is there to help the parent component to detect, among + // its children, those that are not used anymore and thus can be destroyed + parentLastFiberId: number; boundHandlers: { [key: number]: any }; observer: Observer | null; @@ -169,6 +172,7 @@ export class Component { children: {}, cmap: {}, currentFiber: null, + parentLastFiberId: 0, boundHandlers: {}, mountedCB: null, willUnmountCB: null, @@ -579,6 +583,16 @@ export class Component { handlers: __owl__.boundHandlers, fiber: fiber }); + // we iterate over the children to detect those that no longer belong to the + // current rendering: those ones, if not mounted yet, can (and have to) be + // destroyed right now, because they are not in the DOM, and thus we won't + // be notified later on (when patching), that they are removed from the DOM + for (let childKey in __owl__.children) { + let child = __owl__.children[childKey]; + if (!child.__owl__.isMounted && child.__owl__.parentLastFiberId < fiber.id) { + child.destroy(); + } + } if (!vnode) { throw new Error(`Rendering '${this.constructor.name}' did not return anything`); } diff --git a/src/component/directive.ts b/src/component/directive.ts index ed293df9..a00ca646 100644 --- a/src/component/directive.ts +++ b/src/component/directive.ts @@ -443,7 +443,7 @@ QWeb.addDirective({ ); ctx.addLine(`const fiber = w${componentID}.__owl__.currentFiber;`); ctx.addLine( - `def${defID}.then(function () { if (fiber.isCompleted) { w${componentID}.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; ${createHook}});` + `def${defID}.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; ${createHook}});` ); if (registerCode) { ctx.addLine(registerCode); @@ -459,6 +459,7 @@ QWeb.addDirective({ ctx.addLine(`w${componentID}.__owl__.classObj=${classObj};`); } + ctx.addLine(`w${componentID}.__owl__.parentLastFiberId = extra.fiber.id;`); ctx.addLine(`sibling = w${componentID}.__owl__.currentFiber || sibling;`); return true; diff --git a/src/component/fiber.ts b/src/component/fiber.ts index 0afc9e55..a761cc48 100644 --- a/src/component/fiber.ts +++ b/src/component/fiber.ts @@ -16,6 +16,9 @@ import { scheduler } from "./scheduler"; */ export class Fiber { + static nextId: number = 1; + id: number = Fiber.nextId++; + // The force attribute determines if a rendering should bypass the `shouldUpdate` // method potentially implemented by a component. It is usually set to false. force: boolean; @@ -101,6 +104,7 @@ export class Fiber { oldFiber.child = null; } oldFiber.counter = 1; // re-initialize counter + oldFiber.id = Fiber.nextId++; } /** diff --git a/tests/__snapshots__/animations.test.ts.snap b/tests/__snapshots__/animations.test.ts.snap index 3647b7a3..ba83ef2f 100644 --- a/tests/__snapshots__/animations.test.ts.snap +++ b/tests/__snapshots__/animations.test.ts.snap @@ -35,10 +35,11 @@ exports[`animations t-transition combined with component 1`] = ` }; utils.transitionRemove(vn, 'chimay', finalize);}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -80,10 +81,11 @@ exports[`animations t-transition combined with t-component and t-if 1`] = ` }; utils.transitionRemove(vn, 'chimay', finalize);}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; } return vn1; diff --git a/tests/component/__snapshots__/component.test.ts.snap b/tests/component/__snapshots__/component.test.ts.snap index c797c165..ef8b7ecc 100644 --- a/tests/component/__snapshots__/component.test.ts.snap +++ b/tests/component/__snapshots__/component.test.ts.snap @@ -61,10 +61,11 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for let def8 = w9.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k10, hook: {insert(vn) { let nvn=w9.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w9.destroy();}}}); const fiber = w9.__owl__.currentFiber; - def8.then(function () { if (fiber.isCompleted) { w9.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def8.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c1.push(pvnode); w9.__owl__.pvnode = pvnode; } + w9.__owl__.parentLastFiberId = extra.fiber.id; sibling = w9.__owl__.currentFiber || sibling; } } @@ -105,10 +106,11 @@ exports[`class and style attributes with t-component dynamic t-att-style is prop let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.style = _6;}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.style = _6;}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -150,11 +152,12 @@ exports[`class and style attributes with t-component t-att-class is properly add let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;context.__owl__.refs[ref6] = w3;},remove() {},destroy(vn) {w3.destroy();delete context.__owl__.refs[ref6];}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } w3.__owl__.classObj=_7; + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -210,11 +213,12 @@ exports[`class and style attributes with t-component t-att-class is properly add let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;context.__owl__.refs[ref6] = w3;},remove() {},destroy(vn) {w3.destroy();delete context.__owl__.refs[ref6];}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } w3.__owl__.classObj=_7; + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -282,10 +286,11 @@ exports[`composition sub components with some state rendered in a loop 1`] = ` let def6 = w7.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k8, hook: {insert(vn) { let nvn=w7.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}}); const fiber = w7.__owl__.currentFiber; - def6.then(function () { if (fiber.isCompleted) { w7.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def6.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c1.push(pvnode); w7.__owl__.pvnode = pvnode; } + w7.__owl__.parentLastFiberId = extra.fiber.id; sibling = w7.__owl__.currentFiber || sibling; } return vn1; @@ -324,10 +329,11 @@ exports[`composition t-component with dynamic value 1`] = ` let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -365,10 +371,11 @@ exports[`composition t-component with dynamic value 2 1`] = ` let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -406,10 +413,11 @@ exports[`dynamic t-props basic use 1`] = ` let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -451,10 +459,11 @@ exports[`other directives with t-component t-on with getter as handler 1`] = ` let def3 = w4.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k5, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}}); const fiber = w4.__owl__.currentFiber; - def3.then(function () { if (fiber.isCompleted) { w4.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['handler'];if (fn) { fn.call(owner, e); } else { owner.handler; }});}};}); + def3.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['handler'];if (fn) { fn.call(owner, e); } else { owner.handler; }});}};}); c1.push(pvnode); w4.__owl__.pvnode = pvnode; } + w4.__owl__.parentLastFiberId = extra.fiber.id; sibling = w4.__owl__.currentFiber || sibling; return vn1; }" @@ -492,10 +501,11 @@ exports[`other directives with t-component t-on with handler bound to argument 1 let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, 3, e); } else { owner.onEv; }});}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, 3, e); } else { owner.onEv; }});}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -533,10 +543,11 @@ exports[`other directives with t-component t-on with handler bound to empty obje let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -574,10 +585,11 @@ exports[`other directives with t-component t-on with handler bound to empty obje let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -615,10 +627,11 @@ exports[`other directives with t-component t-on with handler bound to object 1`] let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {val:3}, e); } else { owner.onEv; }});}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {val:3}, e); } else { owner.onEv; }});}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -660,10 +673,11 @@ exports[`other directives with t-component t-on with inline statement 1`] = ` let def3 = w4.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k5, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}}); const fiber = w4.__owl__.currentFiber; - def3.then(function () { if (fiber.isCompleted) { w4.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['state.counter++'];if (fn) { fn.call(owner, e); } else { owner.state.counter++; }});}};}); + def3.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['state.counter++'];if (fn) { fn.call(owner, e); } else { owner.state.counter++; }});}};}); c1.push(pvnode); w4.__owl__.pvnode = pvnode; } + w4.__owl__.parentLastFiberId = extra.fiber.id; sibling = w4.__owl__.currentFiber || sibling; return vn1; }" @@ -701,10 +715,11 @@ exports[`other directives with t-component t-on with no handler (only modifiers) let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -742,10 +757,11 @@ exports[`other directives with t-component t-on with prevent and self modifiers let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {e.preventDefault();if (e.target !== vn.elm) {return}const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {e.preventDefault();if (e.target !== vn.elm) {return}const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -783,10 +799,11 @@ exports[`other directives with t-component t-on with self and prevent modifiers let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (e.target !== vn.elm) {return}e.preventDefault();const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (e.target !== vn.elm) {return}e.preventDefault();const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -824,10 +841,11 @@ exports[`other directives with t-component t-on with self modifier 1`] = ` let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {const fn = owner['onEv1'];if (fn) { fn.call(owner, e); } else { owner.onEv1; }});vn.elm.addEventListener('ev-2', function (e) {if (e.target !== vn.elm) {return}const fn = owner['onEv2'];if (fn) { fn.call(owner, e); } else { owner.onEv2; }});}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {const fn = owner['onEv1'];if (fn) { fn.call(owner, e); } else { owner.onEv1; }});vn.elm.addEventListener('ev-2', function (e) {if (e.target !== vn.elm) {return}const fn = owner['onEv2'];if (fn) { fn.call(owner, e); } else { owner.onEv2; }});}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -865,10 +883,11 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {e.stopPropagation();const fn = owner['onEv1'];if (fn) { fn.call(owner, e); } else { owner.onEv1; }});vn.elm.addEventListener('ev-2', function (e) {e.preventDefault();const fn = owner['onEv2'];if (fn) { fn.call(owner, e); } else { owner.onEv2; }});vn.elm.addEventListener('ev-3', function (e) {e.stopPropagation();e.preventDefault();const fn = owner['onEv3'];if (fn) { fn.call(owner, e); } else { owner.onEv3; }});}};}); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {e.stopPropagation();const fn = owner['onEv1'];if (fn) { fn.call(owner, e); } else { owner.onEv1; }});vn.elm.addEventListener('ev-2', function (e) {e.preventDefault();const fn = owner['onEv2'];if (fn) { fn.call(owner, e); } else { owner.onEv2; }});vn.elm.addEventListener('ev-3', function (e) {e.stopPropagation();e.preventDefault();const fn = owner['onEv3'];if (fn) { fn.call(owner, e); } else { owner.onEv3; }});}};}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -925,10 +944,11 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = ` let def3 = w4.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k5, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}}); const fiber = w4.__owl__.currentFiber; - def3.then(function () { if (fiber.isCompleted) { w4.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def3.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c1.push(pvnode); w4.__owl__.pvnode = pvnode; } + w4.__owl__.parentLastFiberId = extra.fiber.id; sibling = w4.__owl__.currentFiber || sibling; return vn1; }" @@ -983,10 +1003,11 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument let def6 = w7.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k8, hook: {insert(vn) { let nvn=w7.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}}); const fiber = w7.__owl__.currentFiber; - def6.then(function () { if (fiber.isCompleted) { w7.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, arg10, e); } else { owner.onEv; }});}};}); + def6.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, arg10, e); } else { owner.onEv; }});}};}); c1.push(pvnode); w7.__owl__.pvnode = pvnode; } + w7.__owl__.parentLastFiberId = extra.fiber.id; sibling = w7.__owl__.currentFiber || sibling; } return vn1; @@ -1284,10 +1305,11 @@ exports[`t-slot directive can define and call slots 1`] = ` let def2 = w3.__prepare(extra.fiber, {}, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -1530,10 +1552,11 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = ` let def8 = w9.__prepare(extra.fiber, Object.assign({}, scope), undefined, sibling); let pvnode = h('dummy', {key: k10, hook: {insert(vn) { let nvn=w9.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w9.destroy();}}}); const fiber = w9.__owl__.currentFiber; - def8.then(function () { if (fiber.isCompleted) { w9.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def8.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c7.push(pvnode); w9.__owl__.pvnode = pvnode; } + w9.__owl__.parentLastFiberId = extra.fiber.id; sibling = w9.__owl__.currentFiber || sibling; } return vn1; @@ -1634,10 +1657,11 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = ` let def9 = w10.__prepare(extra.fiber, Object.assign({}, scope), {_8}, sibling); let pvnode = h('dummy', {key: k11, hook: {insert(vn) { let nvn=w10.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w10.destroy();}}}); const fiber = w10.__owl__.currentFiber; - def9.then(function () { if (fiber.isCompleted) { w10.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def9.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c7.push(pvnode); w10.__owl__.pvnode = pvnode; } + w10.__owl__.parentLastFiberId = extra.fiber.id; sibling = w10.__owl__.currentFiber || sibling; } return vn1; @@ -1692,10 +1716,11 @@ exports[`t-slot directive slots are rendered with proper context, part 4 1`] = ` let def3 = w4.__prepare(extra.fiber, {}, {_2}, sibling); let pvnode = h('dummy', {key: k5, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}}); const fiber = w4.__owl__.currentFiber; - def3.then(function () { if (fiber.isCompleted) { w4.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def3.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c1.push(pvnode); w4.__owl__.pvnode = pvnode; } + w4.__owl__.parentLastFiberId = extra.fiber.id; sibling = w4.__owl__.currentFiber || sibling; return vn1; }" @@ -1767,10 +1792,11 @@ exports[`top level sub widgets basic use 1`] = ` let def1 = w2.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k3, hook: {insert(vn) { let nvn=w2.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w2.destroy();}}}); const fiber = w2.__owl__.currentFiber; - def1.then(function () { if (fiber.isCompleted) { w2.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def1.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); utils.defineProxy(vn5, pvnode); w2.__owl__.pvnode = pvnode; } + w2.__owl__.parentLastFiberId = extra.fiber.id; sibling = w2.__owl__.currentFiber || sibling; return result; }" @@ -1810,10 +1836,11 @@ exports[`top level sub widgets can select a sub widget 1`] = ` let def1 = w2.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k3, hook: {insert(vn) { let nvn=w2.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w2.destroy();}}}); const fiber = w2.__owl__.currentFiber; - def1.then(function () { if (fiber.isCompleted) { w2.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def1.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); utils.defineProxy(vn5, pvnode); w2.__owl__.pvnode = pvnode; } + w2.__owl__.parentLastFiberId = extra.fiber.id; sibling = w2.__owl__.currentFiber || sibling; } if (!context['env'].flag) { @@ -1840,10 +1867,11 @@ exports[`top level sub widgets can select a sub widget 1`] = ` let def6 = w7.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k8, hook: {insert(vn) { let nvn=w7.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}}); const fiber = w7.__owl__.currentFiber; - def6.then(function () { if (fiber.isCompleted) { w7.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def6.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); utils.defineProxy(vn10, pvnode); w7.__owl__.pvnode = pvnode; } + w7.__owl__.parentLastFiberId = extra.fiber.id; sibling = w7.__owl__.currentFiber || sibling; } return result; diff --git a/tests/component/__snapshots__/props_validation.test.ts.snap b/tests/component/__snapshots__/props_validation.test.ts.snap index 17dd7e5b..d909abf4 100644 --- a/tests/component/__snapshots__/props_validation.test.ts.snap +++ b/tests/component/__snapshots__/props_validation.test.ts.snap @@ -32,10 +32,11 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] = let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); c1.push(pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" diff --git a/tests/component/component.test.ts b/tests/component/component.test.ts index 80ebc8bd..507ef5cf 100644 --- a/tests/component/component.test.ts +++ b/tests/component/component.test.ts @@ -977,13 +977,12 @@ describe("destroy method", () => { }); test("destroying a widget before being mounted (2)", async () => { - const def = makeDeferred(); const steps: string[] = []; class Child extends Component { static template = xml``; willStart() { steps.push("willStart"); - return def; + return makeDeferred(); } __destroy(parent) { steps.push("__destroy"); @@ -1002,12 +1001,6 @@ describe("destroy method", () => { parent.state.flag = false; await prom; expect(fixture.innerHTML).toBe("
"); - // we can't easily detect that we have to destroy the component before - // willStart is resolved. However, willStart should have no side-effect - // (like dispatching an action), so it's probably fine - expect(steps).toEqual(["willStart"]); - def.resolve(); - await nextTick(); expect(steps).toEqual(["willStart", "__destroy"]); }); }); diff --git a/tests/context.test.ts b/tests/context.test.ts index a688df01..5be3b1de 100644 --- a/tests/context.test.ts +++ b/tests/context.test.ts @@ -257,13 +257,12 @@ describe("Context", () => { test("destroyed component before being mounted is inactive", async () => { const testContext = new Context({ a: 123 }); - const def = makeDeferred(); class Child extends Component { static template = xml``; contextObj = useContext(testContext); willStart() { - return def; + return makeDeferred(); } } class Parent extends Component { @@ -279,8 +278,6 @@ describe("Context", () => { parent.state.flag = false; await prom; expect(fixture.innerHTML).toBe("
"); - def.resolve(); // must wait for willStart promise to be resolved - await nextTick(); // kind of whitebox... // we make sure we do not have any pending subscriptions to the 'update' // event diff --git a/tests/router/__snapshots__/route_component.test.ts.snap b/tests/router/__snapshots__/route_component.test.ts.snap index 140af816..120a999b 100644 --- a/tests/router/__snapshots__/route_component.test.ts.snap +++ b/tests/router/__snapshots__/route_component.test.ts.snap @@ -35,10 +35,11 @@ exports[`RouteComponent can render simple cases 1`] = ` let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); utils.defineProxy(vn6, pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; } return result;