##// END OF EJS Templates
Corrected Scope.own() to cleanup the supplied object immediately when the scope is disposed already
Corrected Scope.own() to cleanup the supplied object immediately when the scope is disposed already

File last commit:

r118:e07418577cbc v1.6.1 default
r131:c7d9ad82b374 v1.8.1 default
Show More
WatchRendition.ts
98 lines | 2.9 KiB | video/mp2t | TypeScriptLexer
/ djx / src / main / ts / tsx / WatchRendition.ts
import { id as mid } from "module";
import { TraceSource } from "@implab/core-amd/log/TraceSource";
import { argumentNotNull } from "@implab/core-amd/safe";
import { getItemDom, getScope, scheduleRender } from "./render";
import { RenditionBase } from "./RenditionBase";
import { Scope } from "./Scope";
import { Subscribable } from "../observable";
import { Cancellation } from "@implab/core-amd/Cancellation";
import { collectNodes, destroy, isDocumentFragmentNode, isMounted, placeAt, startupWidgets } from "./traits";
const trace = TraceSource.get(mid);
export class WatchRendition<T> extends RenditionBase<Node> {
private readonly _component: (arg: T) => unknown;
private readonly _node: Node;
private readonly _scope = new Scope();
private readonly _subject: Subscribable<T>;
private _renderJob?: { value: T };
private _ct = Cancellation.none;
constructor(component: (arg: T) => unknown, subject: Subscribable<T>) {
super();
argumentNotNull(component, "component");
this._component = component;
this._subject = subject;
this._node = document.createComment("[Watch]");
}
protected _create() {
const scope = getScope();
scope.own(() => {
this._scope.destroy();
destroy(this._node);
});
scope.own(this._subject.subscribe({ next: this._onValue }));
this._ct = new Cancellation(cancel => scope.own(cancel));
}
private readonly _onValue = (value: T) => {
if (!this._renderJob) {
// schedule a new job
this._renderJob = { value };
this._render().catch(e => trace.error(e));
} else {
// update existing job
this._renderJob = { value };
}
};
private async _render() {
const beginRender = await scheduleRender(this._scope);
const endRender = beginRender();
try {
// don't render destroyed rendition
if (this._ct.isRequested())
return;
// remove all previous content
this._scope.clean();
// render the new node
const node = getItemDom(this._renderJob ? this._component(this._renderJob.value) : undefined);
// get actual content
const pending = isDocumentFragmentNode(node) ?
collectNodes(node.childNodes) :
[node];
placeAt(node, this._node, "after");
if (isMounted(this._node))
pending.forEach(n => startupWidgets(n));
if (pending.length)
this._scope.own(() => pending.forEach(destroy));
this._renderJob = undefined;
} finally {
endRender();
}
}
protected _getDomNode() {
if (!this._node)
throw new Error("The instance of the widget isn't created");
return this._node;
}
}