[FIX] allow to combine directives using node hooks

e.g. t-ref and t-transition, or t-mounted and t-transition.
This commit is contained in:
Aaron Bohy
2019-05-10 10:28:20 +02:00
committed by Géry Debongnie
parent 0aba9748c9
commit 1760d0d336
4 changed files with 96 additions and 38 deletions
+31 -3
View File
@@ -31,7 +31,6 @@ interface Template {
} }
interface CompilationInfo { interface CompilationInfo {
nodeID?: number;
node: Element; node: Element;
qweb: QWeb; qweb: QWeb;
ctx: Context; ctx: Context;
@@ -39,6 +38,11 @@ interface CompilationInfo {
value: string; value: string;
} }
interface NodeCreationCompilationInfo extends CompilationInfo {
nodeID: number;
addNodeHook: Function;
}
export interface Directive { export interface Directive {
name: string; name: string;
extraNames?: string[]; extraNames?: string[];
@@ -46,7 +50,7 @@ export interface Directive {
// if return true, then directive is fully applied and there is no need to // if return true, then directive is fully applied and there is no need to
// keep processing node. Otherwise, we keep going. // keep processing node. Otherwise, we keep going.
atNodeEncounter?(info: CompilationInfo): boolean | void; atNodeEncounter?(info: CompilationInfo): boolean | void;
atNodeCreation?(info: CompilationInfo): void; atNodeCreation?(info: NodeCreationCompilationInfo): void;
finalize?(info: CompilationInfo): void; finalize?(info: CompilationInfo): void;
} }
@@ -87,6 +91,12 @@ const DIRECTIVE_NAMES = {
const DIRECTIVES: Directive[] = []; const DIRECTIVES: Directive[] = [];
const NODE_HOOKS_PARAMS = {
create: "(_, n)",
insert: "vn",
remove: "(vn, rm)"
};
export const UTILS = { export const UTILS = {
h: h, h: h,
objectToAttrString(obj: Object): string { objectToAttrString(obj: Object): string {
@@ -497,6 +507,11 @@ export class QWeb {
if (node.nodeName !== "t") { if (node.nodeName !== "t") {
let nodeID = this._compileGenericNode(node, ctx, withHandlers); let nodeID = this._compileGenericNode(node, ctx, withHandlers);
ctx = ctx.withParent(nodeID); ctx = ctx.withParent(nodeID);
let nodeHooks = {};
let addNodeHook = function(hook, handler) {
nodeHooks[hook] = nodeHooks[hook] || [];
nodeHooks[hook].push(handler);
};
for (let { directive, value, fullName } of validDirectives) { for (let { directive, value, fullName } of validDirectives) {
if (directive.atNodeCreation) { if (directive.atNodeCreation) {
@@ -506,10 +521,23 @@ export class QWeb {
ctx, ctx,
fullName, fullName,
value, value,
nodeID nodeID,
addNodeHook
}); });
} }
} }
if (Object.keys(nodeHooks).length) {
ctx.addLine(`p${nodeID}.hook = {`);
for (let hook in nodeHooks) {
ctx.addLine(` ${hook}: ${NODE_HOOKS_PARAMS[hook]} => {`);
for (let handler of nodeHooks[hook]) {
ctx.addLine(` ${handler}`);
}
ctx.addLine(` },`);
}
ctx.addLine(`};`);
}
} }
if (node.nodeName === "pre") { if (node.nodeName === "pre") {
ctx = ctx.subContext("inPreTag", true); ctx = ctx.subContext("inPreTag", true);
+16 -20
View File
@@ -57,12 +57,10 @@ QWeb.addDirective({
QWeb.addDirective({ QWeb.addDirective({
name: "ref", name: "ref",
priority: 95, priority: 95,
atNodeCreation({ ctx, nodeID, value }) { atNodeCreation({ ctx, nodeID, value, addNodeHook }) {
const refKey = `ref${ctx.generateID()}`; const refKey = `ref${ctx.generateID()}`;
ctx.addLine(`const ${refKey} = ${ctx.formatExpression(value)}`); ctx.addLine(`const ${refKey} = ${ctx.formatExpression(value)}`);
ctx.addLine(`p${nodeID}.hook = { addNodeHook("create", `context.refs[${refKey}] = n.elm;`);
create: (_, n) => context.refs[${refKey}] = n.elm,
};`);
} }
}); });
@@ -148,19 +146,16 @@ function whenTransitionEnd(elm: HTMLElement, cb) {
QWeb.addDirective({ QWeb.addDirective({
name: "transition", name: "transition",
priority: 96, priority: 96,
atNodeCreation({ ctx, value, nodeID }) { atNodeCreation({ ctx, value, addNodeHook }) {
let name = value; let name = value;
ctx.addLine(`p${nodeID}.hook = { const hooks = {
create: (_, n) => { create: `this.utils.transitionCreate(n.elm, '${name}');`,
this.utils.transitionCreate(n.elm, '${name}'); insert: `this.utils.transitionInsert(vn.elm, '${name}');`,
}, remove: `this.utils.transitionRemove(vn.elm, '${name}', rm);`
insert: vn => { };
this.utils.transitionInsert(vn.elm, '${name}'); for (let hookName in hooks) {
}, addNodeHook(hookName, hooks[hookName]);
remove: (vn, rm) => { }
this.utils.transitionRemove(vn.elm, '${name}', rm);
}
};`);
} }
}); });
@@ -333,7 +328,7 @@ QWeb.addDirective({
QWeb.addDirective({ QWeb.addDirective({
name: "mounted", name: "mounted",
priority: 97, priority: 97,
atNodeCreation({ ctx, fullName, value, nodeID }) { atNodeCreation({ ctx, fullName, value, nodeID, addNodeHook }) {
ctx.rootContext.shouldDefineOwner = true; ctx.rootContext.shouldDefineOwner = true;
const eventName = fullName.slice(5); const eventName = fullName.slice(5);
if (!eventName) { if (!eventName) {
@@ -359,8 +354,9 @@ QWeb.addDirective({
`extra.mountedHandlers[${nodeID}] = extra.mountedHandlers[${nodeID}] || (context['${handler}'] || ${error}).bind(owner);` `extra.mountedHandlers[${nodeID}] = extra.mountedHandlers[${nodeID}] || (context['${handler}'] || ${error}).bind(owner);`
); );
} }
ctx.addLine(`p${nodeID}.hook = { addNodeHook(
insert: (vn) => { if (context.__owl__.isMounted) { extra.mountedHandlers[${nodeID}](); } }, "insert",
};`); `if (context.__owl__.isMounted) { extra.mountedHandlers[${nodeID}](); }`
);
} }
}); });
+21 -15
View File
@@ -10,16 +10,16 @@ exports[`animations t-transition, on a simple node 1`] = `
var vn2 = h('span', p2, c2); var vn2 = h('span', p2, c2);
c1.push(vn2); c1.push(vn2);
p2.hook = { p2.hook = {
create: (_, n) => { create: (_, n) => {
this.utils.transitionCreate(n.elm, 'chimay'); this.utils.transitionCreate(n.elm, 'chimay');
}, },
insert: vn => { insert: vn => {
this.utils.transitionInsert(vn.elm, 'chimay'); this.utils.transitionInsert(vn.elm, 'chimay');
}, },
remove: (vn, rm) => { remove: (vn, rm) => {
this.utils.transitionRemove(vn.elm, 'chimay', rm); this.utils.transitionRemove(vn.elm, 'chimay', rm);
} },
}; };
c2.push({text: \`blue\`}); c2.push({text: \`blue\`});
return vn1; return vn1;
}" }"
@@ -1356,8 +1356,10 @@ exports[`t-ref can get a dynamic ref on a node 1`] = `
c1.push(vn2); c1.push(vn2);
const ref3 = 'myspan' + 3 const ref3 = 'myspan' + 3
p2.hook = { p2.hook = {
create: (_, n) => context.refs[ref3] = n.elm, create: (_, n) => {
}; context.refs[ref3] = n.elm;
},
};
return vn1; return vn1;
}" }"
`; `;
@@ -1373,8 +1375,10 @@ exports[`t-ref can get a ref on a node 1`] = `
c1.push(vn2); c1.push(vn2);
const ref3 = 'myspan' const ref3 = 'myspan'
p2.hook = { p2.hook = {
create: (_, n) => context.refs[ref3] = n.elm, create: (_, n) => {
}; context.refs[ref3] = n.elm;
},
};
return vn1; return vn1;
}" }"
`; `;
@@ -1403,7 +1407,9 @@ exports[`t-ref refs in a loop 1`] = `
c1.push(vn5); c1.push(vn5);
const ref6 = context['item'] const ref6 = context['item']
p5.hook = { p5.hook = {
create: (_, n) => context.refs[ref6] = n.elm, create: (_, n) => {
context.refs[ref6] = n.elm;
},
}; };
var _7 = context['item']; var _7 = context['item'];
if (_7 || _7 === 0) { if (_7 || _7 === 0) {
+28
View File
@@ -2064,4 +2064,32 @@ describe("t-mounted directive", () => {
await nextTick(); await nextTick();
expect(widget.f).toHaveBeenCalledTimes(1); expect(widget.f).toHaveBeenCalledTimes(1);
}); });
test("combined with a t-ref", async () => {
class TestWidget extends Widget {
inlineTemplate = `<div><input t-ref="'input'" t-mounted="f"/></div>`;
f() {}
}
const widget = new TestWidget(env);
widget.f = jest.fn();
await widget.mount(fixture);
expect(widget.refs.input).toBeDefined();
expect(widget.f).toHaveBeenCalledTimes(1);
});
test("combined with a t-transition", async () => {
class TestWidget extends Widget {
inlineTemplate = `<div><span t-if="state.flag" t-mounted="f" t-transition="chimay">blue</span></div>`;
state = { flag: false };
f() {}
}
const widget = new TestWidget(env);
widget.f = jest.fn();
await widget.mount(fixture);
expect(widget.f).toHaveBeenCalledTimes(0);
widget.state.flag = true;
await nextTick();
expect(widget.f).toHaveBeenCalledTimes(1);
});
}); });