diff --git a/src/component/component.ts b/src/component/component.ts index 83e9a9b0..f08ad7ff 100644 --- a/src/component/component.ts +++ b/src/component/component.ts @@ -1,6 +1,8 @@ import { Observer } from "../core/observer"; import { CompiledTemplate, QWeb } from "../qweb/index"; import { h, patch, VNode } from "../vdom/index"; +import { Fiber } from "./fiber"; +import { scheduler } from "./scheduler"; import "./directive"; import "./props_validation"; @@ -11,7 +13,6 @@ import "./props_validation"; * contains: * * - the Env interface (generic type for the environment) - * - the Fiber interface (owl metadata attached to a rendering) * - the Internal interface (the owl specific metadata attached to a component) * - the Component class */ @@ -34,27 +35,6 @@ export interface Env { [key: string]: any; } -/** - * Fibers are small abstractions designed to contain all the internal state - * associated to a "rendering work unit", relative to a specific component. - * - * A rendering will cause the creation of a fiber for each impacted components. - */ -export interface Fiber { - force: boolean; - rootFiber: Fiber | null; - isCancelled: boolean; - scope: any; - vars: any; - patchQueue: Fiber[]; - component: Component; - vnode: VNode | null; - props: Props; - promise: Promise | null; - // handlers?: any; - // mountedHandlers?: any; -} - /** * 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 @@ -65,6 +45,7 @@ interface Internal { // relationships readonly id: number; vnode: VNode | null; + pvnode: VNode | null; isMounted: boolean; isDestroyed: boolean; @@ -77,7 +58,7 @@ interface Internal { // the component instance back whenever the template is rerendered. cmap: { [key: number]: number }; - currentFiber: Fiber | null; + currentFiber: Fiber | null; boundHandlers: { [key: number]: any }; observer: Observer | null; @@ -183,6 +164,7 @@ export class Component { this.__owl__ = { id: id, vnode: null, + pvnode: null, isMounted: false, isDestroyed: false, parent: p, @@ -296,27 +278,42 @@ export class Component { async mount(target: HTMLElement, renderBeforeRemount: boolean = false): Promise { const __owl__ = this.__owl__; if (__owl__.isMounted) { - return; + return Promise.resolve(); } - const fiber = this.__createFiber(false, undefined, undefined, undefined); + const fiber = new Fiber(null, this, this.props, undefined, undefined, false); if (!__owl__.vnode) { - fiber.promise = this.__prepareAndRender(fiber); - const vnode = await fiber.promise; - if (__owl__.isDestroyed) { - // component was destroyed before we get here... - return; - } - this.__patch(vnode); + this.__prepareAndRender(fiber); + return new Promise(resolve => { + scheduler.addFiber(fiber, () => { + if (!__owl__.isDestroyed) { + this.__patch(fiber.vnode); + target.appendChild(this.el!); + if (document.body.contains(target)) { + this.__callMounted(); + } + } + resolve(); + }); + }); } else if (renderBeforeRemount) { - fiber.patchQueue.push(fiber); - fiber.promise = this.__render(fiber); - await fiber.promise; - this.__applyPatchQueue(fiber); - } - target.appendChild(this.el!); - - if (document.body.contains(target)) { - this.__callMounted(); + this.__render(fiber); + return new Promise(resolve => { + scheduler.addFiber(fiber, () => { + if (!__owl__.isDestroyed) { + this.__patch(fiber.vnode); + target.appendChild(this.el!); + if (document.body.contains(target)) { + this.__callMounted(); + } + } + resolve(); + }); + }); + } else { + target.appendChild(this.el!); + if (document.body.contains(target)) { + this.__callMounted(); + } } } @@ -342,19 +339,22 @@ export class Component { */ async render(force: boolean = false): Promise { const __owl__ = this.__owl__; - if (!__owl__.isMounted) { + if ( + (!__owl__.isMounted && !__owl__.currentFiber) || + (__owl__.currentFiber && !__owl__.currentFiber.isRendered) + ) { return; } - const fiber = this.__createFiber(force, undefined, undefined, undefined); - fiber.patchQueue.push(fiber); - fiber.promise = this.__render(fiber); - await fiber.promise; - - if (__owl__.isMounted && fiber === __owl__.currentFiber) { - // we only update the vnode and the actual DOM if no other rendering - // occurred between now and when the render method was initially called. - this.__applyPatchQueue(fiber); - } + const fiber = new Fiber(null, this, this.props, undefined, undefined, force); + this.__render(fiber); + return new Promise(resolve => { + scheduler.addFiber(fiber.root, () => { + if (__owl__.isMounted && fiber === fiber.root) { + fiber.__applyPatchQueue(); + } + resolve(); + }); + }); } /** @@ -407,27 +407,6 @@ export class Component { // Private //-------------------------------------------------------------------------- - /** - * This method is a helper to create a fiber element. - */ - __createFiber(force, scope, vars, parent?: Fiber): Fiber { - const fiber: Fiber = { - force, - scope, - vars, - rootFiber: null, - isCancelled: false, - component: this, - vnode: null, - patchQueue: parent ? parent.patchQueue : [], - props: this.props, - promise: null - }; - fiber.rootFiber = parent ? parent.rootFiber : fiber; - this.__owl__.currentFiber = fiber; - return fiber; - } - /** * Private helper to perform a full destroy, from the point of view of an Owl * component. It does not remove the el (this is done only once on the top @@ -478,7 +457,7 @@ export class Component { __owl__.mountedCB(); } } catch (e) { - errorHandler(e, this); + console.error(e); // TODO : add a test } } @@ -504,25 +483,35 @@ export class Component { */ async __updateProps( nextProps: Props, - parentFiber: Fiber, - scope?: any, - vars?: any + parentFiber: Fiber, + scope: any, + vars: any, + previousSibling?: Fiber | null ): Promise { const shouldUpdate = parentFiber.force || this.shouldUpdate(nextProps); if (shouldUpdate) { + const __owl__ = this.__owl__; + const fiber = new Fiber(parentFiber, this, this.props, scope, vars, parentFiber.force); + if (!parentFiber.child) { + parentFiber.child = fiber; + } else { + previousSibling!.sibling = fiber; + } + const defaultProps = (this.constructor).defaultProps; if (defaultProps) { nextProps = this.__applyDefaultProps(nextProps, defaultProps); } await Promise.all([ this.willUpdateProps(nextProps), - this.__owl__.willUpdatePropsCB && this.__owl__.willUpdatePropsCB(nextProps) + __owl__.willUpdatePropsCB && __owl__.willUpdatePropsCB(nextProps) ]); + if (fiber.isCancelled) { + return; + } this.props = nextProps; - const fiber = this.__createFiber(parentFiber.force, scope, vars, parentFiber); - fiber.patchQueue.push(fiber); - await this.__render(fiber); + this.__render(fiber); } } @@ -534,6 +523,7 @@ export class Component { const __owl__ = this.__owl__; const target = __owl__.vnode || document.createElement(vnode.sel!); __owl__.vnode = patch(target, vnode); + __owl__.currentFiber = null; } /** @@ -541,10 +531,15 @@ export class Component { * subcomponent is created. It gets its scope and vars, if any, from the * parent template. */ - __prepare(parentFiber: Fiber, scope: any, vars: any): Promise { - const fiber = this.__createFiber(parentFiber.force, scope, vars, parentFiber); - fiber.promise = this.__prepareAndRender(fiber); - return fiber.promise; + __prepare(parentFiber: Fiber, scope: any, vars: any, previousSibling?: Fiber | null) { + const fiber = new Fiber(parentFiber, this, this.props, scope, vars, parentFiber.force); + fiber.shouldPatch = false; + if (!parentFiber.child) { + parentFiber.child = fiber; + } else { + previousSibling!.sibling = fiber; + } + return this.__prepareAndRender(fiber); } __getTemplate(qweb: QWeb): string { @@ -570,68 +565,66 @@ export class Component { } return p._template; } - async __prepareAndRender(fiber: Fiber): Promise { + async __prepareAndRender(fiber: Fiber) { try { await Promise.all([this.willStart(), this.__owl__.willStartCB && this.__owl__.willStartCB()]); } catch (e) { - errorHandler(e, this); - return Promise.resolve(h("div")); + errorHandler(e, fiber); + fiber.vnode = h("div"); // -> we render this div at the end + return Promise.resolve(); } - const __owl__ = this.__owl__; - if (__owl__.isDestroyed) { - return Promise.resolve(h("div")); + if (this.__owl__.isDestroyed) { + return Promise.resolve(); + } + if (!fiber.isCancelled) { + this.__render(fiber); } - return this.__render(fiber); } - __render(fiber: Fiber): Promise { + __render(fiber: Fiber) { const __owl__ = this.__owl__; - const promises: Promise[] = []; if (__owl__.observer) { __owl__.observer.allowMutations = false; } let vnode; try { vnode = __owl__.render!(this, { - promises, handlers: __owl__.boundHandlers, fiber: fiber }); } catch (e) { vnode = __owl__.vnode || h("div"); - errorHandler(e, this); + errorHandler(e, fiber); } fiber.vnode = vnode; if (__owl__.observer) { __owl__.observer.allowMutations = true; } - // this part is critical for the patching process to be done correctly. The - // tricky part is that a child component can be rerendered on its own, which - // will update its own vnode representation without the knowledge of the - // parent component. With this, we make sure that the parent component will be - // able to patch itself properly after - vnode.key = __owl__.id; - - // we applly here the class information described on the component by the + // we apply here the class information described on the component by the // template (so, something like ) to the actual // root vnode if (__owl__.classObj) { vnode.data.class = Object.assign(vnode.data.class || {}, __owl__.classObj); } - - return Promise.all(promises).then(() => vnode); + fiber.root.counter--; + fiber.isRendered = true; } /** * Only called by qweb t-component directive */ - __mount(vnode: VNode, elm: HTMLElement): VNode { + __mount(fiber: Fiber, elm: HTMLElement): VNode { + if (fiber !== this.__owl__.currentFiber) { + fiber = this.__owl__.currentFiber!; // TODO: check if we can remove fiber arg + } + const vnode = fiber.vnode!; const __owl__ = this.__owl__; if (__owl__.classObj) { (vnode).data.class = Object.assign((vnode).data.class || {}, __owl__.classObj); } __owl__.vnode = patch(elm, vnode); + __owl__.currentFiber = null; if (__owl__.parent!.__owl__.isMounted && !__owl__.isMounted) { this.__callMounted(); } @@ -664,47 +657,15 @@ export class Component { } return props; } - - /** - * Apply the given patch queue from a fiber. - * 1) Call 'willPatch' on the component of each patch - * 2) Call '__patch' on the component of each patch - * 3) Call 'patched' on the component of each patch, in reverse order - */ - __applyPatchQueue(fiber: Fiber) { - const patchQueue = fiber.patchQueue; - let component: Component = this; - try { - const patchLen = patchQueue.length; - for (let i = 0; i < patchLen; i++) { - component = patchQueue[i].component; - if (component.__owl__.willPatchCB) { - component.__owl__.willPatchCB(); - } - component.willPatch(); - } - for (let i = 0; i < patchLen; i++) { - const fiber = patchQueue[i]; - component = fiber.component; - component.__patch(fiber.vnode); - } - for (let i = patchLen - 1; i >= 0; i--) { - component = patchQueue[i].component; - component.patched(); - if (component.__owl__.patchedCB) { - component.__owl__.patchedCB(); - } - } - } catch (e) { - errorHandler(e, component); - } - } } //------------------------------------------------------------------------------ // Error handling //------------------------------------------------------------------------------ +Fiber.prototype.handleError = function(error) { + errorHandler(error, this); +}; /** * This is the global error handler for errors occurring in Owl main lifecycle * methods. Caught errors are triggered on the QWeb instance, and are @@ -713,8 +674,9 @@ export class Component { * If there are no such component, we destroy everything. This is better than * being in a corrupted state. */ -function errorHandler(error: Error, component: Component) { +export function errorHandler(error: Error, fiber: Fiber) { let canCatch = false; + let component = fiber.component; let qweb = component.env.qweb; let root = component; while (component && !(canCatch = component.catchError !== Component.prototype.catchError)) { @@ -722,7 +684,6 @@ function errorHandler(error: Error, component: Component) { component = component.__owl__.parent!; } console.error(error); - // we trigger error on QWeb so it can be logged/handled qweb.trigger("error", error); if (canCatch) { diff --git a/src/component/directive.ts b/src/component/directive.ts index c4cd0f2b..e4667fd3 100644 --- a/src/component/directive.ts +++ b/src/component/directive.ts @@ -232,7 +232,6 @@ QWeb.addDirective({ let propStr = Object.keys(props) .map(k => k + ":" + props[k]) .join(","); - let dummyID = ctx.generateID(); let defID = ctx.generateID(); let componentID = ctx.generateID(); let keyID = key && ctx.generateID(); @@ -350,18 +349,13 @@ QWeb.addDirective({ ctx.addLine( `let w${componentID} = ${templateId} in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[${templateId}]] : false;` ); - if (ctx.parentNode) { - ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`); - } - let shouldProxy = false; + let shouldProxy = !ctx.parentNode; if (keepAlive) { ctx.addLine( `const fiber${componentID} = Object.assign(Object.create(extra.fiber), {patchQueue: []});` ); } - if (ctx.parentNode) { - ctx.addLine(`c${ctx.parentNode}.push(null);`); - } else { + if (shouldProxy) { let id = ctx.generateID(); ctx.rootContext.rootNode = id; shouldProxy = true; @@ -378,15 +372,9 @@ QWeb.addDirective({ ctx.addIf( `w${componentID} && w${componentID}.__owl__.currentFiber && !w${componentID}.__owl__.vnode` ); - ctx.addIf( - `utils.shallowEqual(props${componentID}, w${componentID}.__owl__.currentFiber.props)` - ); - ctx.addLine(`def${defID} = w${componentID}.__owl__.currentFiber.promise;`); - ctx.addElse(); ctx.addLine(`w${componentID}.destroy();`); ctx.addLine(`w${componentID} = false;`); ctx.closeIf(); - ctx.closeIf(); ctx.addIf(`!w${componentID}`); // new component @@ -450,15 +438,26 @@ QWeb.addDirective({ } else { scopeVars = "undefined, undefined"; } - ctx.addLine(`def${defID} = w${componentID}.__prepare(extra.fiber, ${scopeVars});`); + ctx.addLine(`def${defID} = w${componentID}.__prepare(extra.fiber, ${scopeVars}, sibling);`); // hack: specify empty remove hook to prevent the node from being removed from the DOM - let registerCode = `c${ctx.parentNode}[_${dummyID}_index]=pvnode;`; + let registerCode = ""; if (shouldProxy) { registerCode = `utils.defineProxy(vn${ctx.rootNode}, pvnode);`; } ctx.addLine( - `def${defID} = def${defID}.then(vnode=>{if (w${componentID}.__owl__.isDestroyed){return}${createHook}let pvnode=h(vnode.sel, {key: ${templateId}, hook: {insert(vn) {let nvn=w${componentID}.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeComponentCode}}}});${registerCode}w${componentID}.__owl__.pvnode = pvnode;});` + `let pvnode = h('dummy', {key: ${templateId}, hook: {insert(vn) { let nvn=w${componentID}.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeComponentCode}}}});` ); + ctx.addLine(`const fiber = w${componentID}.__owl__.currentFiber;`); + ctx.addLine( + `def${defID}.then(function () {if (w${componentID}.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; ${createHook}});` + ); + if (registerCode) { + ctx.addLine(registerCode); + } + if (ctx.parentNode) { + ctx.addLine(`c${ctx.parentNode}.push(pvnode);`); + } + ctx.addLine(`w${componentID}.__owl__.pvnode = pvnode;`); ctx.addElse(); // need to update component @@ -475,24 +474,32 @@ QWeb.addDirective({ } ctx.addLine( `def${defID} = def${defID} || w${componentID}.__updateProps(props${componentID}, ${patchQueueCode}${scopeVars && - ", " + scopeVars});` + ", " + scopeVars}, sibling);` ); + ctx.addLine(`let pvnode = w${componentID}.__owl__.pvnode;`); let keepAliveCode = ""; if (keepAlive) { keepAliveCode = `pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w${componentID}.el,vn.elm);vn.elm=w${componentID}.el;w${componentID}.__remount();};`; + ctx.addLine(keepAliveCode); + } + if (registerCode) { + ctx.addLine(registerCode); + } + if (ctx.parentNode) { + ctx.addLine(`c${ctx.parentNode}.push(pvnode);`); + } + if (tattStyle) { + ctx.addLine( + `def${defID} = def${defID}.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};w${componentID}.el.style=${tattStyle};});` + ); } - ctx.addLine( - `def${defID} = def${defID}.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};${ - tattStyle ? `w${componentID}.el.style=${tattStyle};` : "" - }let pvnode=w${componentID}.__owl__.pvnode;${keepAliveCode}${registerCode}});` - ); ctx.closeIf(); if (classObj) { ctx.addLine(`w${componentID}.__owl__.classObj=${classObj};`); } - ctx.addLine(`extra.promises.push(def${defID});`); + ctx.addLine(`sibling = w${componentID}.__owl__.currentFiber;`); return true; } diff --git a/src/component/fiber.ts b/src/component/fiber.ts new file mode 100644 index 00000000..17435e28 --- /dev/null +++ b/src/component/fiber.ts @@ -0,0 +1,163 @@ +import { VNode } from "../vdom/index"; +import { Component } from "./component"; +/** + * Fibers are small abstractions designed to contain all the internal state + * associated to a "rendering work unit", relative to a specific component. + * + * A rendering will cause the creation of a fiber for each impacted components. + */ +export class Fiber { + force: boolean; + isCancelled: boolean = false; + shouldPatch: boolean = true; + isRendered: boolean = false; + + scope: any; + vars: any; + props: any; + + component: Component; + vnode: VNode | null = null; + + root: Fiber; + child: Fiber | null = null; + sibling: Fiber | null = null; + parent: Fiber | null = null; + + counter: number = 0; + + constructor(parent: Fiber | null, component: Component, props, scope, vars, force) { + this.force = force; + this.scope = scope; + this.vars = vars; + this.props = props; + this.component = component; + + this.root = parent ? parent.root : this; + this.parent = parent; + + let oldFiber = component.__owl__.currentFiber; + if (oldFiber && !oldFiber.isCancelled) { + this.__remapFiber(oldFiber); + } + + this.root.counter++; + + component.__owl__.currentFiber = this; + } + + __remapFiber(oldFiber: Fiber) { + oldFiber.cancel(); + if (oldFiber === oldFiber.root) { + oldFiber.root.counter++; + } + if (oldFiber.parent && !this.parent) { + // re-map links + this.parent = oldFiber.parent; + this.root = this.parent.root; + this.sibling = oldFiber.sibling; + if (this.parent.child === oldFiber) { + this.parent.child = this; + } else { + let current = this.parent.child!; + while (true) { + if (current.sibling === oldFiber) { + current.sibling = this; + break; + } + current = current.sibling!; + } + } + } + } + + /** + * This function has been taken from + * https://medium.com/react-in-depth/the-how-and-why-on-reacts-usage-of-linked-list-in-fiber-67f1014d0eb7 + */ + __walk(doWork: (f: Fiber) => Fiber | null) { + let root = this; + let current: Fiber = this; + while (true) { + const child = doWork(current); + if (child) { + current = child; + continue; + } + if (current === root) { + return; + } + while (!current.sibling) { + if (!current.parent || current.parent === root) { + return; + } + current = current.parent; + } + current = current.sibling; + } + } + + /** + * Apply the given patch queue from a fiber. + * 1) Call 'willPatch' on the component of each patch + * 2) Call '__patch' on the component of each patch + * 3) Call 'patched' on the component of each patch, in reverse order + */ + __applyPatchQueue() { + const patchQueue: Fiber[] = []; + const doWork: (Fiber) => Fiber | null = function(f) { + if (f.shouldPatch) { + patchQueue.push(f); + } + return f.child; + }; + this.__walk(doWork); + let component: Component = this.component; + this.shouldPatch = false; + const patchLen = patchQueue.length; + try { + for (let i = 0; i < patchLen; i++) { + component = patchQueue[i].component; + if (component.__owl__.willPatchCB) { + component.__owl__.willPatchCB(); + } + component.willPatch(); + } + } catch (e) { + console.error(e); + } + try { + for (let i = 0; i < patchLen; i++) { + const fiber = patchQueue[i]; + component = fiber.component; + component.__patch(fiber.vnode); + } + } catch (e) { + this.handleError(e); + } + try { + for (let i = patchLen - 1; i >= 0; i--) { + component = patchQueue[i].component; + component.patched(); + if (component.__owl__.patchedCB) { + component.__owl__.patchedCB(); + } + } + } catch (e) { + console.error(e); + } + this.shouldPatch = true; + } + + cancel() { + this.__walk(f => { + if (!f.isRendered) { + f.root.counter--; + } + f.isCancelled = true; + return f.child; + }); + } + + handleError(e: Error) {} +} diff --git a/src/component/scheduler.ts b/src/component/scheduler.ts new file mode 100644 index 00000000..40e0f150 --- /dev/null +++ b/src/component/scheduler.ts @@ -0,0 +1,50 @@ +import { Fiber } from "./fiber"; + +// scheduler +interface Task { + fiber: Fiber; + callback: () => void; +} + +export const scheduler = { + tasks: [] as Task[], + isRunning: false, + + addFiber(fiber, callback) { + this.tasks.push({ fiber, callback }); + if (this.isRunning) { + return; + } + this.scheduleTasks(); + }, + flush() { + let tasks = this.tasks; + this.tasks = []; + tasks = tasks.filter(task => { + if (task.fiber.isCancelled) { + return false; + } + if (task.fiber.counter === 0) { + task.callback(); + return false; + } + return true; + }); + this.tasks = tasks.concat(this.tasks); + }, + processTasks() { + this.flush(); + if (this.tasks.length > 0) { + this.scheduleTasks(); + } else { + this.isRunning = false; + } + }, + + scheduleTasks() { + this.isRunning = true; + this.requestAnimationFrame(() => this.processTasks()); + }, + + requestAnimationFrame: requestAnimationFrame.bind(window) +}; diff --git a/src/context.ts b/src/context.ts index a7f6c4ff..36f8f7f0 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,8 +1,8 @@ import { Component } from "./component/component"; -import { Observer } from "./core/observer"; +import { scheduler } from "./component/scheduler"; import { EventBus } from "./core/event_bus"; +import { Observer } from "./core/observer"; import { onWillUnmount } from "./hooks"; - /** * The `Context` object provides a way to share data between an arbitrary number * of component. Usually, data is passed from a parent to its children component, @@ -53,7 +53,9 @@ export class Context extends EventBus { const sub = subs[i]; const shouldCallback = sub.owner ? sub.owner.__owl__.isMounted : true; if (shouldCallback) { - await sub.callback.call(sub.owner, id); + const render = sub.callback.call(sub.owner, id); + scheduler.flush(); + await render; } } } diff --git a/src/qweb/compilation_context.ts b/src/qweb/compilation_context.ts index e6f00a78..67ab6371 100644 --- a/src/qweb/compilation_context.ts +++ b/src/qweb/compilation_context.ts @@ -22,6 +22,7 @@ export class CompilationContext { shouldDefineUtils: boolean = false; shouldDefineRefs: boolean = false; shouldDefineResult: boolean = true; + shouldDefineSibling: boolean = true; shouldProtectContext: boolean = false; shouldTrackScope: boolean = false; loopNumber: number = 0; @@ -63,6 +64,9 @@ export class CompilationContext { if (this.shouldDefineResult) { this.code.unshift(" let result;"); } + if (this.shouldDefineSibling) { + this.code.unshift(" let sibling = null;"); + } if (this.shouldDefineRefs) { this.code.unshift(" context.__owl__.refs = context.__owl__.refs || {};"); } diff --git a/src/qweb/extensions.ts b/src/qweb/extensions.ts index c0ed0587..5f8f0ace 100644 --- a/src/qweb/extensions.ts +++ b/src/qweb/extensions.ts @@ -207,9 +207,7 @@ QWeb.addDirective({ `slot${slotKey}.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: ${parentNode}, vars: extra.vars, parent: owner}));` ); if (!ctx.parentNode) { - ctx.addLine( - `Promise.all(extra.promises).then(() => utils.defineProxy(result, ${parentNode}[0]))` - ); + ctx.addLine(`utils.defineProxy(result, ${parentNode}[0]);`); } ctx.closeIf(); return true; diff --git a/src/qweb/qweb.ts b/src/qweb/qweb.ts index e54ac6b1..584a3b80 100644 --- a/src/qweb/qweb.ts +++ b/src/qweb/qweb.ts @@ -645,7 +645,7 @@ export class QWeb extends EventBus { if (name.startsWith("t-att-")) { let attName = name.slice(6); const v = ctx.getValue(value); - let formattedValue = typeof v === 'string' ? ctx.formatExpression(v) : v.id; + let formattedValue = typeof v === "string" ? ctx.formatExpression(v) : v.id; if (attName === "class") { ctx.rootContext.shouldDefineUtils = true; diff --git a/src/vdom/vdom.ts b/src/vdom/vdom.ts index 39bc6e7e..f7731e19 100644 --- a/src/vdom/vdom.ts +++ b/src/vdom/vdom.ts @@ -557,13 +557,15 @@ type ArrayOrElement = T | T[]; type VNodeChildren = ArrayOrElement; export function addNS(data: any, children: VNodes | undefined, sel: string | undefined): void { + if (sel === "dummy") { + // we do not need to add the namespace on dummy elements, they come from a + // subcomponent, which will handle the namespace itself + return; + } data.ns = "http://www.w3.org/2000/svg"; if (sel !== "foreignObject" && children !== undefined) { for (let i = 0, iLen = children.length; i < iLen; ++i) { const child = children[i]; - if (child === null) { - continue; - } let childData = child.data; if (childData !== undefined) { addNS(childData, (child as VNode).children as VNodes, child.sel); diff --git a/tests/__snapshots__/animations.test.ts.snap b/tests/__snapshots__/animations.test.ts.snap index 6831205f..d3382645 100644 --- a/tests/__snapshots__/animations.test.ts.snap +++ b/tests/__snapshots__/animations.test.ts.snap @@ -7,40 +7,40 @@ exports[`animations t-transition combined with component 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`Child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => { - w4.destroy(); + if (!w3) { + let componentKey3 = \`Child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => { + w3.destroy(); }; - utils.transitionRemove(vn, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + utils.transitionRemove(vn, 'chimay', finalize);}}}); + const fiber = w3.__owl__.currentFiber; + def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -52,41 +52,41 @@ exports[`animations t-transition combined with t-component and t-if 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); if (context['state'].display) { //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`Child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => { - w4.destroy(); + if (!w3) { + let componentKey3 = \`Child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => { + w3.destroy(); }; - utils.transitionRemove(vn, 'chimay', finalize);}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + utils.transitionRemove(vn, 'chimay', finalize);}}}); + const fiber = w3.__owl__.currentFiber; + def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; } return vn1; }" @@ -96,6 +96,7 @@ exports[`animations t-transition with no delay/duration 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); @@ -116,6 +117,7 @@ exports[`animations t-transition, on a simple node (insert) 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); diff --git a/tests/animations.test.ts b/tests/animations.test.ts index 968e4034..d6138955 100644 --- a/tests/animations.test.ts +++ b/tests/animations.test.ts @@ -251,11 +251,11 @@ describe("animations", () => { widget.state.display = false; patchNextFrame(cb => { expect(fixture.innerHTML).toBe( - '
blue
' + '
blue
' ); cb(); expect(fixture.innerHTML).toBe( - '
blue
' + '
blue
' ); def.resolve(); }); diff --git a/tests/component/__snapshots__/component.test.ts.snap b/tests/component/__snapshots__/component.test.ts.snap index 512c6664..1d106281 100644 --- a/tests/component/__snapshots__/component.test.ts.snap +++ b/tests/component/__snapshots__/component.test.ts.snap @@ -7,6 +7,7 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -40,33 +41,32 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for context.blip = _6[i2]; context.blip_value = _7[i2]; //COMPONENT - let def9; - let templateId10 = \`__11__\${i1}__\${i2}__\`; - let w10 = templateId10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId10]] : false; - let _8_index = c1.length; - c1.push(null); - let props10 = {blip:context['blip']}; - if (w10 && w10.__owl__.currentFiber && !w10.__owl__.vnode) { - if (utils.shallowEqual(props10, w10.__owl__.currentFiber.props)) { - def9 = w10.__owl__.currentFiber.promise; - } else { - w10.destroy(); - w10 = false; - } + let def8; + let templateId9 = \`__10__\${i1}__\${i2}__\`; + let w9 = templateId9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId9]] : false; + let props9 = {blip:context['blip']}; + if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) { + w9.destroy(); + w9 = false; } - if (!w10) { - let componentKey10 = \`Child\`; - let W10 = context.constructor.components[componentKey10] || QWeb.components[componentKey10]|| context['Child']; - if (!W10) {throw new Error('Cannot find the definition of component \\"' + componentKey10 + '\\"')} - w10 = new W10(parent, props10); - parent.__owl__.cmap[templateId10] = w10.__owl__.id; - def9 = w10.__prepare(extra.fiber, undefined, undefined); - def9 = def9.then(vnode=>{if (w10.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId10, hook: {insert(vn) {let nvn=w10.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w10.destroy();}}});c1[_8_index]=pvnode;w10.__owl__.pvnode = pvnode;}); + if (!w9) { + let componentKey9 = \`Child\`; + let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| context['Child']; + if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')} + w9 = new W9(parent, props9); + parent.__owl__.cmap[templateId9] = w9.__owl__.id; + def8 = w9.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId9, 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 (w9.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w9.__owl__.pvnode = pvnode; } else { - def9 = def9 || w10.__updateProps(props10, extra.fiber, undefined, undefined); - def9 = def9.then(()=>{if (w10.__owl__.isDestroyed) {return};let pvnode=w10.__owl__.pvnode;c1[_8_index]=pvnode;}); + def8 = def8 || w9.__updateProps(props9, extra.fiber, undefined, undefined, sibling); + let pvnode = w9.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def9); + sibling = w9.__owl__.currentFiber; } } return vn1; @@ -80,38 +80,39 @@ exports[`class and style attributes with t-component dynamic t-att-style is prop let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - const _6 = context['state'].style; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + const _5 = context['state'].style; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}vnode.data.hook = {create(_, vn){vn.elm.style = _6;}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.style = _5;}};}); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};w4.el.style=_6;let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); + def2 = def2.then(()=>{if (w3.__owl__.isDestroyed) {return};w3.el.style=_5;}); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -124,41 +125,41 @@ exports[`class and style attributes with t-component t-att-class is properly add let parent = context; let owner = context; context.__owl__.refs = context.__owl__.refs || {}; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - const ref6 = \`child\`; - let _7 = {'a':true}; - Object.assign(_7, {b:context['state'].b}) - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + const ref5 = \`child\`; + let _6 = {'a':true}; + Object.assign(_6, {b:context['state'].b}) + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`Child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}vnode.data.hook = {create(_, vn){}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;context.__owl__.refs[ref6] = w4;},remove() {},destroy(vn) {w4.destroy();delete context.__owl__.refs[ref6];}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`Child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;context.__owl__.refs[ref5] = w3;},remove() {},destroy(vn) {w3.destroy();delete context.__owl__.refs[ref5];}}}); + const fiber = w3.__owl__.currentFiber; + def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};}); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - w4.__owl__.classObj=_7; - extra.promises.push(def3); + w3.__owl__.classObj=_6; + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -167,6 +168,7 @@ exports[`class and style attributes with t-component t-att-class is properly add "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let _2 = {'c':true}; Object.assign(_2, utils.toObj({d:context['state'].d})) @@ -184,41 +186,41 @@ exports[`class and style attributes with t-component t-att-class is properly add let parent = context; let owner = context; context.__owl__.refs = context.__owl__.refs || {}; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - const ref6 = \`child\`; - let _7 = {'a':true}; - Object.assign(_7, utils.toObj(context['state'].b?'b':'')) - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + const ref5 = \`child\`; + let _6 = {'a':true}; + Object.assign(_6, utils.toObj(context['state'].b?'b':'')) + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`Child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}vnode.data.hook = {create(_, vn){}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;context.__owl__.refs[ref6] = w4;},remove() {},destroy(vn) {w4.destroy();delete context.__owl__.refs[ref6];}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`Child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;context.__owl__.refs[ref5] = w3;},remove() {},destroy(vn) {w3.destroy();delete context.__owl__.refs[ref5];}}}); + const fiber = w3.__owl__.currentFiber; + def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};}); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - w4.__owl__.classObj=_7; - extra.promises.push(def3); + w3.__owl__.classObj=_6; + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -227,6 +229,7 @@ exports[`class and style attributes with t-component t-att-class is properly add "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let _2 = {'c':true}; Object.assign(_2, utils.toObj(context['state'].d?'d':'')) @@ -236,50 +239,6 @@ exports[`class and style attributes with t-component t-att-class is properly add }" `; -exports[`class and style attributes with t-component t-att-class is properly added/removed on widget root el 1`] = ` -"function anonymous(context,extra -) { - let utils = this.constructor.utils; - let QWeb = this.constructor; - let parent = context; - let owner = context; - var h = this.h; - let c1 = [], p1 = {key:1}; - var vn1 = h('div', p1, c1); - //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let _6 = {a:context['state'].a,b:context['state'].b}; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } - } - if (!w4) { - let componentKey4 = \`child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}vnode.data.hook = {create(_, vn){}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); - } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); - } - w4.__owl__.classObj=_6; - extra.promises.push(def3); - return vn1; -}" -`; - exports[`composition sub components dom state with t-keepalive is preserved 1`] = ` "function anonymous(context,extra ) { @@ -287,39 +246,40 @@ exports[`composition sub components dom state with t-keepalive is preserved 1`] let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); if (context['state'].ok) { //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - const fiber4 = Object.assign(Object.create(extra.fiber), {patchQueue: []}); - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + const fiber3 = Object.assign(Object.create(extra.fiber), {patchQueue: []}); + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`InputWidget\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['InputWidget']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.unmount();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`InputWidget\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['InputWidget']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.unmount();}}}); + const fiber = w3.__owl__.currentFiber; + def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, w4.__owl__.isMounted ? extra.fiber : fiber4, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w4.el,vn.elm);vn.elm=w4.el;w4.__remount();};c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, w3.__owl__.isMounted ? extra.fiber : fiber3, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w3.el,vn.elm);vn.elm=w3.el;w3.__remount();}; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; } return vn1; }" @@ -332,6 +292,7 @@ exports[`composition sub components with some state rendered in a loop 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -351,34 +312,33 @@ exports[`composition sub components with some state rendered in a loop 1`] = ` context.number = _3[i1]; context.number_value = _4[i1]; //COMPONENT - let key8 = 'key' + context['number']; - let def6; - let templateId7 = \`__9__\` + key8; - let w7 = templateId7 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId7]] : false; - let _5_index = c1.length; - c1.push(null); - let props7 = {}; - if (w7 && w7.__owl__.currentFiber && !w7.__owl__.vnode) { - if (utils.shallowEqual(props7, w7.__owl__.currentFiber.props)) { - def6 = w7.__owl__.currentFiber.promise; - } else { - w7.destroy(); - w7 = false; - } + let key7 = 'key' + context['number']; + let def5; + let templateId6 = \`__8__\` + key7; + let w6 = templateId6 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId6]] : false; + let props6 = {}; + if (w6 && w6.__owl__.currentFiber && !w6.__owl__.vnode) { + w6.destroy(); + w6 = false; } - if (!w7) { - let componentKey7 = \`ChildWidget\`; - let W7 = context.constructor.components[componentKey7] || QWeb.components[componentKey7]|| context['ChildWidget']; - if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')} - w7 = new W7(parent, props7); - parent.__owl__.cmap[templateId7] = w7.__owl__.id; - def6 = w7.__prepare(extra.fiber, undefined, undefined); - def6 = def6.then(vnode=>{if (w7.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId7, hook: {insert(vn) {let nvn=w7.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});c1[_5_index]=pvnode;w7.__owl__.pvnode = pvnode;}); + if (!w6) { + let componentKey6 = \`ChildWidget\`; + let W6 = context.constructor.components[componentKey6] || QWeb.components[componentKey6]|| context['ChildWidget']; + if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')} + w6 = new W6(parent, props6); + parent.__owl__.cmap[templateId6] = w6.__owl__.id; + def5 = w6.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId6, hook: {insert(vn) { let nvn=w6.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w6.destroy();}}}); + const fiber = w6.__owl__.currentFiber; + def5.then(function () {if (w6.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w6.__owl__.pvnode = pvnode; } else { - def6 = def6 || w7.__updateProps(props7, extra.fiber, undefined, undefined); - def6 = def6.then(()=>{if (w7.__owl__.isDestroyed) {return};let pvnode=w7.__owl__.pvnode;c1[_5_index]=pvnode;}); + def5 = def5 || w6.__updateProps(props6, extra.fiber, undefined, undefined, sibling); + let pvnode = w6.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def6); + sibling = w6.__owl__.currentFiber; } return vn1; }" @@ -391,37 +351,37 @@ exports[`composition t-component with dynamic value 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = (context['state'].widget); - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = (context['state'].widget); + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -433,37 +393,37 @@ exports[`composition t-component with dynamic value 2 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`Widget\${context['state'].widget}\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`Widget\${context['state'].widget}\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -475,37 +435,37 @@ exports[`dynamic t-props basic use 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = Object.assign({}, context['some'].obj); - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = Object.assign({}, context['some'].obj); + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`Child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`Child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -517,39 +477,40 @@ exports[`lifecycle hooks willPatch/patched hook with t-keepalive 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); if (context['state'].flag) { //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - const fiber4 = Object.assign(Object.create(extra.fiber), {patchQueue: []}); - c1.push(null); - let props4 = {v:context['state'].n}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + const fiber3 = Object.assign(Object.create(extra.fiber), {patchQueue: []}); + let props3 = {v:context['state'].n}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`ChildWidget\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['ChildWidget']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.unmount();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`ChildWidget\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['ChildWidget']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.unmount();}}}); + const fiber = w3.__owl__.currentFiber; + def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, w4.__owl__.isMounted ? extra.fiber : fiber4, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w4.el,vn.elm);vn.elm=w4.el;w4.__remount();};c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, w3.__owl__.isMounted ? extra.fiber : fiber3, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + pvnode.data.hook.insert = vn => {vn.elm.parentNode.replaceChild(w3.el,vn.elm);vn.elm=w3.el;w3.__remount();}; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; } return vn1; }" @@ -562,6 +523,7 @@ exports[`other directives with t-component t-on with getter as handler 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -570,33 +532,32 @@ exports[`other directives with t-component t-on with getter as handler 1`] = ` c1.push({text: _2}); } //COMPONENT - let def4; - let templateId5 = \`__6__\`; - let w5 = templateId5 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId5]] : false; - let _3_index = c1.length; - c1.push(null); - let props5 = {}; - if (w5 && w5.__owl__.currentFiber && !w5.__owl__.vnode) { - if (utils.shallowEqual(props5, w5.__owl__.currentFiber.props)) { - def4 = w5.__owl__.currentFiber.promise; - } else { - w5.destroy(); - w5 = false; - } + let def3; + let templateId4 = \`__5__\`; + let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; + let props4 = {}; + if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { + w4.destroy(); + w4 = false; } - if (!w5) { - let componentKey5 = \`Child\`; - let W5 = context.constructor.components[componentKey5] || QWeb.components[componentKey5]|| context['Child']; - if (!W5) {throw new Error('Cannot find the definition of component \\"' + componentKey5 + '\\"')} - w5 = new W5(parent, props5); - parent.__owl__.cmap[templateId5] = w5.__owl__.id; - def4 = w5.__prepare(extra.fiber, undefined, undefined); - def4 = def4.then(vnode=>{if (w5.__owl__.isDestroyed){return}vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['handler'];if (fn) { fn.call(owner, e); } else { owner.handler; }});}};let pvnode=h(vnode.sel, {key: templateId5, hook: {insert(vn) {let nvn=w5.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w5.destroy();}}});c1[_3_index]=pvnode;w5.__owl__.pvnode = pvnode;}); + if (!w4) { + let componentKey4 = \`Child\`; + let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child']; + if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} + w4 = new W4(parent, props4); + parent.__owl__.cmap[templateId4] = w4.__owl__.id; + def3 = w4.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId4, 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 (w4.__owl__.isDestroyed) {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; } else { - def4 = def4 || w5.__updateProps(props5, extra.fiber, undefined, undefined); - def4 = def4.then(()=>{if (w5.__owl__.isDestroyed) {return};let pvnode=w5.__owl__.pvnode;c1[_3_index]=pvnode;}); + def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined, sibling); + let pvnode = w4.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def4); + sibling = w4.__owl__.currentFiber; return vn1; }" `; @@ -608,37 +569,37 @@ exports[`other directives with t-component t-on with handler bound to argument 1 let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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; }});}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {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; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -650,37 +611,37 @@ exports[`other directives with t-component t-on with handler bound to empty obje let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {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; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -692,37 +653,37 @@ exports[`other directives with t-component t-on with handler bound to empty obje let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {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; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -734,37 +695,37 @@ exports[`other directives with t-component t-on with handler bound to object 1`] let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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; }});}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {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; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -776,6 +737,7 @@ exports[`other directives with t-component t-on with inline statement 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -784,33 +746,32 @@ exports[`other directives with t-component t-on with inline statement 1`] = ` c1.push({text: _2}); } //COMPONENT - let def4; - let templateId5 = \`__6__\`; - let w5 = templateId5 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId5]] : false; - let _3_index = c1.length; - c1.push(null); - let props5 = {}; - if (w5 && w5.__owl__.currentFiber && !w5.__owl__.vnode) { - if (utils.shallowEqual(props5, w5.__owl__.currentFiber.props)) { - def4 = w5.__owl__.currentFiber.promise; - } else { - w5.destroy(); - w5 = false; - } + let def3; + let templateId4 = \`__5__\`; + let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; + let props4 = {}; + if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { + w4.destroy(); + w4 = false; } - if (!w5) { - let componentKey5 = \`Child\`; - let W5 = context.constructor.components[componentKey5] || QWeb.components[componentKey5]|| context['Child']; - if (!W5) {throw new Error('Cannot find the definition of component \\"' + componentKey5 + '\\"')} - w5 = new W5(parent, props5); - parent.__owl__.cmap[templateId5] = w5.__owl__.id; - def4 = w5.__prepare(extra.fiber, undefined, undefined); - def4 = def4.then(vnode=>{if (w5.__owl__.isDestroyed){return}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++; }});}};let pvnode=h(vnode.sel, {key: templateId5, hook: {insert(vn) {let nvn=w5.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w5.destroy();}}});c1[_3_index]=pvnode;w5.__owl__.pvnode = pvnode;}); + if (!w4) { + let componentKey4 = \`Child\`; + let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child']; + if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} + w4 = new W4(parent, props4); + parent.__owl__.cmap[templateId4] = w4.__owl__.id; + def3 = w4.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId4, 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 (w4.__owl__.isDestroyed) {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; } else { - def4 = def4 || w5.__updateProps(props5, extra.fiber, undefined, undefined); - def4 = def4.then(()=>{if (w5.__owl__.isDestroyed) {return};let pvnode=w5.__owl__.pvnode;c1[_3_index]=pvnode;}); + def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined, sibling); + let pvnode = w4.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def4); + sibling = w4.__owl__.currentFiber; return vn1; }" `; @@ -822,37 +783,37 @@ exports[`other directives with t-component t-on with no handler (only modifiers) let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`ComponentA\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['ComponentA']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`ComponentA\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['ComponentA']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {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; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -864,37 +825,37 @@ exports[`other directives with t-component t-on with prevent and self modifiers let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`Child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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; }});}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`Child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {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; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -906,37 +867,37 @@ exports[`other directives with t-component t-on with self and prevent modifiers let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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; }});}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {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; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -948,37 +909,37 @@ exports[`other directives with t-component t-on with self modifier 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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; }});}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {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; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -990,37 +951,37 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}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; }});}};let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {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; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -1028,6 +989,7 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie exports[`random stuff/miscellaneous can inject values in tagged templates 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1049,38 +1011,38 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let key5 = 'key' + 'somestring'; - let def3; - let templateId4 = \`__6__\` + key5; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {flag:context['state'].flag}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let key4 = 'key' + 'somestring'; + let def2; + let templateId3 = \`__5__\` + key4; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {flag:context['state'].flag}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -1092,6 +1054,7 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -1111,35 +1074,34 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument context.item = _3[i1]; context.item_value = _4[i1]; //COMPONENT - let key8 = 'key' + context['item']; - let def6; - let templateId7 = \`__9__\` + key8; - let arg10 = context['item']; - let w7 = templateId7 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId7]] : false; - let _5_index = c1.length; - c1.push(null); - let props7 = {}; - if (w7 && w7.__owl__.currentFiber && !w7.__owl__.vnode) { - if (utils.shallowEqual(props7, w7.__owl__.currentFiber.props)) { - def6 = w7.__owl__.currentFiber.promise; - } else { - w7.destroy(); - w7 = false; - } + let key7 = 'key' + context['item']; + let def5; + let templateId6 = \`__8__\` + key7; + let arg9 = context['item']; + let w6 = templateId6 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId6]] : false; + let props6 = {}; + if (w6 && w6.__owl__.currentFiber && !w6.__owl__.vnode) { + w6.destroy(); + w6 = false; } - if (!w7) { - let componentKey7 = \`Child\`; - let W7 = context.constructor.components[componentKey7] || QWeb.components[componentKey7]|| context['Child']; - if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')} - w7 = new W7(parent, props7); - parent.__owl__.cmap[templateId7] = w7.__owl__.id; - def6 = w7.__prepare(extra.fiber, undefined, undefined); - def6 = def6.then(vnode=>{if (w7.__owl__.isDestroyed){return}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; }});}};let pvnode=h(vnode.sel, {key: templateId7, hook: {insert(vn) {let nvn=w7.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});c1[_5_index]=pvnode;w7.__owl__.pvnode = pvnode;}); + if (!w6) { + let componentKey6 = \`Child\`; + let W6 = context.constructor.components[componentKey6] || QWeb.components[componentKey6]|| context['Child']; + if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')} + w6 = new W6(parent, props6); + parent.__owl__.cmap[templateId6] = w6.__owl__.id; + def5 = w6.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId6, hook: {insert(vn) { let nvn=w6.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w6.destroy();}}}); + const fiber = w6.__owl__.currentFiber; + def5.then(function () {if (w6.__owl__.isDestroyed) {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, arg9, e); } else { owner.onEv; }});}};}); + c1.push(pvnode); + w6.__owl__.pvnode = pvnode; } else { - def6 = def6 || w7.__updateProps(props7, extra.fiber, undefined, undefined); - def6 = def6.then(()=>{if (w7.__owl__.isDestroyed) {return};let pvnode=w7.__owl__.pvnode;c1[_5_index]=pvnode;}); + def5 = def5 || w6.__updateProps(props6, extra.fiber, undefined, undefined, sibling); + let pvnode = w6.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def6); + sibling = w6.__owl__.currentFiber; } return vn1; }" @@ -1148,6 +1110,7 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument exports[`t-model directive .lazy modifier 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1171,6 +1134,7 @@ exports[`t-model directive .lazy modifier 1`] = ` exports[`t-model directive basic use, on an input 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1194,6 +1158,7 @@ exports[`t-model directive basic use, on an input 1`] = ` exports[`t-model directive basic use, on another key in component 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1217,6 +1182,7 @@ exports[`t-model directive basic use, on another key in component 1`] = ` exports[`t-model directive on a select 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1261,6 +1227,7 @@ exports[`t-model directive on a select 1`] = ` exports[`t-model directive on a sub state key 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1284,6 +1251,7 @@ exports[`t-model directive on a sub state key 1`] = ` exports[`t-model directive on an input type=radio 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1320,6 +1288,7 @@ exports[`t-model directive on an input type=radio 1`] = ` exports[`t-model directive on an input, type=checkbox 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1350,38 +1319,38 @@ exports[`t-slot directive can define and call slots 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`Dialog\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Dialog']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - w4.__owl__.slotId = 1; - def3 = w4.__prepare(extra.fiber, {}, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`Dialog\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Dialog']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + w3.__owl__.slotId = 1; + def2 = w3.__prepare(extra.fiber, {}, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - def3 = def3 || w4.__updateProps(props4, extra.fiber, {}, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + def2 = def2 || w3.__updateProps(props3, extra.fiber, {}, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; @@ -1390,6 +1359,7 @@ exports[`t-slot directive can define and call slots 2`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1414,45 +1384,49 @@ exports[`t-slot directive can define and call slots 2`] = ` exports[`t-slot directive can define and call slots 3`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = extra.parentNode; Object.assign(context, extra.fiber.scope); - let c7 = [], p7 = {key:7}; - var vn7 = h('span', p7, c7); - c1.push(vn7); - c7.push({text: \`header\`}); + let c6 = [], p6 = {key:6}; + var vn6 = h('span', p6, c6); + c1.push(vn6); + c6.push({text: \`header\`}); }" `; exports[`t-slot directive can define and call slots 4`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = extra.parentNode; Object.assign(context, extra.fiber.scope); - let c7 = [], p7 = {key:7}; - var vn7 = h('span', p7, c7); - c1.push(vn7); - c7.push({text: \`footer\`}); + let c6 = [], p6 = {key:6}; + var vn6 = h('span', p6, c6); + c1.push(vn6); + c6.push({text: \`footer\`}); }" `; exports[`t-slot directive content is the default slot 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = extra.parentNode; Object.assign(context, extra.fiber.scope); - let c7 = [], p7 = {key:7}; - var vn7 = h('span', p7, c7); - c1.push(vn7); - c7.push({text: \`sts rocks\`}); + let c6 = [], p6 = {key:6}; + var vn6 = h('span', p6, c6); + c1.push(vn6); + c6.push({text: \`sts rocks\`}); }" `; exports[`t-slot directive default slot work with text nodes 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = extra.parentNode; Object.assign(context, extra.fiber.scope); @@ -1463,34 +1437,36 @@ exports[`t-slot directive default slot work with text nodes 1`] = ` exports[`t-slot directive multiple roots are allowed in a default slot 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = extra.parentNode; Object.assign(context, extra.fiber.scope); + let c6 = [], p6 = {key:6}; + var vn6 = h('span', p6, c6); + c1.push(vn6); + c6.push({text: \`sts\`}); let c7 = [], p7 = {key:7}; var vn7 = h('span', p7, c7); c1.push(vn7); - c7.push({text: \`sts\`}); - let c8 = [], p8 = {key:8}; - var vn8 = h('span', p8, c8); - c1.push(vn8); - c8.push({text: \`rocks\`}); + c7.push({text: \`rocks\`}); }" `; exports[`t-slot directive multiple roots are allowed in a named slot 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = extra.parentNode; Object.assign(context, extra.fiber.scope); + let c6 = [], p6 = {key:6}; + var vn6 = h('span', p6, c6); + c1.push(vn6); + c6.push({text: \`sts\`}); let c7 = [], p7 = {key:7}; var vn7 = h('span', p7, c7); c1.push(vn7); - c7.push({text: \`sts\`}); - let c8 = [], p8 = {key:8}; - var vn8 = h('span', p8, c8); - c1.push(vn8); - c8.push({text: \`rocks\`}); + c7.push({text: \`rocks\`}); }" `; @@ -1499,24 +1475,25 @@ exports[`t-slot directive refs are properly bound in slots 1`] = ` ) { let owner = context; context.__owl__.refs = context.__owl__.refs || {}; + let sibling = null; var h = this.h; let c1 = extra.parentNode; Object.assign(context, extra.fiber.scope); - let c11 = [], p11 = {key:11,on:{}}; - var vn11 = h('button', p11, c11); - c1.push(vn11); - extra.handlers['click' + 11] = extra.handlers['click' + 11] || function (e) {const fn = context['doSomething'];if (fn) { fn.call(owner, e); } else { context.doSomething; }}; - p11.on['click'] = extra.handlers['click' + 11]; - const ref12 = \`myButton\`; - p11.hook = { + let c10 = [], p10 = {key:10,on:{}}; + var vn10 = h('button', p10, c10); + c1.push(vn10); + extra.handlers['click' + 10] = extra.handlers['click' + 10] || function (e) {const fn = context['doSomething'];if (fn) { fn.call(owner, e); } else { context.doSomething; }}; + p10.on['click'] = extra.handlers['click' + 10]; + const ref11 = \`myButton\`; + p10.hook = { create: (_, n) => { - context.__owl__.refs[ref12] = n.elm; + context.__owl__.refs[ref11] = n.elm; }, destroy: () => { - delete context.__owl__.refs[ref12]; + delete context.__owl__.refs[ref11]; }, }; - c11.push({text: \`do something\`}); + c10.push({text: \`do something\`}); }" `; @@ -1524,15 +1501,16 @@ exports[`t-slot directive slots are rendered with proper context 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = extra.parentNode; Object.assign(context, extra.fiber.scope); - let c11 = [], p11 = {key:11,on:{}}; - var vn11 = h('button', p11, c11); - c1.push(vn11); - extra.handlers['click' + 11] = extra.handlers['click' + 11] || function (e) {const fn = context['doSomething'];if (fn) { fn.call(owner, e); } else { context.doSomething; }}; - p11.on['click'] = extra.handlers['click' + 11]; - c11.push({text: \`do something\`}); + let c10 = [], p10 = {key:10,on:{}}; + var vn10 = h('button', p10, c10); + c1.push(vn10); + extra.handlers['click' + 10] = extra.handlers['click' + 10] || function (e) {const fn = context['doSomething'];if (fn) { fn.call(owner, e); } else { context.doSomething; }}; + p10.on['click'] = extra.handlers['click' + 10]; + c10.push({text: \`do something\`}); }" `; @@ -1540,6 +1518,7 @@ exports[`t-slot directive slots are rendered with proper context, part 2 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; var _1 = context['props'].to; let c2 = [], p2 = {key:2,attrs:{href: _1}}; @@ -1559,6 +1538,7 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; context = Object.create(context); const scope = Object.create(null); var h = this.h; @@ -1591,34 +1571,33 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = ` var vn6 = h('li', p6, c6); c2.push(vn6); //COMPONENT - let def8; - let templateId9 = \`__10__\` + nodeKey6; - let w9 = templateId9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId9]] : false; - let _7_index = c6.length; - c6.push(null); - let props9 = {to:'/user/'+context['user'].id}; - if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) { - if (utils.shallowEqual(props9, w9.__owl__.currentFiber.props)) { - def8 = w9.__owl__.currentFiber.promise; - } else { - w9.destroy(); - w9 = false; - } + let def7; + let templateId8 = \`__9__\` + nodeKey6; + let w8 = templateId8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId8]] : false; + let props8 = {to:'/user/'+context['user'].id}; + if (w8 && w8.__owl__.currentFiber && !w8.__owl__.vnode) { + w8.destroy(); + w8 = false; } - if (!w9) { - let componentKey9 = \`Link\`; - let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| context['Link']; - if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')} - w9 = new W9(parent, props9); - parent.__owl__.cmap[templateId9] = w9.__owl__.id; - w9.__owl__.slotId = 1; - def8 = w9.__prepare(extra.fiber, Object.assign({}, scope), undefined); - def8 = def8.then(vnode=>{if (w9.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId9, hook: {insert(vn) {let nvn=w9.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w9.destroy();}}});c6[_7_index]=pvnode;w9.__owl__.pvnode = pvnode;}); + if (!w8) { + let componentKey8 = \`Link\`; + let W8 = context.constructor.components[componentKey8] || QWeb.components[componentKey8]|| context['Link']; + if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')} + w8 = new W8(parent, props8); + parent.__owl__.cmap[templateId8] = w8.__owl__.id; + w8.__owl__.slotId = 1; + def7 = w8.__prepare(extra.fiber, Object.assign({}, scope), undefined, sibling); + let pvnode = h('dummy', {key: templateId8, hook: {insert(vn) { let nvn=w8.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w8.destroy();}}}); + const fiber = w8.__owl__.currentFiber; + def7.then(function () {if (w8.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c6.push(pvnode); + w8.__owl__.pvnode = pvnode; } else { - def8 = def8 || w9.__updateProps(props9, extra.fiber, Object.assign({}, scope), undefined); - def8 = def8.then(()=>{if (w9.__owl__.isDestroyed) {return};let pvnode=w9.__owl__.pvnode;c6[_7_index]=pvnode;}); + def7 = def7 || w8.__updateProps(props8, extra.fiber, Object.assign({}, scope), undefined, sibling); + let pvnode = w8.__owl__.pvnode; + c6.push(pvnode); } - extra.promises.push(def8); + sibling = w8.__owl__.currentFiber; } return vn1; }" @@ -1627,13 +1606,14 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = ` exports[`t-slot directive slots are rendered with proper context, part 2 3`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c6 = extra.parentNode; Object.assign(context, extra.fiber.scope); c6.push({text: \`User \`}); - var _12 = context['user'].name; - if (_12 || _12 === 0) { - c6.push({text: _12}); + var _11 = context['user'].name; + if (_11 || _11 === 0) { + c6.push({text: _11}); } }" `; @@ -1642,6 +1622,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; var _1 = context['props'].to; let c2 = [], p2 = {key:2,attrs:{href: _1}}; @@ -1661,6 +1642,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; context = Object.create(context); const scope = Object.create(null); var h = this.h; @@ -1694,34 +1676,33 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = ` c2.push(vn6); var _7 = 'User '+context['user'].name; //COMPONENT - let def9; - let templateId10 = \`__11__\` + nodeKey6; - let w10 = templateId10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId10]] : false; - let _8_index = c6.length; - c6.push(null); - let props10 = {to:'/user/'+context['user'].id}; - if (w10 && w10.__owl__.currentFiber && !w10.__owl__.vnode) { - if (utils.shallowEqual(props10, w10.__owl__.currentFiber.props)) { - def9 = w10.__owl__.currentFiber.promise; - } else { - w10.destroy(); - w10 = false; - } + let def8; + let templateId9 = \`__10__\` + nodeKey6; + let w9 = templateId9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId9]] : false; + let props9 = {to:'/user/'+context['user'].id}; + if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) { + w9.destroy(); + w9 = false; } - if (!w10) { - let componentKey10 = \`Link\`; - let W10 = context.constructor.components[componentKey10] || QWeb.components[componentKey10]|| context['Link']; - if (!W10) {throw new Error('Cannot find the definition of component \\"' + componentKey10 + '\\"')} - w10 = new W10(parent, props10); - parent.__owl__.cmap[templateId10] = w10.__owl__.id; - w10.__owl__.slotId = 1; - def9 = w10.__prepare(extra.fiber, Object.assign({}, scope), {_7}); - def9 = def9.then(vnode=>{if (w10.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId10, hook: {insert(vn) {let nvn=w10.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w10.destroy();}}});c6[_8_index]=pvnode;w10.__owl__.pvnode = pvnode;}); + if (!w9) { + let componentKey9 = \`Link\`; + let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| context['Link']; + if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')} + w9 = new W9(parent, props9); + parent.__owl__.cmap[templateId9] = w9.__owl__.id; + w9.__owl__.slotId = 1; + def8 = w9.__prepare(extra.fiber, Object.assign({}, scope), {_7}, sibling); + let pvnode = h('dummy', {key: templateId9, 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 (w9.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c6.push(pvnode); + w9.__owl__.pvnode = pvnode; } else { - def9 = def9 || w10.__updateProps(props10, extra.fiber, Object.assign({}, scope), {_7}); - def9 = def9.then(()=>{if (w10.__owl__.isDestroyed) {return};let pvnode=w10.__owl__.pvnode;c6[_8_index]=pvnode;}); + def8 = def8 || w9.__updateProps(props9, extra.fiber, Object.assign({}, scope), {_7}, sibling); + let pvnode = w9.__owl__.pvnode; + c6.push(pvnode); } - extra.promises.push(def9); + sibling = w9.__owl__.currentFiber; } return vn1; }" @@ -1730,6 +1711,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = ` exports[`t-slot directive slots are rendered with proper context, part 3 3`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c6 = extra.parentNode; let _7 = extra.fiber.vars._7 @@ -1747,39 +1729,39 @@ exports[`t-slot directive slots are rendered with proper context, part 4 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); var _2 = 'User '+context['state'].user.name; //COMPONENT - let def4; - let templateId5 = \`__6__\`; - let w5 = templateId5 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId5]] : false; - let _3_index = c1.length; - c1.push(null); - let props5 = {to:'/user/'+context['state'].user.id}; - if (w5 && w5.__owl__.currentFiber && !w5.__owl__.vnode) { - if (utils.shallowEqual(props5, w5.__owl__.currentFiber.props)) { - def4 = w5.__owl__.currentFiber.promise; - } else { - w5.destroy(); - w5 = false; - } + let def3; + let templateId4 = \`__5__\`; + let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; + let props4 = {to:'/user/'+context['state'].user.id}; + if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { + w4.destroy(); + w4 = false; } - if (!w5) { - let componentKey5 = \`Link\`; - let W5 = context.constructor.components[componentKey5] || QWeb.components[componentKey5]|| context['Link']; - if (!W5) {throw new Error('Cannot find the definition of component \\"' + componentKey5 + '\\"')} - w5 = new W5(parent, props5); - parent.__owl__.cmap[templateId5] = w5.__owl__.id; - w5.__owl__.slotId = 1; - def4 = w5.__prepare(extra.fiber, {}, {_2}); - def4 = def4.then(vnode=>{if (w5.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId5, hook: {insert(vn) {let nvn=w5.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w5.destroy();}}});c1[_3_index]=pvnode;w5.__owl__.pvnode = pvnode;}); + if (!w4) { + let componentKey4 = \`Link\`; + let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Link']; + if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} + w4 = new W4(parent, props4); + parent.__owl__.cmap[templateId4] = w4.__owl__.id; + w4.__owl__.slotId = 1; + def3 = w4.__prepare(extra.fiber, {}, {_2}, sibling); + let pvnode = h('dummy', {key: templateId4, 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 (w4.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w4.__owl__.pvnode = pvnode; } else { - def4 = def4 || w5.__updateProps(props5, extra.fiber, {}, {_2}); - def4 = def4.then(()=>{if (w5.__owl__.isDestroyed) {return};let pvnode=w5.__owl__.pvnode;c1[_3_index]=pvnode;}); + def3 = def3 || w4.__updateProps(props4, extra.fiber, {}, {_2}, sibling); + let pvnode = w4.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def4); + sibling = w4.__owl__.currentFiber; return vn1; }" `; @@ -1787,6 +1769,7 @@ exports[`t-slot directive slots are rendered with proper context, part 4 1`] = ` exports[`t-slot directive slots are rendered with proper context, part 4 2`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = extra.parentNode; let _2 = extra.fiber.vars._2 @@ -1802,6 +1785,7 @@ exports[`t-slot directive template can just return a slot 1`] = ` ) { let utils = this.constructor.utils; let owner = context; + let sibling = null; let result; var h = this.h; const slot1 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; @@ -1809,7 +1793,7 @@ exports[`t-slot directive template can just return a slot 1`] = ` let children2= [] result = {} slot1.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: children2, vars: extra.vars, parent: owner})); - Promise.all(extra.promises).then(() => utils.defineProxy(result, children2[0])) + utils.defineProxy(result, children2[0]); } return result; }" @@ -1822,36 +1806,38 @@ exports[`top level sub widgets basic use 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; let result; var h = this.h; //COMPONENT - let def2; - let templateId3 = \`__4__\`; - let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; - let vn5 = {}; - result = vn5; - let props3 = {p:1}; - if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { - if (utils.shallowEqual(props3, w3.__owl__.currentFiber.props)) { - def2 = w3.__owl__.currentFiber.promise; - } else { - w3.destroy(); - w3 = false; - } + let def1; + let templateId2 = \`__3__\`; + let w2 = templateId2 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId2]] : false; + let vn4 = {}; + result = vn4; + let props2 = {p:1}; + if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) { + w2.destroy(); + w2 = false; } - if (!w3) { - let componentKey3 = \`Child\`; - let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child']; - if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} - w3 = new W3(parent, props3); - parent.__owl__.cmap[templateId3] = w3.__owl__.id; - def2 = w3.__prepare(extra.fiber, undefined, undefined); - def2 = def2.then(vnode=>{if (w3.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId3, hook: {insert(vn) {let nvn=w3.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});utils.defineProxy(vn5, pvnode);w3.__owl__.pvnode = pvnode;}); + if (!w2) { + let componentKey2 = \`Child\`; + let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| context['Child']; + if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} + w2 = new W2(parent, props2); + parent.__owl__.cmap[templateId2] = w2.__owl__.id; + def1 = w2.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId2, 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 (w2.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + utils.defineProxy(vn4, pvnode); + w2.__owl__.pvnode = pvnode; } else { - def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined); - def2 = def2.then(()=>{if (w3.__owl__.isDestroyed) {return};let pvnode=w3.__owl__.pvnode;utils.defineProxy(vn5, pvnode);}); + def1 = def1 || w2.__updateProps(props2, extra.fiber, undefined, undefined, sibling); + let pvnode = w2.__owl__.pvnode; + utils.defineProxy(vn4, pvnode); } - extra.promises.push(def2); + sibling = w2.__owl__.currentFiber; return result; }" `; @@ -1863,67 +1849,70 @@ exports[`top level sub widgets can select a sub widget 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; let result; var h = this.h; if (context['env'].flag) { //COMPONENT - let def2; - let templateId3 = \`__4__\`; - let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; - let vn5 = {}; - result = vn5; - let props3 = {}; - if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { - if (utils.shallowEqual(props3, w3.__owl__.currentFiber.props)) { - def2 = w3.__owl__.currentFiber.promise; - } else { - w3.destroy(); - w3 = false; - } + let def1; + let templateId2 = \`__3__\`; + let w2 = templateId2 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId2]] : false; + let vn4 = {}; + result = vn4; + let props2 = {}; + if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) { + w2.destroy(); + w2 = false; } - if (!w3) { - let componentKey3 = \`Child\`; - let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child']; - if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} - w3 = new W3(parent, props3); - parent.__owl__.cmap[templateId3] = w3.__owl__.id; - def2 = w3.__prepare(extra.fiber, undefined, undefined); - def2 = def2.then(vnode=>{if (w3.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId3, hook: {insert(vn) {let nvn=w3.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});utils.defineProxy(vn5, pvnode);w3.__owl__.pvnode = pvnode;}); + if (!w2) { + let componentKey2 = \`Child\`; + let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| context['Child']; + if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} + w2 = new W2(parent, props2); + parent.__owl__.cmap[templateId2] = w2.__owl__.id; + def1 = w2.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId2, 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 (w2.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + utils.defineProxy(vn4, pvnode); + w2.__owl__.pvnode = pvnode; } else { - def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined); - def2 = def2.then(()=>{if (w3.__owl__.isDestroyed) {return};let pvnode=w3.__owl__.pvnode;utils.defineProxy(vn5, pvnode);}); + def1 = def1 || w2.__updateProps(props2, extra.fiber, undefined, undefined, sibling); + let pvnode = w2.__owl__.pvnode; + utils.defineProxy(vn4, pvnode); } - extra.promises.push(def2); + sibling = w2.__owl__.currentFiber; } if (!context['env'].flag) { //COMPONENT - let def7; - let templateId8 = \`__9__\`; - let w8 = templateId8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId8]] : false; - let vn10 = {}; - result = vn10; - let props8 = {}; - if (w8 && w8.__owl__.currentFiber && !w8.__owl__.vnode) { - if (utils.shallowEqual(props8, w8.__owl__.currentFiber.props)) { - def7 = w8.__owl__.currentFiber.promise; - } else { - w8.destroy(); - w8 = false; - } + let def5; + let templateId6 = \`__7__\`; + let w6 = templateId6 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId6]] : false; + let vn8 = {}; + result = vn8; + let props6 = {}; + if (w6 && w6.__owl__.currentFiber && !w6.__owl__.vnode) { + w6.destroy(); + w6 = false; } - if (!w8) { - let componentKey8 = \`OtherChild\`; - let W8 = context.constructor.components[componentKey8] || QWeb.components[componentKey8]|| context['OtherChild']; - if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')} - w8 = new W8(parent, props8); - parent.__owl__.cmap[templateId8] = w8.__owl__.id; - def7 = w8.__prepare(extra.fiber, undefined, undefined); - def7 = def7.then(vnode=>{if (w8.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId8, hook: {insert(vn) {let nvn=w8.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w8.destroy();}}});utils.defineProxy(vn10, pvnode);w8.__owl__.pvnode = pvnode;}); + if (!w6) { + let componentKey6 = \`OtherChild\`; + let W6 = context.constructor.components[componentKey6] || QWeb.components[componentKey6]|| context['OtherChild']; + if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')} + w6 = new W6(parent, props6); + parent.__owl__.cmap[templateId6] = w6.__owl__.id; + def5 = w6.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId6, hook: {insert(vn) { let nvn=w6.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w6.destroy();}}}); + const fiber = w6.__owl__.currentFiber; + def5.then(function () {if (w6.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + utils.defineProxy(vn8, pvnode); + w6.__owl__.pvnode = pvnode; } else { - def7 = def7 || w8.__updateProps(props8, extra.fiber, undefined, undefined); - def7 = def7.then(()=>{if (w8.__owl__.isDestroyed) {return};let pvnode=w8.__owl__.pvnode;utils.defineProxy(vn10, pvnode);}); + def5 = def5 || w6.__updateProps(props6, extra.fiber, undefined, undefined, sibling); + let pvnode = w6.__owl__.pvnode; + utils.defineProxy(vn8, pvnode); } - extra.promises.push(def7); + sibling = w6.__owl__.currentFiber; } return result; }" diff --git a/tests/component/__snapshots__/props_validation.test.ts.snap b/tests/component/__snapshots__/props_validation.test.ts.snap index d8a7ecd3..e56fac8e 100644 --- a/tests/component/__snapshots__/props_validation.test.ts.snap +++ b/tests/component/__snapshots__/props_validation.test.ts.snap @@ -1,9 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`default props default values are also set whenever component is updated 1`] = `"
1
"`; - -exports[`default props default values are also set whenever component is updated 2`] = `"
4
"`; - exports[`props validation props are validated in dev mode (code snapshot) 1`] = ` "function anonymous(context,extra ) { @@ -11,38 +7,38 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] = let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); //COMPONENT - let def3; - let templateId4 = \`__5__\`; - let w4 = templateId4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId4]] : false; - let _2_index = c1.length; - c1.push(null); - let props4 = {message:1}; - if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) { - if (utils.shallowEqual(props4, w4.__owl__.currentFiber.props)) { - def3 = w4.__owl__.currentFiber.promise; - } else { - w4.destroy(); - w4 = false; - } + let def2; + let templateId3 = \`__4__\`; + let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; + let props3 = {message:1}; + if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { + w3.destroy(); + w3 = false; } - if (!w4) { - let componentKey4 = \`Child\`; - let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| context['Child']; - if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')} - w4 = new W4(parent, props4); - parent.__owl__.cmap[templateId4] = w4.__owl__.id; - def3 = w4.__prepare(extra.fiber, undefined, undefined); - def3 = def3.then(vnode=>{if (w4.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;}); + if (!w3) { + let componentKey3 = \`Child\`; + let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child']; + if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} + w3 = new W3(parent, props3); + parent.__owl__.cmap[templateId3] = w3.__owl__.id; + def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId3, 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 (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + c1.push(pvnode); + w3.__owl__.pvnode = pvnode; } else { - utils.validateProps(w4.constructor, props4) - def3 = def3 || w4.__updateProps(props4, extra.fiber, undefined, undefined); - def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;}); + utils.validateProps(w3.constructor, props3) + def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); + let pvnode = w3.__owl__.pvnode; + c1.push(pvnode); } - extra.promises.push(def3); + sibling = w3.__owl__.currentFiber; return vn1; }" `; diff --git a/tests/component/component.test.ts b/tests/component/component.test.ts index 6bba1ec1..f8da6bfd 100644 --- a/tests/component/component.test.ts +++ b/tests/component/component.test.ts @@ -81,9 +81,13 @@ describe("basic widget properties", () => { }); test("can be mounted", async () => { - const widget = new Widget(env); - await widget.mount(fixture); - expect(fixture.innerHTML).toBe("
"); + class SomeWidget extends Component { + static template = xml`
content
`; + } + const widget = new SomeWidget(env); + widget.mount(fixture); + await nextTick(); + expect(fixture.innerHTML).toBe("
content
"); }); test("crashes if it cannot find a template", async () => { @@ -98,10 +102,11 @@ describe("basic widget properties", () => { test("can be clicked on and updated", async () => { const counter = new Counter(env); - await counter.mount(fixture); + counter.mount(fixture); + await nextTick(); expect(fixture.innerHTML).toBe("
0
"); const button = (counter.el).getElementsByTagName("button")[0]; - await button.click(); + button.click(); await nextTick(); expect(fixture.innerHTML).toBe("
1
"); }); @@ -112,7 +117,7 @@ describe("basic widget properties", () => { await counter.mount(target); expect(target.innerHTML).toBe("
0
"); const button = (counter.el).getElementsByTagName("button")[0]; - await button.click(); + button.click(); await nextTick(); expect(target.innerHTML).toBe("
0
"); expect(counter.state.counter).toBe(1); @@ -130,20 +135,62 @@ describe("basic widget properties", () => { }); test("changing state before first render does not trigger a render", async () => { - let renderCalls = 0; + const steps: string[] = []; class TestW extends Widget { state = useState({ drinks: 1 }); async willStart() { this.state.drinks++; } async __render(f) { - renderCalls++; + steps.push("__render"); return super.__render(f); } + mounted() { + steps.push("mounted"); + } + patched() { + steps.push("patched"); + } } const widget = new TestW(env); await widget.mount(fixture); - expect(renderCalls).toBe(1); + expect(steps).toEqual(["__render", "mounted"]); + }); + + test("changing state before first render does not trigger a render (with parent)", async () => { + const steps: string[] = []; + class TestW extends Component { + static template = xml`W`; + state = useState({ drinks: 1 }); + async willStart() { + this.state.drinks++; + } + async __render(f) { + steps.push("__render"); + return super.__render(f); + } + mounted() { + steps.push("mounted"); + } + patched() { + steps.push("patched"); + } + } + class Parent extends Component { + state = useState({ flag: false }); + static components = { TestW }; + static template = xml`
`; + } + const parent = new Parent(env); + await parent.mount(fixture); + + expect(fixture.innerHTML).toBe("
"); + expect(steps).toEqual([]); + + parent.state.flag = true; + await nextTick(); + expect(fixture.innerHTML).toBe("
W
"); + expect(steps).toEqual(["__render", "mounted"]); }); test("keeps a reference to env", async () => { @@ -157,7 +204,7 @@ describe("basic widget properties", () => { expect(fixture.innerHTML).toBe(`
`); widget.el!.appendChild(document.createElement("span")); expect(fixture.innerHTML).toBe(`
`); - widget.render(); + widget.render(); // FIXME? expect(fixture.innerHTML).toBe(`
`); }); @@ -447,15 +494,13 @@ describe("lifecycle hooks", () => { expect(steps).toEqual(["child:willStart", "child:mounted"]); }); - test("mounted hook is correctly called on subcomponents created in mounted hook", async done => { + test("mounted hook is correctly called on subcomponents created in mounted hook", async () => { // the issue here is that the parent widget creates in the // mounted hook a new widget, which means that it modifies // in place its list of children. But this list of children is currently // being visited, so the mount action of the parent could cause a mount // action of the new child widget, even though it is not ready yet. expect.assertions(1); - const target = document.createElement("div"); - document.body.appendChild(target); class ParentWidget extends Widget { mounted() { const child = new ChildWidget(this); @@ -465,11 +510,11 @@ describe("lifecycle hooks", () => { class ChildWidget extends Widget { mounted() { expect(this.el).toBeTruthy(); - done(); } } const widget = new ParentWidget(env); - await widget.mount(target); + await widget.mount(fixture); // wait for ParentWidget + await nextTick(); // wait for ChildWidget }); test("components are unmounted and destroyed if no longer in DOM", async () => { @@ -966,6 +1011,30 @@ describe("composition", () => { expect(fixture.innerHTML).toBe("child b"); }); + test("can use dynamic components (the class) if given (with different root tagname)", async () => { + class A extends Component { + static template = xml`child a`; + } + class B extends Component { + static template = xml`
child b
`; + } + class App extends Component { + static template = xml``; + state = useState({ + child: "a" + }); + get myComponent() { + return this.state.child === "a" ? A : B; + } + } + const widget = new App(env); + await widget.mount(fixture); + expect(fixture.innerHTML).toBe("child a"); + widget.state.child = "b"; + await nextTick(); + expect(fixture.innerHTML).toBe("
child b
"); + }); + test("don't fallback to global registry if widget defined locally", async () => { QWeb.registerComponent("WidgetB", WidgetB); // should not use this widget env.qweb.addTemplate("ParentWidget", `
`); @@ -1363,26 +1432,32 @@ describe("composition", () => { test("sub components between t-ifs", async () => { // this confuses the patching algorithm... - env.qweb.addTemplate("ChildWidget", `child`); - class ChildWidget extends Widget {} - - env.qweb.addTemplate( - "Parent", - `
-

hey

-

noo

- - test -
` - ); + class ChildWidget extends Widget { + static template = xml`child`; + } class Parent extends Widget { - state = useState({ flag: false }); + static template = xml` +
+

hey

+

noo

+ + test +
`; static components = { ChildWidget }; + state = useState({ flag: false }); } const parent = new Parent(env); await parent.mount(fixture); const child = children(parent)[0]; + expect(normalize(fixture.innerHTML)).toBe( + normalize(` +
+

noo

+ child +
+ `) + ); parent.state.flag = true; await nextTick(); @@ -1547,40 +1622,31 @@ describe("props evaluation ", () => { describe("class and style attributes with t-component", () => { test("class is properly added on widget root el", async () => { - env.qweb.addTemplate( - "ParentWidget", - ` -
- -
` - ); - class Child extends Widget {} - class ParentWidget extends Widget { - static components = { child: Child }; + class Child extends Widget { + static template = xml`
`; + } + class ParentWidget extends Widget { + static template = xml`
`; + static components = { Child }; } - env.qweb.addTemplate("Child", `
`); const widget = new ParentWidget(env); await widget.mount(fixture); expect(fixture.innerHTML).toBe(`
`); }); test("t-att-class is properly added/removed on widget root el", async () => { - env.qweb.addTemplate( - "ParentWidget", - `
- -
` - ); - class Child extends Widget {} + class Child extends Widget { + static template = xml`
`; + } class ParentWidget extends Widget { - static components = { child: Child }; + static template = xml`
`; + static components = { Child }; state = useState({ a: true, b: false }); } - env.qweb.addTemplate("Child", `
`); const widget = new ParentWidget(env); await widget.mount(fixture); expect(fixture.innerHTML).toBe(`
`); - expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot(); + expect(QWeb.TEMPLATES[ParentWidget.template].fn.toString()); widget.state.a = false; widget.state.b = true; @@ -2464,9 +2530,8 @@ describe("async rendering", () => { test("destroying/recreating a subwidget with different props (if start is not over)", async () => { let def = makeDeferred(); let n = 0; - env.qweb.addTemplate("W", `
`); - env.qweb.addTemplate("Child", `child:`); class Child extends Widget { + static template = xml`child:`; constructor(parent, props) { super(parent, props); n++; @@ -2476,6 +2541,7 @@ describe("async rendering", () => { } } class W extends Widget { + static template = xml`
`; static components = { Child }; state = useState({ val: 1 }); } @@ -2499,95 +2565,172 @@ describe("async rendering", () => { test("creating two async components, scenario 1", async () => { let defA = makeDeferred(); let defB = makeDeferred(); + let nbRenderings: number = 0; - env.qweb.addTemplate("ChildA", "a"); - class ChildA extends Widget { + class ChildA extends Component { + static template = xml``; willStart(): Promise { return defA; } + getValue() { + nbRenderings++; + return "a"; + } } - env.qweb.addTemplate("ChildB", "b"); - class ChildB extends Widget { + class ChildB extends Component { + static template = xml`b`; willStart(): Promise { return defB; } } - env.qweb.addTemplate( - "Parent", - ` + class Parent extends Component { + static template = xml`
-
` - ); - class Parent extends Widget { +
`; static components = { ChildA, ChildB }; state = useState({ flagA: false, flagB: false }); } const parent = new Parent(env); await parent.mount(fixture); - expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("
"); + expect(fixture.innerHTML).toBe("
"); parent.state.flagA = true; await nextTick(); - expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("
"); + expect(fixture.innerHTML).toBe("
"); parent.state.flagB = true; await nextTick(); - expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("
"); + expect(fixture.innerHTML).toBe("
"); defB.resolve(); - expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("
"); + await nextTick(); + expect(fixture.innerHTML).toBe("
"); + expect(nbRenderings).toBe(0); defA.resolve(); await nextTick(); - expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe( - "
ab
" - ); + expect(fixture.innerHTML).toBe("
ab
"); + expect(nbRenderings).toBe(1); }); test("creating two async components, scenario 2", async () => { let defA = makeDeferred(); let defB = makeDeferred(); - env.qweb.addTemplate("ChildA", `a`); - class ChildA extends Widget { - __updateProps(props, forceUpdate, fiber): Promise { - return defA.then(() => super.__updateProps(props, forceUpdate, fiber)); + class ChildA extends Component { + static template = xml`a`; + willUpdateProps(): Promise { + return defA; } } - env.qweb.addTemplate("ChildB", `b`); - class ChildB extends Widget { + class ChildB extends Component { + static template = xml`b`; willStart(): Promise { return defB; } } - env.qweb.addTemplate( - "Parent", - ` + class Parent extends Component { + static template = xml`
-
` - ); - class Parent extends Widget { +
`; static components = { ChildA, ChildB }; state = useState({ valA: 1, valB: 2, flagB: false }); } const parent = new Parent(env); await parent.mount(fixture); - expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("
a1
"); + expect(fixture.innerHTML).toBe("
a1
"); parent.state.valA = 2; await nextTick(); - expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("
a1
"); + expect(fixture.innerHTML).toBe("
a1
"); parent.state.flagB = true; await nextTick(); - expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("
a1
"); + expect(fixture.innerHTML).toBe("
a1
"); defB.resolve(); - expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("
a1
"); + await nextTick(); + expect(fixture.innerHTML).toBe("
a1
"); defA.resolve(); await nextTick(); - expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe( - "
a2b2
" - ); + expect(fixture.innerHTML).toBe("
a2b2
"); + }); + + test("creating two async components, scenario 3 (patching in the same frame)", async () => { + let defA = makeDeferred(); + let defB = makeDeferred(); + + class ChildA extends Component { + static template = xml`a`; + willUpdateProps(): Promise { + return defA; + } + } + class ChildB extends Component { + static template = xml`b`; + willStart(): Promise { + return defB; + } + } + + class Parent extends Component { + static template = xml` +
+ + +
`; + static components = { ChildA, ChildB }; + state = useState({ valA: 1, valB: 2, flagB: false }); + } + const parent = new Parent(env); + await parent.mount(fixture); + expect(fixture.innerHTML).toBe("
a1
"); + parent.state.valA = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("
a1
"); + parent.state.flagB = true; + await nextTick(); + expect(fixture.innerHTML).toBe("
a1
"); + defB.resolve(); + expect(fixture.innerHTML).toBe("
a1
"); + defA.resolve(); + await nextTick(); + expect(fixture.innerHTML).toBe("
a2b2
"); + }); + + test("update a sub-component twice in the same frame", async () => { + const steps: string[] = []; + const defs = [makeDeferred(), makeDeferred()]; + let index = 0; + class ChildA extends Component { + static template = xml``; + willUpdateProps(): Promise { + return defs[index++]; + } + patched() { + steps.push('patched'); + } + } + + class Parent extends Component { + static template = xml`
`; + static components = { ChildA }; + state = useState({ valA: 1 }); + } + const parent = new Parent(env); + await parent.mount(fixture); + expect(fixture.innerHTML).toBe("
1
"); + parent.state.valA = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("
1
"); + parent.state.valA = 3; + await nextTick(); + expect(fixture.innerHTML).toBe("
1
"); + defs[0].resolve(); + await Promise.resolve(); + defs[1].resolve(); + await nextTick(); + expect(fixture.innerHTML).toBe("
3
"); + expect(steps).toEqual(['patched']); }); test("components in a node in a t-foreach ", async () => { @@ -2622,9 +2765,8 @@ describe("async rendering", () => { }); test("properly behave when destroyed/unmounted while rendering ", async () => { - let def = Promise.resolve(); + const def = makeDeferred(); - env.qweb.addTemplate("Child", `
`); class SubChild extends Widget { willPatch() { throw new Error("Should not happen!"); @@ -2632,28 +2774,18 @@ describe("async rendering", () => { patched() { throw new Error("Should not happen!"); } + willUpdateProps() { + return def; + } } class Child extends Widget { + static template = xml`
`; static components = { SubChild }; - mounted() { - // from now on, each rendering in child widget will be delayed (see - // __render) - def = makeDeferred(); - } - async __render(f) { - const result = await super.__render(f); - await def; - return result; - } } - env.qweb.addTemplate( - "Parent", - ` -
` - ); class Parent extends Widget { + static template = xml`
`; static components = { Child }; state = useState({ flag: true, val: "Framboise Lindemans" }); } @@ -2665,11 +2797,13 @@ describe("async rendering", () => { // because child is now waiting for def to be resolved parent.state.val = "Framboise Girardin"; await nextTick(); + expect(fixture.innerHTML).toBe("
"); - // with this, we remove child, and childchild, even though it is not finished + // with this, we remove child, and subchild, even though it is not finished // rendering from previous changes parent.state.flag = false; await nextTick(); + expect(fixture.innerHTML).toBe("
"); // we now resolve def, so the child rendering is now complete. (def).resolve(); @@ -2677,7 +2811,8 @@ describe("async rendering", () => { expect(fixture.innerHTML).toBe("
"); }); - test("reuse widget if possible, in some async situation", async () => { + test.skip("reuse widget if possible, in some async situation", async () => { + // this optimization has been temporarily deactivated env.qweb.addTemplates(` a @@ -2722,9 +2857,16 @@ describe("async rendering", () => { }); test("rendering component again in next microtick", async () => { - class Child extends Widget {} + class Child extends Widget { + static template = xml`
Child
`; + } class App extends Widget { + static template = xml` +
+ + +
`; static components = { Child }; async onClick() { @@ -2735,17 +2877,6 @@ describe("async rendering", () => { } } - env.qweb.addTemplates(` - -
Child
- -
- - -
-
- `); - const app = new App(env); await app.mount(fixture); expect(fixture.innerHTML).toBe("
"); @@ -2753,12 +2884,468 @@ describe("async rendering", () => { await nextTick(); expect(fixture.innerHTML).toBe("
Child
"); }); + + test("concurrent renderings scenario 1", async () => { + const def = makeDeferred(); + let stateB; + + class ComponentC extends Component { + static template = xml``; + someValue() { + return this.props.fromB; + } + willUpdateProps() { + return def; + } + } + ComponentC.prototype.someValue = jest.fn(ComponentC.prototype.someValue); + + class ComponentB extends Component { + static components = { ComponentC }; + static template = xml`

`; + state = useState({ fromB: "b" }); + + constructor(parent, props) { + super(parent, props); + stateB = this.state; + } + } + + class ComponentA extends Component { + static components = { ComponentB }; + static template = xml`
`; + state = useState({ fromA: 1 }); + } + + const component = new ComponentA(env); + await component.mount(fixture); + + expect(fixture.innerHTML).toBe("

1b

"); + + stateB.fromB = "c"; + await nextTick(); + + expect(fixture.innerHTML).toBe("

1b

"); + + component.state.fromA = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("

1b

"); + + expect(ComponentC.prototype.someValue).toBeCalledTimes(1); + def.resolve(); + await nextTick(); + + expect(ComponentC.prototype.someValue).toBeCalledTimes(2); + expect(fixture.innerHTML).toBe("

2c

"); + }); + + test("concurrent renderings scenario 2", async () => { + // this test asserts that a rendering initiated before another one, and that + // ends after it, is re-mapped to that second rendering + const defs = [makeDeferred(), makeDeferred()]; + let index = 0; + let stateB; + class ComponentC extends Component { + static template = xml``; + willUpdateProps() { + return defs[index++]; + } + } + + class ComponentB extends Component { + static template = xml`

`; + static components = { ComponentC }; + state = useState({ fromB: "b" }); + + constructor(parent, props) { + super(parent, props); + stateB = this.state; + } + } + + class ComponentA extends Component { + static template = xml`
`; + static components = { ComponentB }; + state = useState({ fromA: 1 }); + } + + const component = new ComponentA(env); + await component.mount(fixture); + + expect(fixture.innerHTML).toBe("
1

1b

"); + + component.state.fromA = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("
1

1b

"); + + stateB.fromB = "c"; + await nextTick(); + expect(fixture.innerHTML).toBe("
1

1b

"); + + defs[1].resolve(); // resolve rendering initiated in B + await nextTick(); + expect(fixture.innerHTML).toBe("
2

2c

"); + + defs[0].resolve(); // resolve rendering initiated in A + await nextTick(); + expect(fixture.innerHTML).toBe("
2

2c

"); + }); + + test("concurrent renderings scenario 2bis", async () => { + const defs = [makeDeferred(), makeDeferred()]; + let index = 0; + let stateB; + class ComponentC extends Component { + static template = xml``; + willUpdateProps() { + return defs[index++]; + } + } + + class ComponentB extends Component { + static template = xml`

`; + static components = { ComponentC }; + state = useState({ fromB: "b" }); + + constructor(parent, props) { + super(parent, props); + stateB = this.state; + } + } + + class ComponentA extends Component { + static template = xml`
`; + static components = { ComponentB }; + state = useState({ fromA: 1 }); + } + + const component = new ComponentA(env); + await component.mount(fixture); + + expect(fixture.innerHTML).toBe("

1b

"); + + component.state.fromA = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("

1b

"); + + stateB.fromB = "c"; + await nextTick(); + expect(fixture.innerHTML).toBe("

1b

"); + + defs[0].resolve(); // resolve rendering initiated in A + await nextTick(); + expect(fixture.innerHTML).toBe("

1b

"); // TODO: is this what we want?? 2b could be ok too + + defs[1].resolve(); // resolve rendering initiated in B + await nextTick(); + expect(fixture.innerHTML).toBe("

2c

"); + }); + + test("concurrent renderings scenario 3", async () => { + const defB = makeDeferred(); + const defsD = [makeDeferred(), makeDeferred()]; + let index = 0; + let stateC; + + class ComponentD extends Component { + static template = xml``; + someValue() { + return this.props.fromC; + } + willUpdateProps() { + return defsD[index++]; + } + } + ComponentD.prototype.someValue = jest.fn(ComponentD.prototype.someValue); + + class ComponentC extends Component { + static template = xml``; + static components = { ComponentD }; + state = useState({ fromC: "c" }); + constructor(parent, props) { + super(parent, props); + stateC = this.state; + } + } + + class ComponentB extends Component { + static template = xml`

`; + static components = { ComponentC }; + + willUpdateProps() { + return defB; + } + } + + class ComponentA extends Component { + static components = { ComponentB }; + static template = xml`
`; + state = useState({ fromA: 1 }); + } + + const component = new ComponentA(env); + await component.mount(fixture); + + expect(fixture.innerHTML).toBe("

1c

"); + + component.state.fromA = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("

1c

"); + + stateC.fromC = "d"; + await nextTick(); + expect(fixture.innerHTML).toBe("

1c

"); + + defB.resolve(); // resolve rendering initiated in A (still blocked in D) + await nextTick(); + expect(fixture.innerHTML).toBe("

1c

"); + + defsD[0].resolve(); // resolve rendering initiated in C (should be ignored) + await nextTick(); + expect(ComponentD.prototype.someValue).toBeCalledTimes(1); + expect(fixture.innerHTML).toBe("

1c

"); + + defsD[1].resolve(); // completely resolve rendering initiated in A + await nextTick(); + expect(fixture.innerHTML).toBe("

2d

"); + expect(ComponentD.prototype.someValue).toBeCalledTimes(2); + }); + + test("concurrent renderings scenario 4", async () => { + const defB = makeDeferred(); + const defsD = [makeDeferred(), makeDeferred()]; + let index = 0; + let stateC; + + class ComponentD extends Component { + static template = xml``; + someValue() { + return this.props.fromC; + } + willUpdateProps() { + return defsD[index++]; + } + } + ComponentD.prototype.someValue = jest.fn(ComponentD.prototype.someValue); + + class ComponentC extends Component { + static template = xml``; + static components = { ComponentD }; + state = useState({ fromC: "c" }); + constructor(parent, props) { + super(parent, props); + stateC = this.state; + } + } + + class ComponentB extends Component { + static template = xml`

`; + static components = { ComponentC }; + + willUpdateProps() { + return defB; + } + } + + class ComponentA extends Component { + static components = { ComponentB }; + static template = xml`
`; + state = useState({ fromA: 1 }); + } + + const component = new ComponentA(env); + await component.mount(fixture); + + expect(fixture.innerHTML).toBe("

1c

"); + + component.state.fromA = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("

1c

"); + + stateC.fromC = "d"; + await nextTick(); + expect(fixture.innerHTML).toBe("

1c

"); + + defB.resolve(); // resolve rendering initiated in A (still blocked in D) + await nextTick(); + expect(fixture.innerHTML).toBe("

1c

"); + + defsD[1].resolve(); // completely resolve rendering initiated in A + await nextTick(); + expect(fixture.innerHTML).toBe("

2d

"); + expect(ComponentD.prototype.someValue).toBeCalledTimes(2); + + defsD[0].resolve(); // resolve rendering initiated in C (should be ignored) + await nextTick(); + expect(fixture.innerHTML).toBe("

2d

"); + expect(ComponentD.prototype.someValue).toBeCalledTimes(2); + }); + + test("concurrent renderings scenario 5", async () => { + const defsB = [makeDeferred(), makeDeferred()]; + let index = 0; + + class ComponentB extends Component { + static template = xml`

`; + someValue() { + return this.props.fromA; + } + willUpdateProps() { + return defsB[index++]; + } + } + ComponentB.prototype.someValue = jest.fn(ComponentB.prototype.someValue); + + class ComponentA extends Component { + static components = { ComponentB }; + static template = xml`
`; + state = useState({ fromA: 1 }); + } + + const component = new ComponentA(env); + await component.mount(fixture); + + expect(fixture.innerHTML).toBe("

1

"); + + component.state.fromA = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("

1

"); + + component.state.fromA = 3; + await nextTick(); + expect(fixture.innerHTML).toBe("

1

"); + + defsB[0].resolve(); // resolve first re-rendering (should be ignored) + await nextTick(); + expect(fixture.innerHTML).toBe("

1

"); + expect(ComponentB.prototype.someValue).toBeCalledTimes(1); + + defsB[1].resolve(); // resolve second re-rendering + await nextTick(); + expect(fixture.innerHTML).toBe("

3

"); + expect(ComponentB.prototype.someValue).toBeCalledTimes(2); + }); + + test("concurrent renderings scenario 6", async () => { + const defsB = [makeDeferred(), makeDeferred()]; + let index = 0; + + class ComponentB extends Component { + static template = xml`

`; + someValue() { + return this.props.fromA; + } + willUpdateProps() { + return defsB[index++]; + } + } + ComponentB.prototype.someValue = jest.fn(ComponentB.prototype.someValue); + + class ComponentA extends Component { + static components = { ComponentB }; + static template = xml`
`; + state = useState({ fromA: 1 }); + } + + const component = new ComponentA(env); + await component.mount(fixture); + + expect(fixture.innerHTML).toBe("

1

"); + + component.state.fromA = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("

1

"); + + component.state.fromA = 3; + await nextTick(); + expect(fixture.innerHTML).toBe("

1

"); + + defsB[1].resolve(); // resolve second re-rendering + await nextTick(); + expect(fixture.innerHTML).toBe("

3

"); + expect(ComponentB.prototype.someValue).toBeCalledTimes(2); + + defsB[0].resolve(); // resolve first re-rendering (should be ignored) + await nextTick(); + expect(fixture.innerHTML).toBe("

3

"); + expect(ComponentB.prototype.someValue).toBeCalledTimes(2); + }); + + test("concurrent renderings scenario 7", async () => { + class ComponentB extends Component { + static template = xml`

`; + state = useState({ fromB: "b" }); + someValue() { + return this.state.fromB; + } + async willUpdateProps() { + this.state.fromB = "c"; + } + } + ComponentB.prototype.someValue = jest.fn(ComponentB.prototype.someValue); + + class ComponentA extends Component { + static components = { ComponentB }; + static template = xml`
`; + state = useState({ fromA: 1 }); + } + + const component = new ComponentA(env); + await component.mount(fixture); + + expect(fixture.innerHTML).toBe("

1b

"); + expect(ComponentB.prototype.someValue).toBeCalledTimes(1); + + component.state.fromA = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("

2c

"); + expect(ComponentB.prototype.someValue).toBeCalledTimes(2); + }); + + test("concurrent renderings scenario 8", async () => { + const def = makeDeferred(); + let stateB; + class ComponentB extends Component { + static template = xml`

`; + state = useState({ fromB: "b" }); + constructor(parent, props) { + super(parent, props); + stateB = this.state; + } + async willUpdateProps(nextProps) { + return def; + } + } + + class ComponentA extends Component { + static components = { ComponentB }; + static template = xml`
`; + state = useState({ fromA: 1 }); + } + + const component = new ComponentA(env); + await component.mount(fixture); + + expect(fixture.innerHTML).toBe("

1b

"); + + component.state.fromA = 2; + await nextTick(); + expect(fixture.innerHTML).toBe("

1b

"); + + stateB.fromB = "c"; + await nextTick(); + expect(fixture.innerHTML).toBe("

1b

"); + + def.resolve(); + await nextTick(); + expect(fixture.innerHTML).toBe("

2c

"); + }); }); describe("widget and observable state", () => { test("widget is rerendered when its state is changed", async () => { - env.qweb.addTemplate("TestWidget", `
`); class TestWidget extends Widget { + static template = xml`
`; state = useState({ drink: "water" }); } const widget = new TestWidget(env); @@ -2767,9 +3354,7 @@ describe("widget and observable state", () => { expect(fixture.innerHTML).toBe("
water
"); widget.state.drink = "beer"; - // 2 microtask ticks: one for observer, one for rendering - await nextMicroTick(); - await nextMicroTick(); + await nextTick(); expect(fixture.innerHTML).toBe("
beer
"); }); @@ -3300,7 +3885,6 @@ describe("t-slot directive", () => { } const parent = new Parent(env); await parent.mount(fixture); - expect(fixture.innerHTML).toBe("
3
"); expect(QWeb.TEMPLATES[SlotComponent.template].fn.toString()).toMatchSnapshot(); @@ -3313,14 +3897,12 @@ describe("t-slot directive", () => { describe("t-model directive", () => { test("basic use, on an input", async () => { - env.qweb.addTemplates(` - -
- - -
-
`); class SomeComponent extends Widget { + static template = xml` +
+ + +
`; state = useState({ text: "" }); } const comp = new SomeComponent(env); @@ -3332,7 +3914,7 @@ describe("t-model directive", () => { await editInput(input, "test"); expect(comp.state.text).toBe("test"); expect(fixture.innerHTML).toBe("
test
"); - expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot(); + expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot(); }); test("basic use, on another key in component", async () => { @@ -3644,22 +4226,17 @@ describe("component error handling (catchError)", () => { test("can catch an error in a component render function", async () => { const consoleError = console.error; console.error = jest.fn(); - env.qweb.addTemplates(` - -
- Error handled - -
-
hey -
-
- -
-
`); const handler = jest.fn(); env.qweb.on("error", null, handler); - class ErrorComponent extends Widget {} + class ErrorComponent extends Widget { + static template = xml`
hey
`; + } class ErrorBoundary extends Widget { + static template = xml` +
+ Error handled + +
`; state = useState({ error: false }); catchError() { @@ -3667,6 +4244,10 @@ describe("component error handling (catchError)", () => { } } class App extends Widget { + static template = xml` +
+ +
`; state = useState({ flag: false }); static components = { ErrorBoundary, ErrorComponent }; } @@ -3722,27 +4303,26 @@ describe("component error handling (catchError)", () => { env.qweb.on("error", null, handler); const consoleError = console.error; console.error = jest.fn(); - env.qweb.addTemplates(` - -
- Error handled - -
-
hey -
-
- -
-
`); - class ErrorComponent extends Widget {} - class ErrorBoundary extends Widget { + class ErrorComponent extends Component { + static template = xml`
hey
`; + } + class ErrorBoundary extends Component { + static template = xml` +
+ Error handled + +
`; state = useState({ error: false }); catchError() { this.state.error = true; } } - class App extends Widget { + class App extends Component { + static template = xml` +
+ +
`; static components = { ErrorBoundary, ErrorComponent }; } const app = new App(env); @@ -3804,18 +4384,8 @@ describe("component error handling (catchError)", () => { test("can catch an error in the willStart call", async () => { const consoleError = console.error; console.error = jest.fn(); - env.qweb.addTemplates(` - -
- Error handled - -
-
Some text
-
- -
-
`); class ErrorComponent extends Widget { + static template = xml`
Some text
`; async willStart() { // we wait a little bit to be in a different stack frame await nextTick(); @@ -3823,6 +4393,11 @@ describe("component error handling (catchError)", () => { } } class ErrorBoundary extends Widget { + static template = xml` +
+ Error handled + +
`; state = useState({ error: false }); catchError() { @@ -3830,6 +4405,7 @@ describe("component error handling (catchError)", () => { } } class App extends Widget { + static template = xml`
`; static components = { ErrorBoundary, ErrorComponent }; } const app = new App(env); @@ -3843,7 +4419,8 @@ describe("component error handling (catchError)", () => { console.error = consoleError; }); - test("can catch an error in the mounted call", async () => { + test.skip("can catch an error in the mounted call", async () => { + // we do not catch error in mounted anymore console.error = jest.fn(); env.qweb.addTemplates(` @@ -3879,24 +4456,20 @@ describe("component error handling (catchError)", () => { expect(fixture.innerHTML).toBe("
Error handled
"); }); - test("can catch an error in the willPatch call", async () => { - env.qweb.addTemplates(` - -
- Error handled - -
-
-
- -
-
`); + test.skip("can catch an error in the willPatch call", async () => { + // we do not catch error in willPatch anymore class ErrorComponent extends Widget { + static template = xml`
`; willPatch() { throw new Error("NOOOOO"); } } class ErrorBoundary extends Widget { + static template = xml` +
+ Error handled + +
`; state = useState({ error: false }); catchError() { @@ -3904,6 +4477,10 @@ describe("component error handling (catchError)", () => { } } class App extends Widget { + static template = xml` +
+ +
`; state = useState({ message: "abc" }); static components = { ErrorBoundary, ErrorComponent }; } @@ -4016,6 +4593,25 @@ describe("top level sub widgets", () => { await nextTick(); expect(fixture.innerHTML).toBe("
CHILD 2
"); }); + + test("top level sub widget with a parent", async () => { + class ComponentC extends Component { + static template = xml`Hello`; + } + class ComponentB extends Component { + static template = xml``; + static components = { ComponentC }; + } + class ComponentA extends Component { + static template = xml`
`; + static components = { ComponentB }; + } + + const component = new ComponentA(env); + await component.mount(fixture); + + expect(fixture.innerHTML).toBe("
Hello
"); + }); }); describe("unmounting and remounting", () => { @@ -4032,6 +4628,9 @@ describe("unmounting and remounting", () => { willUnmount() { steps.push("willunmount"); } + patched() { + throw new Error("patched should not be called"); + } } const w = new MyWidget(env); diff --git a/tests/component/props_validation.test.ts b/tests/component/props_validation.test.ts index da4a031a..5ec33a88 100644 --- a/tests/component/props_validation.test.ts +++ b/tests/component/props_validation.test.ts @@ -1,5 +1,6 @@ import { Component, Env } from "../../src/component/component"; -import { makeTestFixture, makeTestEnv } from "../helpers"; +import { makeTestFixture, makeTestEnv, nextTick } from "../helpers"; +import { useState } from "../../src/hooks"; import { QWeb } from "../../src/qweb"; import { xml } from "../../src/tags"; @@ -336,21 +337,22 @@ describe("default props", () => { test("default values are also set whenever component is updated", async () => { class TestWidget extends Widget { + static template = xml`
`; static defaultProps = { p: 4 }; } - env.qweb.addTemplates(` - -
-
`); + class Parent extends Widget { + static template = xml`
`; + static components = { TestWidget }; + state: any = useState({ p: 1 }); + } - const w = new TestWidget(env, { p: 1 }); + const w = new Parent(env); await w.mount(fixture); - expect(fixture.innerHTML).toMatchSnapshot(); - const fiber = w.__createFiber(false, undefined, undefined, undefined); - await w.__updateProps({}, fiber); - await w.render(); - expect(w.props.p).toBe(4); - expect(fixture.innerHTML).toMatchSnapshot(); + expect(fixture.innerHTML).toBe("
1
"); + + w.state.p = undefined; + await nextTick(); + expect(fixture.innerHTML).toBe("
4
"); }); test("can set default required boolean values", async () => { diff --git a/tests/context.test.ts b/tests/context.test.ts index 25221959..311ad75c 100644 --- a/tests/context.test.ts +++ b/tests/context.test.ts @@ -1,4 +1,4 @@ -import { makeTestEnv, makeTestFixture, nextTick } from "./helpers"; +import { makeDeferred, makeTestEnv, makeTestFixture, nextTick } from "./helpers"; import { Component, Env } from "../src/component/component"; import { Context, useContext } from "../src/context"; import { xml } from "../src/tags"; @@ -173,4 +173,52 @@ describe("Context", () => { // event expect(testContext.subscriptions.update.length).toBe(0); }); + + test.skip("concurrent renderings", async () => { + const testContext = new Context({ x: { n: 1 }, key: "x" }); + const def = makeDeferred(); + let stateC; + class ComponentC extends Component { + static template = xml``; + context = useContext(testContext); + state = useState({ x: "a" }); + + constructor(parent, props) { + super(parent, props); + stateC = this.state; + } + } + class ComponentB extends Component { + static components = { ComponentC }; + static template = xml`

`; + + willUpdateProps() { + return def; + } + } + class ComponentA extends Component { + static components = { ComponentB }; + static template = xml`
`; + context = useContext(testContext); + } + + const component = new ComponentA(env); + await component.mount(fixture); + + expect(fixture.innerHTML).toBe("

1a

"); + testContext.state.key = "y"; + testContext.state.y = 2; + delete testContext.state.x; + await nextTick(); + + expect(fixture.innerHTML).toBe("

1a

"); + stateC.x = "b"; + await nextTick(); + + expect(fixture.innerHTML).toBe("

1a

"); + def.resolve(); + await nextTick(); + + expect(fixture.innerHTML).toBe("

1a

"); + }); }); diff --git a/tests/doc_link_checker.test.ts b/tests/doc_link_checker.test.ts index 8d1207de..36c1429b 100644 --- a/tests/doc_link_checker.test.ts +++ b/tests/doc_link_checker.test.ts @@ -13,7 +13,7 @@ const HEADING_REGEXP = /\n(#+\s*)(.*)/g; function getFiles(): string[] { const DOCFILES = fs.readdirSync("doc").map(f => `doc/${f}`); const MAINREADME = "README.md"; - DOCFILES.push('roadmap.md', MAINREADME) + DOCFILES.push("roadmap.md", MAINREADME); return DOCFILES; } @@ -54,7 +54,7 @@ interface FileData { } function isLinkValid(link: MarkDownLink, current: FileData, files: FileData[]): boolean { - if (link.link.startsWith('http')) { + if (link.link.startsWith("http")) { // no check on external links return true; } diff --git a/tests/helpers.ts b/tests/helpers.ts index 9d2870ae..f845f73d 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -5,6 +5,16 @@ import "../src/qweb/base_directives"; import "../src/qweb/extensions"; import "../src/component/directive"; +// modifies scheduler to make it easier to test components +// let current; +// scheduler.requestAnimationFrame = function(callback: FrameRequestCallback) { +// if (current) { +// throw new Error("should not schedule 2 callbacks!"); +// } +// current = callback; +// return 1; +// }; + // Some static cleanup let nextSlotId; let slots; @@ -12,6 +22,7 @@ let nextId; let TEMPLATES; beforeEach(() => { + // current = null; nextSlotId = QWeb.nextSlotId; slots = Object.assign({}, QWeb.slots); nextId = QWeb.nextId; @@ -19,6 +30,7 @@ beforeEach(() => { }); afterEach(() => { + // current = null; QWeb.nextSlotId = nextSlotId; QWeb.slots = slots; QWeb.nextId = nextId; @@ -29,9 +41,23 @@ afterEach(() => { export function nextMicroTick(): Promise { return Promise.resolve(); } +// export async function nextTick(): Promise { +// await Promise.resolve(); +// let max = 1000; +// while (current && max > 0) { +// const cb = current; +// console.warn('next'); +// current = null; +// cb(); +// max--; +// await Promise.resolve(); +// } +// } -export function nextTick(): Promise { - return new Promise(resolve => setTimeout(resolve)); +export async function nextTick(): Promise { + return new Promise(function (resolve) { + setTimeout(() => requestAnimationFrame(() => resolve())); + }); } export function makeTestFixture() { diff --git a/tests/misc/async_root.test.ts b/tests/misc/async_root.test.ts index 1de43e6a..4bae5238 100644 --- a/tests/misc/async_root.test.ts +++ b/tests/misc/async_root.test.ts @@ -178,7 +178,7 @@ describe("Asyncroot", () => { children[1]!.click(); await nextTick(); - expect(fixture.querySelector(".children")!.innerHTML).toBe("1/11/0"); + expect(fixture.querySelector(".children")!.innerHTML).toBe("1/10/0"); // finalize first re-rendering (coming from the props update) def.resolve(); diff --git a/tests/qweb/__snapshots__/qweb.test.ts.snap b/tests/qweb/__snapshots__/qweb.test.ts.snap index 1f94139d..9d3ac2b0 100644 --- a/tests/qweb/__snapshots__/qweb.test.ts.snap +++ b/tests/qweb/__snapshots__/qweb.test.ts.snap @@ -3,6 +3,7 @@ exports[`attributes class and t-attf-class with ternary operation 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = 'hello ' + (context['value']?'world':''); let c2 = [], p2 = {key:2,attrs:{class: _1}}; @@ -14,6 +15,7 @@ exports[`attributes class and t-attf-class with ternary operation 1`] = ` exports[`attributes dynamic attribute falsy variable 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = context['value']; let c2 = [], p2 = {key:2,attrs:{foo: _1}}; @@ -25,6 +27,7 @@ exports[`attributes dynamic attribute falsy variable 1`] = ` exports[`attributes dynamic attribute with a dash 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = context['id']; let c2 = [], p2 = {key:2,attrs:{\\"data-action-id\\": _1}}; @@ -36,6 +39,7 @@ exports[`attributes dynamic attribute with a dash 1`] = ` exports[`attributes dynamic attributes 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = 'bar'; let c2 = [], p2 = {key:2,attrs:{foo: _1}}; @@ -47,6 +51,7 @@ exports[`attributes dynamic attributes 1`] = ` exports[`attributes dynamic formatted attributes with a dash 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = \`Some text \${context['id']}\`; let c2 = [], p2 = {key:2,attrs:{\\"aria-label\\": _1}}; @@ -58,6 +63,7 @@ exports[`attributes dynamic formatted attributes with a dash 1`] = ` exports[`attributes fixed variable 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = context['value']; let c2 = [], p2 = {key:2,attrs:{foo: _1}}; @@ -69,6 +75,7 @@ exports[`attributes fixed variable 1`] = ` exports[`attributes format expression 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = (context['value']+37); let c2 = [], p2 = {key:2,attrs:{foo: _1}}; @@ -80,6 +87,7 @@ exports[`attributes format expression 1`] = ` exports[`attributes format expression, other format 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = (context['value']+37); let c2 = [], p2 = {key:2,attrs:{foo: _1}}; @@ -91,6 +99,7 @@ exports[`attributes format expression, other format 1`] = ` exports[`attributes format literal 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = \`bar\`; let c2 = [], p2 = {key:2,attrs:{foo: _1}}; @@ -102,6 +111,7 @@ exports[`attributes format literal 1`] = ` exports[`attributes format multiple 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = \`a \${context['value1']} is \${context['value2']} of \${context['value3']} ]\`; let c2 = [], p2 = {key:2,attrs:{foo: _1}}; @@ -113,6 +123,7 @@ exports[`attributes format multiple 1`] = ` exports[`attributes format value 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = \`b\${context['value']}r\`; let c2 = [], p2 = {key:2,attrs:{foo: _1}}; @@ -125,6 +136,7 @@ exports[`attributes from object variables set previously 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -141,6 +153,7 @@ exports[`attributes from variables set previously 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -156,6 +169,7 @@ exports[`attributes from variables set previously 1`] = ` exports[`attributes object 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = context['value']; let c2 = [], p2 = {key:2,attrs:{}}; @@ -174,6 +188,7 @@ exports[`attributes object 1`] = ` exports[`attributes static attributes 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = 'a'; var _2 = 'b'; @@ -187,6 +202,7 @@ exports[`attributes static attributes 1`] = ` exports[`attributes static attributes on void elements 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = '/test.jpg'; var _2 = 'Test'; @@ -199,6 +215,7 @@ exports[`attributes static attributes on void elements 1`] = ` exports[`attributes static attributes with dashes 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = 'Close'; let c2 = [], p2 = {key:2,attrs:{\\"aria-label\\": _1}}; @@ -211,6 +228,7 @@ exports[`attributes t-att-class and class should combine together 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let _2 = {'hello':true}; Object.assign(_2, utils.toObj(context['value'])) @@ -224,6 +242,7 @@ exports[`attributes t-att-class with object 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let _2 = {'static':true}; Object.assign(_2, utils.toObj({a:context['b'],c:context['d'],e:context['f']})) @@ -236,6 +255,7 @@ exports[`attributes t-att-class with object 1`] = ` exports[`attributes t-attf-class should combine with class 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = 'hello ' + \`world\`; let c2 = [], p2 = {key:2,attrs:{class: _1}}; @@ -247,6 +267,7 @@ exports[`attributes t-attf-class should combine with class 1`] = ` exports[`attributes tuple literal 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = ['foo','bar']; let c2 = [], p2 = {key:2,attrs:{}}; @@ -265,6 +286,7 @@ exports[`attributes tuple literal 1`] = ` exports[`attributes tuple variable 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = context['value']; let c2 = [], p2 = {key:2,attrs:{}}; @@ -283,6 +305,7 @@ exports[`attributes tuple variable 1`] = ` exports[`debugging t-debug 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; debugger; let c1 = [], p1 = {key:1}; @@ -301,6 +324,7 @@ exports[`debugging t-debug 1`] = ` exports[`debugging t-log 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -313,6 +337,7 @@ exports[`debugging t-log 1`] = ` exports[`foreach does not pollute the rendering context 1`] = ` "function anonymous(context,extra ) { + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -343,6 +368,7 @@ exports[`foreach does not pollute the rendering context 1`] = ` exports[`foreach iterate on items (on a element node) 1`] = ` "function anonymous(context,extra ) { + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -377,6 +403,7 @@ exports[`foreach iterate on items (on a element node) 1`] = ` exports[`foreach iterate on items 1`] = ` "function anonymous(context,extra ) { + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -419,6 +446,7 @@ exports[`foreach iterate on items 1`] = ` exports[`foreach iterate, dict param 1`] = ` "function anonymous(context,extra ) { + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -461,6 +489,7 @@ exports[`foreach iterate, dict param 1`] = ` exports[`foreach iterate, position 1`] = ` "function anonymous(context,extra ) { + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -500,6 +529,7 @@ exports[`foreach iterate, position 1`] = ` exports[`foreach t-foreach in t-forach 1`] = ` "function anonymous(context,extra ) { + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -551,6 +581,7 @@ exports[`foreach t-foreach in t-forach 1`] = ` exports[`foreach warn if no key in some case 1`] = ` "function anonymous(context,extra ) { + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -584,6 +615,7 @@ exports[`foreach warn if no key in some case 1`] = ` exports[`loading templates can initialize qweb with a string 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -595,6 +627,7 @@ exports[`loading templates can initialize qweb with a string 1`] = ` exports[`loading templates can load a few templates from a xml string 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('ul', p1, c1); @@ -614,6 +647,7 @@ exports[`misc global 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -693,6 +727,7 @@ exports[`properly support svg add proper namespace to g tags 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('g', p1, c1); @@ -715,6 +750,7 @@ exports[`properly support svg add proper namespace to svg 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; var _1 = '100px'; var _2 = '90px'; @@ -738,6 +774,7 @@ exports[`properly support svg add proper namespace to svg 1`] = ` exports[`special cases for some boolean html attributes/properties input type= checkbox, with t-att-checked 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; var _1 = 'checkbox'; var _2 = context['flag']; @@ -750,6 +787,7 @@ exports[`special cases for some boolean html attributes/properties input type= c exports[`special cases for some boolean html attributes/properties various boolean html attributes 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -790,6 +828,7 @@ exports[`special cases for some boolean html attributes/properties various boole exports[`static templates div with a span child node 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -804,6 +843,7 @@ exports[`static templates div with a span child node 1`] = ` exports[`static templates div with a text node 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -815,6 +855,7 @@ exports[`static templates div with a text node 1`] = ` exports[`static templates empty div 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -825,6 +866,7 @@ exports[`static templates empty div 1`] = ` exports[`static templates properly handle comments 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -838,6 +880,7 @@ exports[`static templates properly handle comments 1`] = ` exports[`static templates simple dynamic value 1`] = ` "function anonymous(context,extra ) { + let sibling = null; let result; var h = this.h; var _1 = context['text']; @@ -852,6 +895,7 @@ exports[`static templates simple dynamic value 1`] = ` exports[`static templates simple string 1`] = ` "function anonymous(context,extra ) { + let sibling = null; let result; var h = this.h; var vn1 = {text: \`hello vdom\`}; @@ -863,6 +907,7 @@ exports[`static templates simple string 1`] = ` exports[`static templates simple string, with some dynamic value 1`] = ` "function anonymous(context,extra ) { + let sibling = null; let result; var h = this.h; var vn1 = {text: \`hello \`}; @@ -878,6 +923,7 @@ exports[`static templates simple string, with some dynamic value 1`] = ` exports[`t-call (template calling basic caller 1`] = ` "function anonymous(context,extra ) { + let sibling = null; let result; var h = this.h; let c1 = [], p1 = {key:1}; @@ -891,6 +937,7 @@ exports[`t-call (template calling basic caller 1`] = ` exports[`t-call (template calling call with several sub nodes on same line 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -913,6 +960,7 @@ exports[`t-call (template calling call with several sub nodes on same line 1`] = exports[`t-call (template calling inherit context 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -928,6 +976,7 @@ exports[`t-call (template calling recursive template, part 1 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -946,6 +995,7 @@ exports[`t-call (template calling recursive template, part 1 2`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = extra.parentNode; Object.assign(context, extra.fiber.scope); @@ -966,6 +1016,7 @@ exports[`t-call (template calling recursive template, part 2 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; context = Object.create(context); const scope = Object.create(null); var h = this.h; @@ -1013,6 +1064,7 @@ exports[`t-call (template calling recursive template, part 2 2`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; context = Object.create(context); const scope = Object.create(null); var h = this.h; @@ -1057,6 +1109,7 @@ exports[`t-call (template calling recursive template, part 3 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; context = Object.create(context); const scope = Object.create(null); var h = this.h; @@ -1104,6 +1157,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; context = Object.create(context); const scope = Object.create(null); var h = this.h; @@ -1147,6 +1201,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = ` exports[`t-call (template calling scoped parameters 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1165,6 +1220,7 @@ exports[`t-call (template calling scoped parameters 1`] = ` exports[`t-call (template calling t-call with t-if 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1181,6 +1237,7 @@ exports[`t-call (template calling t-call with t-if 1`] = ` exports[`t-call (template calling t-call, global templates 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1195,6 +1252,7 @@ exports[`t-call (template calling t-call, global templates 1`] = ` exports[`t-call (template calling with unused body 1`] = ` "function anonymous(context,extra ) { + let sibling = null; let result; var h = this.h; let c2 = [], p2 = {key:2}; @@ -1208,6 +1266,7 @@ exports[`t-call (template calling with unused body 1`] = ` exports[`t-call (template calling with unused setbody 1`] = ` "function anonymous(context,extra ) { + let sibling = null; let result; var h = this.h; { @@ -1224,6 +1283,7 @@ exports[`t-call (template calling with unused setbody 1`] = ` exports[`t-call (template calling with used body 1`] = ` "function anonymous(context,extra ) { + let sibling = null; let result; var h = this.h; let c2 = [], p2 = {key:2}; @@ -1237,6 +1297,7 @@ exports[`t-call (template calling with used body 1`] = ` exports[`t-call (template calling with used set body 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); @@ -1253,6 +1314,7 @@ exports[`t-call (template calling with used set body 1`] = ` exports[`t-esc escaping on a node 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); @@ -1267,6 +1329,7 @@ exports[`t-esc escaping on a node 1`] = ` exports[`t-esc escaping on a node with a body 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); @@ -1283,6 +1346,7 @@ exports[`t-esc escaping on a node with a body 1`] = ` exports[`t-esc escaping on a node with a body, as a default 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); @@ -1299,6 +1363,7 @@ exports[`t-esc escaping on a node with a body, as a default 1`] = ` exports[`t-esc literal 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); @@ -1313,6 +1378,7 @@ exports[`t-esc literal 1`] = ` exports[`t-esc variable 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); @@ -1327,6 +1393,7 @@ exports[`t-esc variable 1`] = ` exports[`t-if boolean value condition elif 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1349,6 +1416,7 @@ exports[`t-if boolean value condition elif 1`] = ` exports[`t-if boolean value condition else 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1373,6 +1441,7 @@ exports[`t-if boolean value condition else 1`] = ` exports[`t-if boolean value condition false else 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1397,6 +1466,7 @@ exports[`t-if boolean value condition false else 1`] = ` exports[`t-if boolean value condition missing 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); @@ -1410,6 +1480,7 @@ exports[`t-if boolean value condition missing 1`] = ` exports[`t-if boolean value false condition 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1423,6 +1494,7 @@ exports[`t-if boolean value false condition 1`] = ` exports[`t-if boolean value true condition 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1436,6 +1508,7 @@ exports[`t-if boolean value true condition 1`] = ` exports[`t-if can use some boolean operators in expressions 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1470,6 +1543,7 @@ exports[`t-if can use some boolean operators in expressions 1`] = ` exports[`t-if t-esc with t-elif 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1489,6 +1563,7 @@ exports[`t-if t-esc with t-elif 1`] = ` exports[`t-if t-esc with t-if 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1505,6 +1580,7 @@ exports[`t-if t-esc with t-if 1`] = ` exports[`t-if t-set, then t-elif, part 3 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1529,6 +1605,7 @@ exports[`t-if t-set, then t-elif, part 3 1`] = ` exports[`t-if t-set, then t-if 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1545,6 +1622,7 @@ exports[`t-if t-set, then t-if 1`] = ` exports[`t-if t-set, then t-if, part 2 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1563,6 +1641,7 @@ exports[`t-if t-set, then t-if, part 2 1`] = ` exports[`t-key can use t-key directive on a node 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; const nodeKey1 = context['beer'].id let c1 = [], p1 = {key:nodeKey1}; @@ -1578,6 +1657,7 @@ exports[`t-key can use t-key directive on a node 1`] = ` exports[`t-key t-key directive in a list 1`] = ` "function anonymous(context,extra ) { + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -1615,6 +1695,7 @@ exports[`t-on can bind event handler 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); @@ -1629,6 +1710,7 @@ exports[`t-on can bind handlers with arguments 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); @@ -1643,6 +1725,7 @@ exports[`t-on can bind handlers with empty object (with non empty inner string) "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); @@ -1657,6 +1740,7 @@ exports[`t-on can bind handlers with empty object 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); @@ -1671,6 +1755,7 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -1708,6 +1793,7 @@ exports[`t-on can bind handlers with object arguments 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); @@ -1722,6 +1808,7 @@ exports[`t-on can bind two event handlers 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); @@ -1738,6 +1825,7 @@ exports[`t-on handler is bound to proper owner 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); @@ -1752,6 +1840,7 @@ exports[`t-on t-on combined with t-esc 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1773,6 +1862,7 @@ exports[`t-on t-on combined with t-raw 1`] = ` ) { let utils = this.constructor.utils; let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1793,6 +1883,7 @@ exports[`t-on t-on with empty handler (only modifiers) 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1809,6 +1900,7 @@ exports[`t-on t-on with inline statement (function call) 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); @@ -1823,6 +1915,7 @@ exports[`t-on t-on with inline statement 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); @@ -1837,6 +1930,7 @@ exports[`t-on t-on with prevent and self modifiers (order matters) 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1857,6 +1951,7 @@ exports[`t-on t-on with prevent and/or stop modifiers 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1886,6 +1981,7 @@ exports[`t-on t-on with prevent modifier in t-foreach 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -1925,6 +2021,7 @@ exports[`t-on t-on with self and prevent modifiers (order matters) 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1945,6 +2042,7 @@ exports[`t-on t-on with self modifier 1`] = ` "function anonymous(context,extra ) { let owner = context; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -1974,6 +2072,7 @@ exports[`t-raw literal 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); @@ -1989,6 +2088,7 @@ exports[`t-raw not escaping 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2004,6 +2104,7 @@ exports[`t-raw t-raw and another sibling node 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); @@ -2023,6 +2124,7 @@ exports[`t-raw variable 1`] = ` "function anonymous(context,extra ) { let utils = this.constructor.utils; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('span', p1, c1); @@ -2038,6 +2140,7 @@ exports[`t-ref can get a dynamic ref on a node 1`] = ` "function anonymous(context,extra ) { context.__owl__.refs = context.__owl__.refs || {}; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2061,6 +2164,7 @@ exports[`t-ref can get a ref on a node 1`] = ` "function anonymous(context,extra ) { context.__owl__.refs = context.__owl__.refs || {}; + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2084,6 +2188,7 @@ exports[`t-ref refs in a loop 1`] = ` "function anonymous(context,extra ) { context.__owl__.refs = context.__owl__.refs || {}; + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -2127,6 +2232,7 @@ exports[`t-ref refs in a loop 1`] = ` exports[`t-set evaluate value expression 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2141,6 +2247,7 @@ exports[`t-set evaluate value expression 1`] = ` exports[`t-set evaluate value expression, part 2 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2155,6 +2262,7 @@ exports[`t-set evaluate value expression, part 2 1`] = ` exports[`t-set set from attribute literal 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2169,6 +2277,7 @@ exports[`t-set set from attribute literal 1`] = ` exports[`t-set set from attribute lookup 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2183,6 +2292,7 @@ exports[`t-set set from attribute lookup 1`] = ` exports[`t-set set from body literal 1`] = ` "function anonymous(context,extra ) { + let sibling = null; let result; var h = this.h; var vn1 = {text: \`ok\`}; @@ -2194,6 +2304,7 @@ exports[`t-set set from body literal 1`] = ` exports[`t-set set from body lookup 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2208,6 +2319,7 @@ exports[`t-set set from body lookup 1`] = ` exports[`t-set set from empty body 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2218,6 +2330,7 @@ exports[`t-set set from empty body 1`] = ` exports[`t-set t-set and t-if 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2232,6 +2345,7 @@ exports[`t-set t-set and t-if 1`] = ` exports[`t-set t-set evaluates an expression only once 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2249,6 +2363,7 @@ exports[`t-set t-set evaluates an expression only once 1`] = ` exports[`t-set t-set should reuse variable if possible 1`] = ` "function anonymous(context,extra ) { + let sibling = null; context = Object.create(context); var h = this.h; let c1 = [], p1 = {key:1}; @@ -2288,6 +2403,7 @@ exports[`t-set t-set should reuse variable if possible 1`] = ` exports[`t-set value priority 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2302,6 +2418,7 @@ exports[`t-set value priority 1`] = ` exports[`whitespace handling consecutives whitespaces are condensed into a single space 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2313,6 +2430,7 @@ exports[`whitespace handling consecutives whitespaces are condensed into a singl exports[`whitespace handling nothing is done in pre tags 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('pre', p1, c1); @@ -2324,6 +2442,7 @@ exports[`whitespace handling nothing is done in pre tags 1`] = ` exports[`whitespace handling nothing is done in pre tags 2`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('pre', p1, c1); @@ -2337,6 +2456,7 @@ exports[`whitespace handling nothing is done in pre tags 2`] = ` exports[`whitespace handling nothing is done in pre tags 3`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('pre', p1, c1); @@ -2350,6 +2470,7 @@ exports[`whitespace handling nothing is done in pre tags 3`] = ` exports[`whitespace handling white space only text nodes are condensed into a single space 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); @@ -2361,6 +2482,7 @@ exports[`whitespace handling white space only text nodes are condensed into a si exports[`whitespace handling whitespace only text nodes with newlines are removed 1`] = ` "function anonymous(context,extra ) { + let sibling = null; var h = this.h; let c1 = [], p1 = {key:1}; var vn1 = h('div', p1, c1); diff --git a/tests/router/__snapshots__/link.test.ts.snap b/tests/router/__snapshots__/link.test.ts.snap index 7bdbabe7..df3330d1 100644 --- a/tests/router/__snapshots__/link.test.ts.snap +++ b/tests/router/__snapshots__/link.test.ts.snap @@ -5,6 +5,7 @@ exports[`Link component can render simple cases 1`] = ` ) { let utils = this.constructor.utils; let owner = context; + let sibling = null; var h = this.h; let _1 = utils.toObj({'router-link-active':context['isActive']}); var _2 = context['href']; diff --git a/tests/router/__snapshots__/route_component.test.ts.snap b/tests/router/__snapshots__/route_component.test.ts.snap index 44c1fbe7..0f7f7d55 100644 --- a/tests/router/__snapshots__/route_component.test.ts.snap +++ b/tests/router/__snapshots__/route_component.test.ts.snap @@ -7,38 +7,40 @@ exports[`RouteComponent can render simple cases 1`] = ` let QWeb = this.constructor; let parent = context; let owner = context; + let sibling = null; let result; var h = this.h; if (context['routeComponent']) { //COMPONENT - let key4 = 'key' + context['env'].router.currentRouteName; - let def2; - let templateId3 = \`__5__\` + key4; - let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false; - let vn6 = {}; - result = vn6; - let props3 = Object.assign({}, context['env'].router.currentParams); - if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) { - if (utils.shallowEqual(props3, w3.__owl__.currentFiber.props)) { - def2 = w3.__owl__.currentFiber.promise; - } else { - w3.destroy(); - w3 = false; - } + let key3 = 'key' + context['env'].router.currentRouteName; + let def1; + let templateId2 = \`__4__\` + key3; + let w2 = templateId2 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId2]] : false; + let vn5 = {}; + result = vn5; + let props2 = Object.assign({}, context['env'].router.currentParams); + if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) { + w2.destroy(); + w2 = false; } - if (!w3) { - let componentKey3 = \`routeComponent\`; - let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['routeComponent']; - if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} - w3 = new W3(parent, props3); - parent.__owl__.cmap[templateId3] = w3.__owl__.id; - def2 = w3.__prepare(extra.fiber, undefined, undefined); - def2 = def2.then(vnode=>{if (w3.__owl__.isDestroyed){return}let pvnode=h(vnode.sel, {key: templateId3, hook: {insert(vn) {let nvn=w3.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});utils.defineProxy(vn6, pvnode);w3.__owl__.pvnode = pvnode;}); + if (!w2) { + let componentKey2 = \`routeComponent\`; + let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| context['routeComponent']; + if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} + w2 = new W2(parent, props2); + parent.__owl__.cmap[templateId2] = w2.__owl__.id; + def1 = w2.__prepare(extra.fiber, undefined, undefined, sibling); + let pvnode = h('dummy', {key: templateId2, 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 (w2.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + utils.defineProxy(vn5, pvnode); + w2.__owl__.pvnode = pvnode; } else { - def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined); - def2 = def2.then(()=>{if (w3.__owl__.isDestroyed) {return};let pvnode=w3.__owl__.pvnode;utils.defineProxy(vn6, pvnode);}); + def1 = def1 || w2.__updateProps(props2, extra.fiber, undefined, undefined, sibling); + let pvnode = w2.__owl__.pvnode; + utils.defineProxy(vn5, pvnode); } - extra.promises.push(def2); + sibling = w2.__owl__.currentFiber; } return result; }" diff --git a/tests/store_hooks.test.ts b/tests/store_hooks.test.ts index c391b3d8..49c5275e 100644 --- a/tests/store_hooks.test.ts +++ b/tests/store_hooks.test.ts @@ -51,8 +51,8 @@ describe("connecting a component to store", () => { const state = { a: 1, b: 2 }; const actions = { doSomething({ state }) { - state.a = 2; - state.b = 3; + state.a = 2; + state.b = 3; } }; const store = new Store({ state, actions }); @@ -63,8 +63,8 @@ describe("connecting a component to store", () => {
`; - a = useStore(state => ({value: state.a})); - b = useStore(state => ({value: state.b})); + a = useStore(state => ({ value: state.a })); + b = useStore(state => ({ value: state.b })); } App.prototype.__render = jest.fn(App.prototype.__render); @@ -79,7 +79,7 @@ describe("connecting a component to store", () => { await nextTick(); expect(fixture.innerHTML).toBe("
23
"); expect(App.prototype.__render).toBeCalledTimes(2); - }); + }); test("useStore: do not re-render if not changed", async () => { let nextId = 1; diff --git a/tools/playground/samples.js b/tools/playground/samples.js index 8590ced2..a36ae5bf 100644 --- a/tools/playground/samples.js +++ b/tools/playground/samples.js @@ -1273,9 +1273,10 @@ const ASYNC_COMPONENTS = `// This example will not work if your browser does not // In this example, we have 2 sub components, one of them being async (slow). // However, we don't want renderings of the other sub component to be delayed -// because of the slow component. We use the 't-asyncroot' directive for this +// because of the slow component. We use the AsyncRoot component for this // purpose. Try removing it to see the difference. const { Component, useState } = owl; +const { AsyncRoot } = owl.misc; class SlowComponent extends Component { willUpdateProps() { @@ -1311,9 +1312,10 @@ const ASYNC_COMPONENTS_XML = `
- + + +
-
Current value: