UNPKG

aurelia-typed-observable-plugin

Version:
189 lines (165 loc) 6.3 kB
// tslint:disable: interface-name no-invalid-this no-non-null-assertion import * as LogManager from 'aurelia-logging'; import { TaskQueue } from 'aurelia-task-queue'; import { BehaviorPropertyObserver, BindableProperty, HtmlBehaviorResource } from 'aurelia-templating'; import { coerceFunctions } from './coerce-functions'; import { ICoerceFunction } from './interfaces'; /** @internal */ declare module 'aurelia-templating' { interface BehaviorPropertyObserver { coerce?: ICoerceFunction; // currentValue: any; // oldValue: any; // selfSubscriber: Function | null; setCoerce(coerce: string | Function | undefined): void; } interface BindableProperty { name: any; coerce: string | ICoerceFunction | undefined; } } interface ExtendedBehaviorPropertyObserver extends BehaviorPropertyObserver { // coerce?: ICoerceFunction; currentValue: any; oldValue: any; selfSubscriber: Function | null; notqueued: boolean; publishing: boolean; taskQueue: TaskQueue; // setCoerce(coerce: string | Function | undefined): void; } interface BindablePropertyForeignExtension { changeHandler: string; defaultValue: any; owner: HtmlBehaviorResource & { taskQueue: TaskQueue }; } interface ExtendedBindableProperty extends BindableProperty { changeHandler: string; defaultValue: any; owner: HtmlBehaviorResource & { taskQueue: TaskQueue }; } BehaviorPropertyObserver.prototype.setCoerce = function(coerce: string | ICoerceFunction): void { this.coerce = typeof coerce === 'function' ? coerce : coerceFunctions[coerce]; if (this.coerce === undefined) { LogManager .getLogger('behavior-property-observer') .warn(`Invalid coerce instruction. Should be either one of ${Object.keys(coerceFunctions)} or a function.`); } }; /** * Slightly override the builtin implementation that will handle coercion */ BehaviorPropertyObserver.prototype.setValue = function( this: BehaviorPropertyObserver & ExtendedBehaviorPropertyObserver, newValue: any ): void { const oldValue = this.currentValue; const coercedValue = this.coerce === undefined ? newValue : this.coerce(newValue); if (oldValue !== coercedValue) { this.oldValue = oldValue; this.currentValue = coercedValue; if (this.publishing && this.notqueued) { if (this.taskQueue.flushing) { this.call(); } else { this.notqueued = false; this.taskQueue.queueMicroTask(this); } } } }; BindableProperty.prototype.createObserver = function( this: ExtendedBindableProperty, viewModel: any ): BehaviorPropertyObserver { let selfSubscriber: Function | null = null; const defaultValue = this.defaultValue; const changeHandlerName = this.changeHandler; const name = this.name; let initialValue; if ((this as any).hasOptions) { return undefined; } if (changeHandlerName in viewModel) { if ('propertyChanged' in viewModel) { selfSubscriber = (newValue: any, oldValue: any) => { viewModel[changeHandlerName](newValue, oldValue); viewModel.propertyChanged(name, newValue, oldValue); }; } else { selfSubscriber = (newValue: any, oldValue: any) => viewModel[changeHandlerName](newValue, oldValue); } } else if ('propertyChanged' in viewModel) { selfSubscriber = (newValue: any, oldValue: any) => viewModel.propertyChanged(name, newValue, oldValue); } else if (changeHandlerName !== null) { throw new Error(`Change handler ${changeHandlerName} was specified but not declared on the class.`); } if (defaultValue !== undefined) { initialValue = typeof defaultValue === 'function' ? defaultValue.call(viewModel) : defaultValue; } const observer: ExtendedBehaviorPropertyObserver = new BehaviorPropertyObserver( this.owner.taskQueue, viewModel, this.name, selfSubscriber, initialValue ) as any; if (this.coerce !== undefined) { observer.setCoerce(this.coerce); observer.currentValue = observer.oldValue = observer.coerce === undefined ? observer.currentValue : observer.coerce(initialValue); } return observer; }; (BindableProperty as any).prototype._createDynamicProperty = function( this: BindableProperty & BindablePropertyForeignExtension, viewModel: any, observerLookup: Record<string, ExtendedBehaviorPropertyObserver>, behaviorHandlesBind: boolean, name: string, attribute: string | { createBinding(viewModel: any): any }, boundProperties: any[] ): void { const changeHandlerName = `${name}Changed`; let selfSubscriber: Function | null = null; let observer: ExtendedBehaviorPropertyObserver; let info; if (changeHandlerName in viewModel) { if ('propertyChanged' in viewModel) { selfSubscriber = (newValue: any, oldValue: any) => { viewModel[changeHandlerName](newValue, oldValue); viewModel.propertyChanged(name, newValue, oldValue); }; } else { selfSubscriber = (newValue: any, oldValue: any) => viewModel[changeHandlerName](newValue, oldValue); } } else if ('propertyChanged' in viewModel) { selfSubscriber = (newValue: any, oldValue: any) => viewModel.propertyChanged(name, newValue, oldValue); } observer = observerLookup[name] = new BehaviorPropertyObserver( this.owner.taskQueue, viewModel, name, selfSubscriber, undefined ) as ExtendedBehaviorPropertyObserver; observer.setCoerce(this.coerce); observer.currentValue = observer.oldValue = observer.coerce === undefined ? observer.currentValue : observer.coerce(undefined); Object.defineProperty(viewModel, name, { configurable: true, enumerable: true, get: observer.getValue.bind(observer), set: observer.setValue.bind(observer) }); if (behaviorHandlesBind) { observer.selfSubscriber = null; } if (typeof attribute === 'string') { viewModel[name] = attribute; observer.call(); } else if (attribute) { info = { observer: observer, binding: attribute.createBinding(viewModel) }; boundProperties.push(info); } observer.publishing = true; observer.selfSubscriber = selfSubscriber; };