mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
t-widget directive
This commit is contained in:
@@ -5,6 +5,7 @@ const template = `
|
|||||||
<div t-debug="1">
|
<div t-debug="1">
|
||||||
<span>Root Widget</span>
|
<span>Root Widget</span>
|
||||||
<button t-on-click="resetCounter">Reset</button>
|
<button t-on-click="resetCounter">Reset</button>
|
||||||
|
<button t-on-click="resetCounterAsync">Reset in 3s</button>
|
||||||
<button t-on-click="toggle">Toggle Counter</button>
|
<button t-on-click="toggle">Toggle Counter</button>
|
||||||
<input/>
|
<input/>
|
||||||
<t t-if="state.validcounter">
|
<t t-if="state.validcounter">
|
||||||
@@ -23,6 +24,12 @@ export default class RootWidget extends Widget {
|
|||||||
this.refs.counter.updateState({counter: 3})
|
this.refs.counter.updateState({counter: 3})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetCounterAsync(ev: MouseEvent) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.refs.counter.updateState({counter: 3})
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
this.updateState({validcounter: !this.state.validcounter});
|
this.updateState({validcounter: !this.state.validcounter});
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -11,12 +11,12 @@
|
|||||||
// (<any>window).h = h;
|
// (<any>window).h = h;
|
||||||
// (<any>window).patch = patch;
|
// (<any>window).patch = patch;
|
||||||
// (<any>window).QWeb = QWeb;
|
// (<any>window).QWeb = QWeb;
|
||||||
import Counter from "./Counter";
|
// import Counter from "./Counter";
|
||||||
// import RootWidget from "./RootWidget";
|
import RootWidget from "./RootWidget";
|
||||||
import env from "./env";
|
import env from "./env";
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async function() {
|
document.addEventListener("DOMContentLoaded", async function() {
|
||||||
const rootWidget = new Counter(null, {});
|
const rootWidget = new RootWidget(null);
|
||||||
rootWidget.setEnvironment(env);
|
rootWidget.setEnvironment(env);
|
||||||
const mainDiv = document.getElementById("app")!;
|
const mainDiv = document.getElementById("app")!;
|
||||||
await rootWidget.mount(mainDiv);
|
await rootWidget.mount(mainDiv);
|
||||||
|
|||||||
+24
-19
@@ -96,7 +96,8 @@ export default class QWeb {
|
|||||||
elifDirective,
|
elifDirective,
|
||||||
ifDirective,
|
ifDirective,
|
||||||
callDirective,
|
callDirective,
|
||||||
onDirective
|
onDirective,
|
||||||
|
widgetDirective
|
||||||
].forEach(d => this.addDirective(d));
|
].forEach(d => this.addDirective(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,21 +642,25 @@ const onDirective: Directive = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// const widgetDirective: Directive = {
|
const widgetDirective: Directive = {
|
||||||
// name: "widget",
|
name: "widget",
|
||||||
// priority: 100,
|
priority: 100,
|
||||||
// atNodeEncounter({ ctx, fullName, value, node, qweb}): boolean {
|
atNodeEncounter({ ctx, fullName, value, node, qweb}): boolean {
|
||||||
// let spanID = ctx.generateID();
|
let dummyID = ctx.generateID();
|
||||||
// ctx.addLine(`let ${spanID} = document.createElement('span')`);
|
let defID = ctx.generateID();
|
||||||
// ctx.addNode(spanID)
|
ctx.addLine(`let _${dummyID} = {} // DUMMY`);
|
||||||
// let props = node.getAttribute('t-props');
|
ctx.addLine(`c${ctx.parentNode}.push(_${dummyID})`);
|
||||||
// let widgetID = ctx.generateID();
|
let props = node.getAttribute('t-props');
|
||||||
// ctx.addLine(`let ${widgetID} = new context.widgets['${value}'](context, ${props})`);
|
let widgetID = ctx.generateID();
|
||||||
// ctx.addLine(`${widgetID}.mount(${spanID}).then(()=>${ctx.parentNode}.replaceChild(${widgetID}.el, ${spanID}))`);
|
ctx.addLine(`let _${widgetID} = new context.widgets['${value}'](context, ${props})`);
|
||||||
// let ref = node.getAttribute('t-ref');
|
ctx.addLine(`let def${defID} = _${widgetID}.mount().then(vnode=>Object.assign(_${dummyID}, vnode))`);
|
||||||
// if (ref) {
|
ctx.addLine(`context._TEMP.push(def${defID})`);
|
||||||
// ctx.addLine(`context.refs['${ref}'] = ${widgetID}`);
|
|
||||||
// }
|
// split into extra directive?
|
||||||
// return true;
|
let ref = node.getAttribute('t-ref');
|
||||||
// }
|
if (ref) {
|
||||||
// };
|
ctx.addLine(`context.refs['${ref}'] = _${widgetID}`);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
+12
-12
@@ -17,6 +17,7 @@ export default class Widget {
|
|||||||
name: string = "widget";
|
name: string = "widget";
|
||||||
template: string = "<div></div>";
|
template: string = "<div></div>";
|
||||||
vnode: VNode | null = null;
|
vnode: VNode | null = null;
|
||||||
|
_TEMP: Promise<any>[] | null = null;
|
||||||
|
|
||||||
parent: Widget | null;
|
parent: Widget | null;
|
||||||
children: Widget[] = [];
|
children: Widget[] = [];
|
||||||
@@ -50,13 +51,16 @@ export default class Widget {
|
|||||||
// Public
|
// Public
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
async mount(target: HTMLElement) {
|
async mount(target?: HTMLElement): Promise<VNode> {
|
||||||
await this.willStart();
|
await this.willStart();
|
||||||
this.env!.qweb.addTemplate(this.name, this.template);
|
this.env!.qweb.addTemplate(this.name, this.template);
|
||||||
delete this.template;
|
delete this.template;
|
||||||
await this.render();
|
const vnode = await this.render();
|
||||||
|
|
||||||
target.appendChild(this.el!);
|
if (target) {
|
||||||
|
target.appendChild(this.el!);
|
||||||
|
}
|
||||||
|
return vnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
@@ -84,19 +88,15 @@ export default class Widget {
|
|||||||
// Private
|
// Private
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
async render() {
|
async render(): Promise<VNode> {
|
||||||
let vnode = await this.env!.qweb.render(this.name, this);
|
this._TEMP = [];
|
||||||
|
let vnode = this.env!.qweb.render(this.name, this);
|
||||||
|
await Promise.all(this._TEMP);
|
||||||
if (!this.el) {
|
if (!this.el) {
|
||||||
this.el = document.createElement(vnode.sel!);
|
this.el = document.createElement(vnode.sel!);
|
||||||
}
|
}
|
||||||
patch(this.vnode || this.el, vnode);
|
patch(this.vnode || this.el, vnode);
|
||||||
this.vnode = vnode;
|
this.vnode = vnode;
|
||||||
|
return vnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private _setElement(el: ChildNode) {
|
|
||||||
// if (this.el) {
|
|
||||||
// this.el.replaceWith(el);
|
|
||||||
// }
|
|
||||||
// this.el = el;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ describe("attributes", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
xit("various escapes", () => {
|
test.skip("various escapes", () => {
|
||||||
// need to think about this... This one does not pass, but I am not sure it is
|
// need to think about this... This one does not pass, but I am not sure it is
|
||||||
// a correct test
|
// a correct test
|
||||||
const template = `
|
const template = `
|
||||||
|
|||||||
@@ -93,3 +93,23 @@ describe("destroy method", () => {
|
|||||||
expect(document.contains(widget.el)).toBe(false);
|
expect(document.contains(widget.el)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("composition", () => {
|
||||||
|
test("a widget with a sub widget", async () => {
|
||||||
|
|
||||||
|
class WidgetB extends Widget {
|
||||||
|
template= `<div>world</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WidgetA extends Widget {
|
||||||
|
name="a";
|
||||||
|
template= `<div>Hello<t t-widget="b"/></div>`;
|
||||||
|
widgets = {b: WidgetB}
|
||||||
|
}
|
||||||
|
|
||||||
|
const widget = makeWidget(WidgetA);
|
||||||
|
const target = document.createElement("div");
|
||||||
|
await widget.mount(target);
|
||||||
|
expect(target.innerHTML).toBe("<div>Hello<div>world</div></div>");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user