aurelia-binding
Version:
A modern databinding library for JavaScript and HTML.
144 lines (130 loc) • 4.13 kB
JavaScript
import {bindingMode} from './binding-mode';
import {connectable} from './connectable-binding';
import {enqueueBindingConnect} from './connect-queue';
import {sourceContext, targetContext} from './call-context';
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
);
}
}
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 || mode === bindingMode.fromView ? 'getObserver' : 'getAccessor';
this.targetObserver = this.observerLocator[method](this.target, this.targetProperty);
}
if ('bind' in this.targetObserver) {
this.targetObserver.bind();
}
if (this.mode !== bindingMode.fromView) {
let value = this.sourceExpression.evaluate(source, this.lookupFunctions);
this.updateTarget(value);
}
if (mode === bindingMode.oneTime) {
return;
} else if (mode === bindingMode.toView) {
enqueueBindingConnect(this);
} else if (mode === bindingMode.twoWay) {
this.sourceExpression.connect(this, source);
this.targetObserver.subscribe(targetContext, this);
} else if (mode === bindingMode.fromView) {
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);
}
}