diff --git a/src/component/component.ts b/src/component/component.ts index cec032a6..f941a8b3 100644 --- a/src/component/component.ts +++ b/src/component/component.ts @@ -28,7 +28,7 @@ export class Component { setup() {} - render(): Promise { - return this.__owl__.render(); + render(deep: boolean = false): Promise { + return this.__owl__.render(deep); } } diff --git a/src/component/component_node.ts b/src/component/component_node.ts index c65809aa..c17d1966 100644 --- a/src/component/component_node.ts +++ b/src/component/component_node.ts @@ -48,7 +48,7 @@ export function component( const parentFiber = ctx.fiber!; if (node) { - if (hasSlots || arePropsDifferent(node.component.props, props)) { + if (hasSlots || parentFiber.deep || arePropsDifferent(node.component.props, props)) { node.updateAndRender(props, parentFiber); } } else { @@ -148,7 +148,7 @@ export class ComponentNode } } - async render() { + async render(deep: boolean = false) { let fiber = this.fiber; if (fiber && !fiber.bdom && !fibersInError.has(fiber)) { return fiber.root.promise; @@ -158,6 +158,7 @@ export class ComponentNode return; } fiber = makeRootFiber(this); + fiber.deep = deep; this.app.scheduler.addFiber(fiber); await Promise.resolve(); if (this.status === STATUS.DESTROYED) { diff --git a/src/component/fibers.ts b/src/component/fibers.ts index 4ffaeb26..9930b8e9 100644 --- a/src/component/fibers.ts +++ b/src/component/fibers.ts @@ -67,6 +67,7 @@ export class Fiber { parent: Fiber | null; children: Fiber[] = []; appliedToDom = false; + deep: boolean = false; constructor(node: ComponentNode, parent: Fiber | null) { this.node = node; diff --git a/tests/components/__snapshots__/rendering.test.ts.snap b/tests/components/__snapshots__/rendering.test.ts.snap index 24686781..185a10de 100644 --- a/tests/components/__snapshots__/rendering.test.ts.snap +++ b/tests/components/__snapshots__/rendering.test.ts.snap @@ -1,5 +1,31 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`rendering semantics can force a render to update sub tree 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers; + + return function template(ctx, node, key = \\"\\") { + return text(\`child\`); + } +}" +`; + +exports[`rendering semantics can force a render to update sub tree 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers; + + return function template(ctx, node, key = \\"\\") { + let b2 = text(ctx['state'].value); + let b3 = component(\`Child\`, {}, key + \`__1\`, node, ctx); + return multi([b2, b3]); + } +}" +`; + exports[`rendering semantics can render a parent without rendering child 1`] = ` "function anonymous(bdom, helpers ) { diff --git a/tests/components/rendering.test.ts b/tests/components/rendering.test.ts index 8350822e..adefc8dc 100644 --- a/tests/components/rendering.test.ts +++ b/tests/components/rendering.test.ts @@ -46,4 +46,41 @@ describe("rendering semantics", () => { expect(parentN).toBe(2); expect(childN).toBe(1); }); + + test("can force a render to update sub tree", async () => { + let childN = 0; + let parentN = 0; + class Child extends Component { + static template = xml`child`; + setup() { + onRender(() => childN++); + } + } + + class Parent extends Component { + static template = xml` + + + `; + static components = { Child }; + + state = { value: "A" }; + setup() { + onRender(() => parentN++); + } + } + + const parent = await mount(Parent, fixture); + + expect(fixture.innerHTML).toBe("Achild"); + expect(parentN).toBe(1); + expect(childN).toBe(1); + + parent.state.value = "B"; + parent.render(true); + await nextTick(); + expect(fixture.innerHTML).toBe("Bchild"); + expect(parentN).toBe(2); + expect(childN).toBe(2); + }); });