mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] component: observe the state and rerender if it changes
closes #35
This commit is contained in:
@@ -3,6 +3,7 @@ module.exports = {
|
|||||||
transform: {
|
transform: {
|
||||||
"^.+\\.ts?$": "ts-jest"
|
"^.+\\.ts?$": "ts-jest"
|
||||||
},
|
},
|
||||||
|
verbose: false,
|
||||||
testRegex: "(/tests/.*(test|spec))\\.ts?$",
|
testRegex: "(/tests/.*(test|spec))\\.ts?$",
|
||||||
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"]
|
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"]
|
||||||
};
|
};
|
||||||
|
|||||||
+16
-4
@@ -5,6 +5,7 @@ import sdListeners from "../libs/snabbdom/src/modules/eventlisteners";
|
|||||||
import { init } from "../libs/snabbdom/src/snabbdom";
|
import { init } from "../libs/snabbdom/src/snabbdom";
|
||||||
import { VNode } from "../libs/snabbdom/src/vnode";
|
import { VNode } from "../libs/snabbdom/src/vnode";
|
||||||
import { EventBus } from "./event_bus";
|
import { EventBus } from "./event_bus";
|
||||||
|
import { Observer } from "./observer";
|
||||||
import { QWeb } from "./qweb";
|
import { QWeb } from "./qweb";
|
||||||
import { idGenerator } from "./utils";
|
import { idGenerator } from "./utils";
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ export interface Meta<T extends Env, Props> {
|
|||||||
renderProps: Props | null;
|
renderProps: Props | null;
|
||||||
renderPromise: Promise<VNode> | null;
|
renderPromise: Promise<VNode> | null;
|
||||||
boundHandlers: { [key: number]: any };
|
boundHandlers: { [key: number]: any };
|
||||||
|
observer: Observer;
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = init([sdListeners, sdAttrs, sdProps]);
|
const patch = init([sdListeners, sdAttrs, sdProps]);
|
||||||
@@ -118,7 +120,8 @@ export class Component<
|
|||||||
renderId: 1,
|
renderId: 1,
|
||||||
renderPromise: null,
|
renderPromise: null,
|
||||||
renderProps: props || null,
|
renderProps: props || null,
|
||||||
boundHandlers: {}
|
boundHandlers: {},
|
||||||
|
observer: new Observer()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,6 +235,7 @@ export class Component<
|
|||||||
this._visitSubTree(w => {
|
this._visitSubTree(w => {
|
||||||
if (!w.__owl__.isMounted && this.el!.contains(w.el)) {
|
if (!w.__owl__.isMounted && this.el!.contains(w.el)) {
|
||||||
w.__owl__.isMounted = true;
|
w.__owl__.isMounted = true;
|
||||||
|
this._observeState();
|
||||||
w.mounted();
|
w.mounted();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -312,7 +316,6 @@ export class Component<
|
|||||||
if (this.__owl__.isMounted) {
|
if (this.__owl__.isMounted) {
|
||||||
await this.render(true);
|
await this.render(true);
|
||||||
}
|
}
|
||||||
this.patched();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProps(
|
async updateProps(
|
||||||
@@ -340,7 +343,6 @@ export class Component<
|
|||||||
if (this.__owl__.isStarted) {
|
if (this.__owl__.isStarted) {
|
||||||
await this.render();
|
await this.render();
|
||||||
}
|
}
|
||||||
this.patched();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -351,7 +353,6 @@ export class Component<
|
|||||||
await this.willUpdateProps(nextProps);
|
await this.willUpdateProps(nextProps);
|
||||||
this.props = nextProps;
|
this.props = nextProps;
|
||||||
await this.render();
|
await this.render();
|
||||||
this.patched();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_patch(vnode) {
|
_patch(vnode) {
|
||||||
@@ -359,6 +360,7 @@ export class Component<
|
|||||||
if (this.__owl__.vnode) {
|
if (this.__owl__.vnode) {
|
||||||
this.willPatch();
|
this.willPatch();
|
||||||
this.__owl__.vnode = patch(this.__owl__.vnode, vnode);
|
this.__owl__.vnode = patch(this.__owl__.vnode, vnode);
|
||||||
|
this.patched();
|
||||||
} else {
|
} else {
|
||||||
this.__owl__.vnode = patch(document.createElement(vnode.sel!), vnode);
|
this.__owl__.vnode = patch(document.createElement(vnode.sel!), vnode);
|
||||||
}
|
}
|
||||||
@@ -419,6 +421,7 @@ export class Component<
|
|||||||
if (this.__owl__.parent) {
|
if (this.__owl__.parent) {
|
||||||
if (this.__owl__.parent!.__owl__.isMounted) {
|
if (this.__owl__.parent!.__owl__.isMounted) {
|
||||||
this.__owl__.isMounted = true;
|
this.__owl__.isMounted = true;
|
||||||
|
this._observeState();
|
||||||
this.mounted();
|
this.mounted();
|
||||||
const children = this.__owl__.children;
|
const children = this.__owl__.children;
|
||||||
for (let id in children) {
|
for (let id in children) {
|
||||||
@@ -437,4 +440,13 @@ export class Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_observeState() {
|
||||||
|
if (Object.keys(this.state).length) {
|
||||||
|
this.__owl__.observer.observe(this.state);
|
||||||
|
this.__owl__.observer.notifyCB = () => {
|
||||||
|
this.render();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-5
@@ -259,7 +259,8 @@ describe("lifecycle hooks", () => {
|
|||||||
const widget = new ParentWidget(env);
|
const widget = new ParentWidget(env);
|
||||||
await widget.mount(fixture);
|
await widget.mount(fixture);
|
||||||
expect(hookCounter).toBe(0); // sub widget not created yet
|
expect(hookCounter).toBe(0); // sub widget not created yet
|
||||||
await widget.updateState({ ok: true });
|
widget.state.ok = true;
|
||||||
|
await nextTick();
|
||||||
expect(hookCounter).toBe(2);
|
expect(hookCounter).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -566,7 +567,8 @@ describe("lifecycle hooks", () => {
|
|||||||
const widget = new ParentWidget(env);
|
const widget = new ParentWidget(env);
|
||||||
await widget.mount(fixture);
|
await widget.mount(fixture);
|
||||||
expect(steps).toEqual([]);
|
expect(steps).toEqual([]);
|
||||||
await widget.updateState({ n: 2 });
|
widget.state.n = 2;
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
// Not sure about this order. If you disagree, feel free to open an issue...
|
// Not sure about this order. If you disagree, feel free to open an issue...
|
||||||
expect(steps).toEqual([
|
expect(steps).toEqual([
|
||||||
@@ -1181,10 +1183,12 @@ describe("async rendering", () => {
|
|||||||
const w = new W(env);
|
const w = new W(env);
|
||||||
await w.mount(fixture);
|
await w.mount(fixture);
|
||||||
expect(n).toBe(0);
|
expect(n).toBe(0);
|
||||||
w.updateState({ val: 2 });
|
w.state.val = 2;
|
||||||
|
|
||||||
|
await nextMicroTick();
|
||||||
expect(n).toBe(1);
|
expect(n).toBe(1);
|
||||||
await nextTick();
|
w.state.val = 3;
|
||||||
w.updateState({ val: 3 });
|
await nextMicroTick();
|
||||||
expect(n).toBe(2);
|
expect(n).toBe(2);
|
||||||
def.resolve();
|
def.resolve();
|
||||||
await nextTick();
|
await nextTick();
|
||||||
@@ -1391,3 +1395,22 @@ describe("updating environment", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><div>rerendered</div></div>");
|
expect(fixture.innerHTML).toBe("<div><div>rerendered</div></div>");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("widget and observable state", () => {
|
||||||
|
test("widget is rerendered when its state is changed", async () => {
|
||||||
|
class TestWidget extends Widget {
|
||||||
|
state = { drink: "water" };
|
||||||
|
inlineTemplate = `<div><t t-esc="state.drink"/></div>`;
|
||||||
|
}
|
||||||
|
const widget = new TestWidget(env);
|
||||||
|
await widget.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div>water</div>");
|
||||||
|
widget.state.drink = "beer";
|
||||||
|
|
||||||
|
// 2 microtask ticks: one for observer, one for rendering
|
||||||
|
await nextMicroTick();
|
||||||
|
await nextMicroTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div>beer</div>");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user