Skip to content

Commit b8fd162

Browse files
committed
Provide live binding for properties and children with function-based widgets (dojo#564)
* support tsx in vdom tests * failing unit test for live property binding * fix up failing unit tests * support live binding to children and properties * abstract widget args to function * use factory for middleware
1 parent 53f062c commit b8fd162

File tree

2 files changed

+107
-37
lines changed

2 files changed

+107
-37
lines changed

src/core/vdom.ts

+32-36
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,27 @@ export function renderer(renderer: () => RenderResult): Renderer {
16311631
return {};
16321632
}
16331633

1634+
function createWidgetOptions(id: string, widgetId: string, middleware?: any) {
1635+
return {
1636+
id,
1637+
properties: () => {
1638+
const widgetMeta = widgetMetaMap.get(widgetId);
1639+
if (widgetMeta) {
1640+
return { ...widgetMeta.properties };
1641+
}
1642+
return {};
1643+
},
1644+
children: () => {
1645+
const widgetMeta = widgetMetaMap.get(widgetId);
1646+
if (widgetMeta) {
1647+
return widgetMeta.children;
1648+
}
1649+
return [];
1650+
},
1651+
middleware
1652+
};
1653+
}
1654+
16341655
function resolveMiddleware(
16351656
middlewares: any,
16361657
id: string,
@@ -1641,23 +1662,7 @@ export function renderer(renderer: () => RenderResult): Renderer {
16411662
const uniqueId = `${id}-${metaId++}`;
16421663
for (let i = 0; i < keys.length; i++) {
16431664
const middleware = middlewares[keys[i]]();
1644-
const payload: any = {
1645-
id: uniqueId,
1646-
properties: () => {
1647-
const widgetMeta = widgetMetaMap.get(id);
1648-
if (widgetMeta) {
1649-
return { ...widgetMeta.properties };
1650-
}
1651-
return {};
1652-
},
1653-
children: () => {
1654-
const widgetMeta = widgetMetaMap.get(id);
1655-
if (widgetMeta) {
1656-
return widgetMeta.children;
1657-
}
1658-
return [];
1659-
}
1660-
};
1665+
const payload = createWidgetOptions(uniqueId, id);
16611666
if (middleware.middlewares) {
16621667
const { middlewares: resolvedMiddleware } = resolveMiddleware(
16631668
middleware.middlewares,
@@ -1731,12 +1736,7 @@ export function renderer(renderer: () => RenderResult): Renderer {
17311736
invalidate = widgetMeta.invalidator;
17321737
}
17331738

1734-
rendered = Constructor({
1735-
id,
1736-
properties: () => next.node.properties,
1737-
children: () => next.node.children,
1738-
middleware: widgetMeta.middleware
1739-
});
1739+
rendered = Constructor(createWidgetOptions(id, id, widgetMeta.middleware));
17401740
widgetMeta.rendering = false;
17411741
if (widgetMeta.deferRefs > 0) {
17421742
return false;
@@ -1786,7 +1786,7 @@ export function renderer(renderer: () => RenderResult): Renderer {
17861786

17871787
function _updateWidget({ current, next }: UpdateWidgetInstruction): ProcessResult {
17881788
current = getWNodeWrapper(current.id) || current;
1789-
const { instance, domNode, hasAnimations } = current;
1789+
const { instance, domNode, hasAnimations, id } = current;
17901790
let {
17911791
node: { widgetConstructor }
17921792
} = next;
@@ -1800,7 +1800,7 @@ export function renderer(renderer: () => RenderResult): Renderer {
18001800
let didRender = false;
18011801
let currentChildren = _idToChildrenWrappers.get(current.id);
18021802
next.hasAnimations = hasAnimations;
1803-
next.id = current.id;
1803+
next.id = id;
18041804
next.childDomWrapperId = current.childDomWrapperId;
18051805
next.properties = next.node.properties;
18061806
_wrapperSiblingMap.delete(current);
@@ -1809,9 +1809,10 @@ export function renderer(renderer: () => RenderResult): Renderer {
18091809
}
18101810

18111811
if (!isWidgetBaseConstructor(Constructor)) {
1812-
const widgetMeta = widgetMetaMap.get(next.id);
1812+
const widgetMeta = widgetMetaMap.get(id);
18131813
if (widgetMeta) {
18141814
widgetMeta.properties = next.properties;
1815+
widgetMeta.children = next.node.children;
18151816
widgetMeta.rendering = true;
18161817
runDiffs(widgetMeta, current.properties, next.properties);
18171818
if (current.node.children.length > 0 || next.node.children.length > 0) {
@@ -1828,15 +1829,10 @@ export function renderer(renderer: () => RenderResult): Renderer {
18281829
);
18291830
}
18301831
if (widgetMeta.dirty) {
1831-
_idToChildrenWrappers.delete(next.id);
1832+
_idToChildrenWrappers.delete(id);
18321833
didRender = true;
18331834
widgetMeta.dirty = false;
1834-
rendered = Constructor({
1835-
id: next.id,
1836-
properties: () => next.node.properties,
1837-
children: () => next.node.children,
1838-
middleware: widgetMeta.middleware
1839-
});
1835+
rendered = Constructor(createWidgetOptions(id, id, widgetMeta.middleware));
18401836
if (widgetMeta.deferRefs > 0) {
18411837
rendered = null;
18421838
}
@@ -1851,19 +1847,19 @@ export function renderer(renderer: () => RenderResult): Renderer {
18511847
instance!.__setChildren__(next.node.children);
18521848
if (instanceData.dirty) {
18531849
didRender = true;
1854-
_idToChildrenWrappers.delete(next.id);
1850+
_idToChildrenWrappers.delete(id);
18551851
rendered = instance!.__render__();
18561852
}
18571853
instanceData.rendering = false;
18581854
}
18591855
_idToWrapperMap.set(next.id, next);
1860-
processResult.widget = { type: 'attach', instance, id: next.id, attached: false };
1856+
processResult.widget = { type: 'attach', instance, id, attached: false };
18611857

18621858
let children: DNodeWrapper[] | undefined;
18631859
if (rendered) {
18641860
rendered = Array.isArray(rendered) ? rendered : [rendered];
18651861
children = renderedToWrapper(rendered, next, current);
1866-
_idToChildrenWrappers.set(next.id, children);
1862+
_idToChildrenWrappers.set(id, children);
18671863
}
18681864

18691865
if (didRender) {

tests/core/unit/vdom.ts tests/core/unit/vdom.tsx

+75-1
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ import {
1717
widgetInstanceMap,
1818
v,
1919
w,
20-
dom as d
20+
dom as d,
21+
tsx
2122
} from '../../../src/core/vdom';
2223
import { VNode, DNode, DomVNode } from '../../../src/core/interfaces';
2324
import { WidgetBase } from '../../../src/core/WidgetBase';
2425
import Registry from '../../../src/core/Registry';
2526
import { I18nMixin } from '../../../src/core/mixins/I18n';
27+
import icache from '../../../src/core/middleware/icache';
2628
import registry from '../../../src/core/decorators/registry';
2729
import { alwaysRender } from '../../../src/core/decorators/alwaysRender';
2830

@@ -3430,6 +3432,78 @@ jsdomDescribe('vdom', () => {
34303432
assert.isTrue(consoleWarnStub.calledOnce);
34313433
});
34323434

3435+
it('properties should have a live binding', () => {
3436+
const factory = create({ icache }).properties<any>();
3437+
3438+
const RunnerWidget = factory(({ properties, middleware: { icache } }) => {
3439+
return (
3440+
<div>
3441+
<button
3442+
onclick={() => {
3443+
const { doSomething } = properties();
3444+
icache.set('value', doSomething());
3445+
}}
3446+
>
3447+
Click me
3448+
</button>
3449+
<div>{icache.getOrSet('value', '')}</div>
3450+
</div>
3451+
);
3452+
});
3453+
3454+
const MyWidget = factory(({ properties }) => {
3455+
return (
3456+
<RunnerWidget
3457+
doSomething={() => {
3458+
return properties().value;
3459+
}}
3460+
/>
3461+
);
3462+
});
3463+
3464+
const App = factory(function App({ middleware: { icache } }) {
3465+
const value = icache.getOrSet('value', '1');
3466+
return (
3467+
<div>
3468+
<button
3469+
onclick={() => {
3470+
icache.set('value', `${value}1`);
3471+
}}
3472+
>
3473+
Increment Value
3474+
</button>
3475+
<MyWidget value={value} />
3476+
</div>
3477+
);
3478+
});
3479+
3480+
const root = document.createElement('div');
3481+
const r = renderer(() => <App />);
3482+
r.mount({ domNode: root });
3483+
(root as any).children[0].children[0].click();
3484+
resolvers.resolve();
3485+
(root as any).children[0].children[1].children[0].click();
3486+
resolvers.resolve();
3487+
assert.strictEqual(
3488+
root.outerHTML,
3489+
'<div><div><button>Increment Value</button><div><button>Click me</button><div>11</div></div></div></div>'
3490+
);
3491+
(root as any).children[0].children[0].click();
3492+
resolvers.resolve();
3493+
(root as any).children[0].children[0].click();
3494+
resolvers.resolve();
3495+
(root as any).children[0].children[0].click();
3496+
resolvers.resolve();
3497+
(root as any).children[0].children[0].click();
3498+
resolvers.resolve();
3499+
(root as any).children[0].children[1].children[0].click();
3500+
resolvers.resolve();
3501+
assert.strictEqual(
3502+
root.outerHTML,
3503+
'<div><div><button>Increment Value</button><div><button>Click me</button><div>111111</div></div></div></div>'
3504+
);
3505+
});
3506+
34333507
describe('core middleware', () => {
34343508
describe('node', () => {
34353509
it('should invalidate widget once node is available', () => {

0 commit comments

Comments
 (0)