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: {
|
||||
"^.+\\.ts?$": "ts-jest"
|
||||
},
|
||||
verbose: false,
|
||||
testRegex: "(/tests/.*(test|spec))\\.ts?$",
|
||||
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 { VNode } from "../libs/snabbdom/src/vnode";
|
||||
import { EventBus } from "./event_bus";
|
||||
import { Observer } from "./observer";
|
||||
import { QWeb } from "./qweb";
|
||||
import { idGenerator } from "./utils";
|
||||
|
||||
@@ -34,6 +35,7 @@ export interface Meta<T extends Env, Props> {
|
||||
renderProps: Props | null;
|
||||
renderPromise: Promise<VNode> | null;
|
||||
boundHandlers: { [key: number]: any };
|
||||
observer: Observer;
|
||||
}
|
||||
|
||||
const patch = init([sdListeners, sdAttrs, sdProps]);
|
||||
@@ -118,7 +120,8 @@ export class Component<
|
||||
renderId: 1,
|
||||
renderPromise: null,
|
||||
renderProps: props || null,
|
||||
boundHandlers: {}
|
||||
boundHandlers: {},
|
||||
observer: new Observer()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -232,6 +235,7 @@ export class Component<
|
||||
this._visitSubTree(w => {
|
||||
if (!w.__owl__.isMounted && this.el!.contains(w.el)) {
|
||||
w.__owl__.isMounted = true;
|
||||
this._observeState();
|
||||
w.mounted();
|
||||
return true;
|
||||
}
|
||||
@@ -312,7 +316,6 @@ export class Component<
|
||||
if (this.__owl__.isMounted) {
|
||||
await this.render(true);
|
||||
}
|
||||
this.patched();
|
||||
}
|
||||
|
||||
async updateProps(
|
||||
@@ -340,7 +343,6 @@ export class Component<
|
||||
if (this.__owl__.isStarted) {
|
||||
await this.render();
|
||||
}
|
||||
this.patched();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -351,7 +353,6 @@ export class Component<
|
||||
await this.willUpdateProps(nextProps);
|
||||
this.props = nextProps;
|
||||
await this.render();
|
||||
this.patched();
|
||||
}
|
||||
|
||||
_patch(vnode) {
|
||||
@@ -359,6 +360,7 @@ export class Component<
|
||||
if (this.__owl__.vnode) {
|
||||
this.willPatch();
|
||||
this.__owl__.vnode = patch(this.__owl__.vnode, vnode);
|
||||
this.patched();
|
||||
} else {
|
||||
this.__owl__.vnode = patch(document.createElement(vnode.sel!), vnode);
|
||||
}
|
||||
@@ -419,6 +421,7 @@ export class Component<
|
||||
if (this.__owl__.parent) {
|
||||
if (this.__owl__.parent!.__owl__.isMounted) {
|
||||
this.__owl__.isMounted = true;
|
||||
this._observeState();
|
||||
this.mounted();
|
||||
const children = this.__owl__.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);
|
||||
await widget.mount(fixture);
|
||||
expect(hookCounter).toBe(0); // sub widget not created yet
|
||||
await widget.updateState({ ok: true });
|
||||
widget.state.ok = true;
|
||||
await nextTick();
|
||||
expect(hookCounter).toBe(2);
|
||||
});
|
||||
|
||||
@@ -566,7 +567,8 @@ describe("lifecycle hooks", () => {
|
||||
const widget = new ParentWidget(env);
|
||||
await widget.mount(fixture);
|
||||
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...
|
||||
expect(steps).toEqual([
|
||||
@@ -1181,10 +1183,12 @@ describe("async rendering", () => {
|
||||
const w = new W(env);
|
||||
await w.mount(fixture);
|
||||
expect(n).toBe(0);
|
||||
w.updateState({ val: 2 });
|
||||
w.state.val = 2;
|
||||
|
||||
await nextMicroTick();
|
||||
expect(n).toBe(1);
|
||||
await nextTick();
|
||||
w.updateState({ val: 3 });
|
||||
w.state.val = 3;
|
||||
await nextMicroTick();
|
||||
expect(n).toBe(2);
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
@@ -1391,3 +1395,22 @@ describe("updating environment", () => {
|
||||
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