create subclass for inline template views (e.g. without sub classing)

This commit is contained in:
Bruno Windels 2022-01-17 16:25:48 +01:00
parent d673c8714e
commit 164d72830f
2 changed files with 23 additions and 22 deletions

View file

@ -29,7 +29,6 @@ function objHasFns(obj: ClassNames<unknown>): obj is { [className: string]: bool
return false; return false;
} }
export type RenderFn<T> = (t: Builder<T>, vm: T) => ViewNode; export type RenderFn<T> = (t: Builder<T>, vm: T) => ViewNode;
type EventHandler = ((event: Event) => void); type EventHandler = ((event: Event) => void);
type AttributeStaticValue = string | boolean; type AttributeStaticValue = string | boolean;
@ -52,20 +51,13 @@ export type Builder<T> = TemplateBuilder<T> & { [tagName in typeof TAG_NAMES[str
- add subviews inside the template - add subviews inside the template
*/ */
// TODO: should we rename this to BoundView or something? As opposed to StaticView ... // TODO: should we rename this to BoundView or something? As opposed to StaticView ...
export class TemplateView<T extends IObservableValue> extends BaseUpdateView<T> { export abstract class TemplateView<T extends IObservableValue> extends BaseUpdateView<T> {
private _render?: RenderFn<T>;
private _eventListeners?: { node: Element, name: string, fn: EventHandler, useCapture: boolean }[] = undefined; private _eventListeners?: { node: Element, name: string, fn: EventHandler, useCapture: boolean }[] = undefined;
private _bindings?: (() => void)[] = undefined; private _bindings?: (() => void)[] = undefined;
private _root?: ViewNode = undefined; private _root?: ViewNode = undefined;
// public because used by TemplateBuilder // public because used by TemplateBuilder
_subViews?: IView[] = undefined; _subViews?: IView[] = undefined;
constructor(value: T, render?: RenderFn<T>) {
super(value);
// TODO: can avoid this if we have a separate class for inline templates vs class template views
this._render = render;
}
_attach(): void { _attach(): void {
if (this._eventListeners) { if (this._eventListeners) {
for (let {node, name, fn, useCapture} of this._eventListeners) { for (let {node, name, fn, useCapture} of this._eventListeners) {
@ -82,16 +74,12 @@ export class TemplateView<T extends IObservableValue> extends BaseUpdateView<T>
} }
} }
abstract render(t: Builder<T>, value: T): ViewNode;
mount(options?: IMountArgs): ViewNode { mount(options?: IMountArgs): ViewNode {
const builder = new TemplateBuilder(this) as Builder<T>; const builder = new TemplateBuilder(this) as Builder<T>;
try { try {
if (this._render) { this._root = this.render(builder, this._value);
this._root = this._render(builder, this._value);
} else if (this["render"]) { // overriden in subclass
this._root = this["render"](builder, this._value);
} else {
throw new Error("no render function passed in, or overriden in subclass");
}
} finally { } finally {
builder.close(); builder.close();
} }
@ -344,7 +332,7 @@ export class TemplateBuilder<T extends IObservableValue> {
// on mappedValue, use `if` or `mapView` // on mappedValue, use `if` or `mapView`
map<R>(mapFn: (value: T) => R, renderFn: (mapped: R, t: Builder<T>, vm: T) => ViewNode): ViewNode { map<R>(mapFn: (value: T) => R, renderFn: (mapped: R, t: Builder<T>, vm: T) => ViewNode): ViewNode {
return this.mapView(mapFn, mappedValue => { return this.mapView(mapFn, mappedValue => {
return new TemplateView(this._value, (t, vm) => { return new InlineTemplateView(this._value, (t, vm) => {
const rootNode = renderFn(mappedValue, t, vm); const rootNode = renderFn(mappedValue, t, vm);
if (!rootNode) { if (!rootNode) {
// TODO: this will confuse mapView which assumes that // TODO: this will confuse mapView which assumes that
@ -366,7 +354,7 @@ export class TemplateBuilder<T extends IObservableValue> {
// creates a conditional subtemplate // creates a conditional subtemplate
// use mapView if you need to map to a different view class // use mapView if you need to map to a different view class
if(predicate: (value: T) => boolean, renderFn: (t: Builder<T>, vm: T) => ViewNode) { if(predicate: (value: T) => boolean, renderFn: (t: Builder<T>, vm: T) => ViewNode) {
return this.ifView(predicate, vm => new TemplateView(vm, renderFn)); return this.ifView(predicate, vm => new InlineTemplateView(vm, renderFn));
} }
/** You probably are looking for something else, like map or mapView. /** You probably are looking for something else, like map or mapView.
@ -398,3 +386,16 @@ for (const [ns, tags] of Object.entries(TAG_NAMES)) {
}; };
} }
} }
export class InlineTemplateView<T> extends TemplateView<T> {
private _render: RenderFn<T>;
constructor(value: T, render: RenderFn<T>) {
super(value);
this._render = render;
}
override render(t: Builder<T>, value: T): ViewNode {
return this._render(t, value);
}
}

View file

@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {TemplateView} from "../../general/TemplateView"; import {TemplateView, InlineTemplateView} from "../../general/TemplateView";
import {StaticView} from "../../general/StaticView.js"; import {StaticView} from "../../general/StaticView.js";
export class SessionBackupSettingsView extends TemplateView { export class SessionBackupSettingsView extends TemplateView {
render(t, vm) { render(t, vm) {
return t.mapView(vm => vm.status, status => { return t.mapView(vm => vm.status, status => {
switch (status) { switch (status) {
case "Enabled": return new TemplateView(vm, renderEnabled) case "Enabled": return new InlineTemplateView(vm, renderEnabled)
case "SetupKey": return new TemplateView(vm, renderEnableFromKey) case "SetupKey": return new InlineTemplateView(vm, renderEnableFromKey)
case "SetupPhrase": return new TemplateView(vm, renderEnableFromPhrase) case "SetupPhrase": return new InlineTemplateView(vm, renderEnableFromPhrase)
case "Pending": return new StaticView(vm, t => t.p(vm.i18n`Waiting to go online…`)) case "Pending": return new StaticView(vm, t => t.p(vm.i18n`Waiting to go online…`))
} }
}); });