@danielkalen/simplybind
Version:
Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.
108 lines (85 loc) • 2.47 kB
JavaScript
import * as LogManager from 'aurelia-logging';
import {subscriberCollection} from './subscriber-collection';
const logger = LogManager.getLogger('property-observation');
export const propertyAccessor = {
getValue: (obj, propertyName) => obj[propertyName],
setValue: (value, obj, propertyName) => { obj[propertyName] = value; }
};
export class PrimitiveObserver {
doNotCache = true;
constructor(primitive, propertyName) {
this.primitive = primitive;
this.propertyName = propertyName;
}
getValue() {
return this.primitive[this.propertyName];
}
setValue() {
let type = typeof this.primitive;
throw new Error(`The ${this.propertyName} property of a ${type} (${this.primitive}) cannot be assigned.`);
}
subscribe() {
}
unsubscribe() {
}
}
export class SetterObserver {
constructor(taskQueue, obj, propertyName) {
this.taskQueue = taskQueue;
this.obj = obj;
this.propertyName = propertyName;
this.queued = false;
this.observing = false;
}
getValue() {
return this.obj[this.propertyName];
}
setValue(newValue) {
this.obj[this.propertyName] = newValue;
}
getterValue() {
return this.currentValue;
}
setterValue(newValue) {
let oldValue = this.currentValue;
if (oldValue !== newValue) {
if (!this.queued) {
this.oldValue = oldValue;
this.queued = true;
this.taskQueue.queueMicroTask(this);
}
this.currentValue = newValue;
}
}
call() {
let oldValue = this.oldValue;
let newValue = this.currentValue;
this.queued = false;
this.callSubscribers(newValue, oldValue);
}
subscribe(context, callable) {
if (!this.observing) {
this.convertProperty();
}
this.addSubscriber(context, callable);
}
unsubscribe(context, callable) {
this.removeSubscriber(context, callable);
}
convertProperty() {
this.observing = true;
this.currentValue = this.obj[this.propertyName];
this.setValue = this.setterValue;
this.getValue = this.getterValue;
if (!Reflect.defineProperty(this.obj, this.propertyName, {
configurable: true,
enumerable: this.propertyName in this.obj ?
this.obj.propertyIsEnumerable(this.propertyName) : true,
get: this.getValue.bind(this),
set: this.setValue.bind(this)
})) {
logger.warn(`Cannot observe property '${this.propertyName}' of object`, this.obj);
}
}
}