UNPKG

@danielkalen/simplybind

Version:

Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.

139 lines (124 loc) 3.87 kB
import {bindingMode} from './binding-mode'; import {connectable, sourceContext} from './connectable-binding'; import {enqueueBindingConnect} from './connect-queue'; export class BindingExpression { constructor(observerLocator, targetProperty, sourceExpression, mode, lookupFunctions, attribute) { this.observerLocator = observerLocator; this.targetProperty = targetProperty; this.sourceExpression = sourceExpression; this.mode = mode; this.lookupFunctions = lookupFunctions; this.attribute = attribute; this.discrete = false; } createBinding(target) { return new Binding( this.observerLocator, this.sourceExpression, target, this.targetProperty, this.mode, this.lookupFunctions ); } } const targetContext = 'Binding:target'; @connectable() export class Binding { constructor(observerLocator, sourceExpression, target, targetProperty, mode, lookupFunctions) { this.observerLocator = observerLocator; this.sourceExpression = sourceExpression; this.target = target; this.targetProperty = targetProperty; this.mode = mode; this.lookupFunctions = lookupFunctions; } updateTarget(value) { this.targetObserver.setValue(value, this.target, this.targetProperty); } updateSource(value) { this.sourceExpression.assign(this.source, value, this.lookupFunctions); } call(context, newValue, oldValue) { if (!this.isBound) { return; } if (context === sourceContext) { oldValue = this.targetObserver.getValue(this.target, this.targetProperty); newValue = this.sourceExpression.evaluate(this.source, this.lookupFunctions); if (newValue !== oldValue) { this.updateTarget(newValue); } if (this.mode !== bindingMode.oneTime) { this._version++; this.sourceExpression.connect(this, this.source); this.unobserve(false); } return; } if (context === targetContext) { if (newValue !== this.sourceExpression.evaluate(this.source, this.lookupFunctions)) { this.updateSource(newValue); } return; } throw new Error(`Unexpected call context ${context}`); } bind(source) { if (this.isBound) { if (this.source === source) { return; } this.unbind(); } this.isBound = true; this.source = source; if (this.sourceExpression.bind) { this.sourceExpression.bind(this, source, this.lookupFunctions); } let mode = this.mode; if (!this.targetObserver) { let method = mode === bindingMode.twoWay ? 'getObserver' : 'getAccessor'; this.targetObserver = this.observerLocator[method](this.target, this.targetProperty); } if ('bind' in this.targetObserver) { this.targetObserver.bind(); } let value = this.sourceExpression.evaluate(source, this.lookupFunctions); this.updateTarget(value); if (mode === bindingMode.oneWay) { enqueueBindingConnect(this); } else if (mode === bindingMode.twoWay) { this.sourceExpression.connect(this, source); this.targetObserver.subscribe(targetContext, this); } } unbind() { if (!this.isBound) { return; } this.isBound = false; if (this.sourceExpression.unbind) { this.sourceExpression.unbind(this, this.source); } this.source = null; if ('unbind' in this.targetObserver) { this.targetObserver.unbind(); } if (this.targetObserver.unsubscribe) { this.targetObserver.unsubscribe(targetContext, this); } this.unobserve(true); } connect(evaluate) { if (!this.isBound) { return; } if (evaluate) { let value = this.sourceExpression.evaluate(this.source, this.lookupFunctions); this.updateTarget(value); } this.sourceExpression.connect(this, this.source); } }