##// END OF EJS Templates
corrected tear down logic handling in observables. Added support for observable query results
corrected tear down logic handling in observables. Added support for observable query results

File last commit:

r107:e59104632d14 default
r110:1a190b3a757d v1.4.0 default
Show More
WatchRendition.ts
97 lines | 2.7 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 { getScope, render } 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() {
// fork
await Promise.resolve();
// don't render destroyed rendition
if (this._ct.isRequested())
return;
// remove all previous content
this._scope.clean();
// render the new node
const node = render(
this._renderJob ? this._component(this._renderJob.value) : undefined,
this._scope
);
// 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;
}
protected _getDomNode() {
if (!this._node)
throw new Error("The instance of the widget isn't created");
return this._node;
}
}