[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 {
nodeID?: number;
node: Element;
qweb: QWeb;
ctx: Context;
@@ -39,6 +38,11 @@ interface CompilationInfo {
value: string;
}
interface NodeCreationCompilationInfo extends CompilationInfo {
nodeID: number;
addNodeHook: Function;
}
export interface Directive {
name: string;
extraNames?: string[];
@@ -46,7 +50,7 @@ export interface Directive {
// if return true, then directive is fully applied and there is no need to
// keep processing node. Otherwise, we keep going.
atNodeEncounter?(info: CompilationInfo): boolean | void;
atNodeCreation?(info: CompilationInfo): void;
atNodeCreation?(info: NodeCreationCompilationInfo): void;
finalize?(info: CompilationInfo): void;
}
@@ -87,6 +91,12 @@ const DIRECTIVE_NAMES = {
const DIRECTIVES: Directive[] = [];
const NODE_HOOKS_PARAMS = {
create: "(_, n)",
insert: "vn",
remove: "(vn, rm)"
};
export const UTILS = {
h: h,
objectToAttrString(obj: Object): string {
@@ -497,6 +507,11 @@ export class QWeb {
if (node.nodeName !== "t") {
let nodeID = this._compileGenericNode(node, ctx, withHandlers);
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) {
if (directive.atNodeCreation) {
@@ -506,10 +521,23 @@ export class QWeb {
ctx,
fullName,
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") {
ctx = ctx.subContext("inPreTag", true);
+16 -20
View File
@@ -57,12 +57,10 @@ QWeb.addDirective({
QWeb.addDirective({
name: "ref",
priority: 95,
atNodeCreation({ ctx, nodeID, value }) {
atNodeCreation({ ctx, nodeID, value, addNodeHook }) {
const refKey = `ref${ctx.generateID()}`;
ctx.addLine(`const ${refKey} = ${ctx.formatExpression(value)}`);
ctx.addLine(`p${nodeID}.hook = {
create: (_, n) => context.refs[${refKey}] = n.elm,
};`);
addNodeHook("create", `context.refs[${refKey}] = n.elm;`);
}
});
@@ -148,19 +146,16 @@ function whenTransitionEnd(elm: HTMLElement, cb) {
QWeb.addDirective({
name: "transition",
priority: 96,
atNodeCreation({ ctx, value, nodeID }) {
atNodeCreation({ ctx, value, addNodeHook }) {
let name = value;
ctx.addLine(`p${nodeID}.hook = {
create: (_, n) => {
this.utils.transitionCreate(n.elm, '${name}');
},
insert: vn => {
this.utils.transitionInsert(vn.elm, '${name}');
},
remove: (vn, rm) => {
this.utils.transitionRemove(vn.elm, '${name}', rm);
}
};`);
const hooks = {
create: `this.utils.transitionCreate(n.elm, '${name}');`,
insert: `this.utils.transitionInsert(vn.elm, '${name}');`,
remove: `this.utils.transitionRemove(vn.elm, '${name}', rm);`
};
for (let hookName in hooks) {
addNodeHook(hookName, hooks[hookName]);
}
}
});
@@ -333,7 +328,7 @@ QWeb.addDirective({
QWeb.addDirective({
name: "mounted",
priority: 97,
atNodeCreation({ ctx, fullName, value, nodeID }) {
atNodeCreation({ ctx, fullName, value, nodeID, addNodeHook }) {
ctx.rootContext.shouldDefineOwner = true;
const eventName = fullName.slice(5);
if (!eventName) {
@@ -359,8 +354,9 @@ QWeb.addDirective({
`extra.mountedHandlers[${nodeID}] = extra.mountedHandlers[${nodeID}] || (context['${handler}'] || ${error}).bind(owner);`
);
}
ctx.addLine(`p${nodeID}.hook = {
insert: (vn) => { if (context.__owl__.isMounted) { extra.mountedHandlers[${nodeID}](); } },
};`);
addNodeHook(
"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);
c1.push(vn2);
p2.hook = {
create: (_, n) => {
this.utils.transitionCreate(n.elm, 'chimay');
},
insert: vn => {
this.utils.transitionInsert(vn.elm, 'chimay');
},
remove: (vn, rm) => {
this.utils.transitionRemove(vn.elm, 'chimay', rm);
}
};
create: (_, n) => {
this.utils.transitionCreate(n.elm, 'chimay');
},
insert: vn => {
this.utils.transitionInsert(vn.elm, 'chimay');
},
remove: (vn, rm) => {
this.utils.transitionRemove(vn.elm, 'chimay', rm);
},
};
c2.push({text: \`blue\`});
return vn1;
}"
@@ -1356,8 +1356,10 @@ exports[`t-ref can get a dynamic ref on a node 1`] = `
c1.push(vn2);
const ref3 = 'myspan' + 3
p2.hook = {
create: (_, n) => context.refs[ref3] = n.elm,
};
create: (_, n) => {
context.refs[ref3] = n.elm;
},
};
return vn1;
}"
`;
@@ -1373,8 +1375,10 @@ exports[`t-ref can get a ref on a node 1`] = `
c1.push(vn2);
const ref3 = 'myspan'
p2.hook = {
create: (_, n) => context.refs[ref3] = n.elm,
};
create: (_, n) => {
context.refs[ref3] = n.elm;
},
};
return vn1;
}"
`;
@@ -1403,7 +1407,9 @@ exports[`t-ref refs in a loop 1`] = `
c1.push(vn5);
const ref6 = context['item']
p5.hook = {
create: (_, n) => context.refs[ref6] = n.elm,
create: (_, n) => {
context.refs[ref6] = n.elm;
},
};
var _7 = context['item'];
if (_7 || _7 === 0) {
+28
View File
@@ -2064,4 +2064,32 @@ describe("t-mounted directive", () => {
await nextTick();
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);
});
});