[IMP] add t-mounted directive

closes #45
This commit is contained in:
Aaron Bohy
2019-05-10 15:03:54 +02:00
committed by Géry Debongnie
parent 8ee0973dab
commit 0aba9748c9
4 changed files with 104 additions and 1 deletions
+6
View File
@@ -19,6 +19,7 @@
- [`t-ref` directive](#t-ref-directive)
- [`t-key` directive](#t-key-directive)
- [`t-transition` directive](#t-transition-directive)
- [`t-mounted` directive](#t-mounted-directive)
- [Debugging (`t-debug` and `t-log`)](#debugging-t-debug-and-t-log)
- [White spaces](#white-spaces)
- [Root nodes](#root-nodes)
@@ -352,6 +353,11 @@ For example, a simple fade in/out effect can be done with this:
Note: more information on animations are available [here](doc/animations.md).
### `t-mounted` directive
The `t-mounted` directive allows to register a callback to execute when the node
is inserted into the DOM.
### Debugging (`t-debug` and `t-log`)
The javascript QWeb implementation provides two useful debugging directives:
+7 -1
View File
@@ -40,6 +40,7 @@ export interface Meta<T extends Env, Props> {
boundHandlers: { [key: number]: any };
observer?: Observer;
render?: CompiledTemplate;
mountedHandlers: { [key: number]: Function };
}
//------------------------------------------------------------------------------
@@ -119,7 +120,8 @@ export class Component<
renderId: 1,
renderPromise: null,
renderProps: props || null,
boundHandlers: {}
boundHandlers: {},
mountedHandlers: {}
};
}
@@ -226,6 +228,9 @@ export class Component<
}
}
this.__owl__.isMounted = true;
for (let key in this.__owl__.mountedHandlers) {
this.__owl__.mountedHandlers[key]();
}
this.mounted();
}
@@ -400,6 +405,7 @@ export class Component<
let vnode = this.__owl__.render!(this, {
promises,
handlers: this.__owl__.boundHandlers,
mountedHandlers: this.__owl__.mountedHandlers,
forceUpdate: force,
patchQueue
});
+39
View File
@@ -10,6 +10,7 @@ import { QWeb, UTILS } from "./qweb_core";
* - t-ref
* - t-transition
* - t-widget/t-props/t-keepalive
* - t-mounted
*/
//------------------------------------------------------------------------------
@@ -325,3 +326,41 @@ QWeb.addDirective({
return true;
}
});
//------------------------------------------------------------------------------
// t-mounted
//------------------------------------------------------------------------------
QWeb.addDirective({
name: "mounted",
priority: 97,
atNodeCreation({ ctx, fullName, value, nodeID }) {
ctx.rootContext.shouldDefineOwner = true;
const eventName = fullName.slice(5);
if (!eventName) {
throw new Error("Missing event name with t-on directive");
}
let extraArgs;
let handler = value.replace(/\(.*\)/, function(args) {
extraArgs = args.slice(1, -1);
return "";
});
let error = `(function () {throw new Error('Missing handler \\'' + '${handler}' + \`\\' when evaluating template '${ctx.templateName.replace(
/`/g,
"'"
)}'\`)})()`;
if (extraArgs) {
ctx.addLine(
`extra.mountedHandlers[${nodeID}] = (context['${handler}'] || ${error}).bind(owner, ${ctx.formatExpression(
extraArgs
)});`
);
} else {
ctx.addLine(
`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}](); } },
};`);
}
});
+52
View File
@@ -2013,3 +2013,55 @@ describe("widget and observable state", () => {
expect(fixture.innerHTML).toBe("<div>12</div>");
});
});
describe("t-mounted directive", () => {
test("callback is not called when not in DOM", async () => {
class TestWidget extends Widget {
inlineTemplate = `<div><input t-mounted="f"/></div>`;
f() {}
}
const widget = new TestWidget(env);
widget.f = jest.fn();
await widget.mount(document.createElement("div"));
expect(widget.f).toHaveBeenCalledTimes(0);
});
test("callback is called when in DOM", async () => {
class TestWidget extends Widget {
inlineTemplate = `<div><input t-mounted="f"/></div>`;
f() {}
}
const widget = new TestWidget(env);
widget.f = jest.fn();
await widget.mount(fixture);
expect(widget.f).toHaveBeenCalledTimes(1);
});
test("callback with args is called when in DOM", async () => {
class TestWidget extends Widget {
inlineTemplate = `<div><input t-mounted="f(2)"/></div>`;
f() {}
}
const widget = new TestWidget(env);
widget.f = jest.fn();
await widget.mount(fixture);
expect(widget.f).toHaveBeenCalledTimes(1);
expect(widget.f).toHaveBeenCalledWith(2);
});
test("combined with a t-if", async () => {
class TestWidget extends Widget {
inlineTemplate = `<div><input t-if="state.flag" t-mounted="f"/></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);
});
});