Skip to content

Instantly share code, notes, and snippets.

@lynsei
Created April 19, 2016 09:17
Show Gist options
  • Save lynsei/7dbeb839e2eeef7d0b68d1cc5655863f to your computer and use it in GitHub Desktop.
Save lynsei/7dbeb839e2eeef7d0b68d1cc5655863f to your computer and use it in GitHub Desktop.
<template>
<require from="./dynamic-expression-binding-behavior"></require>
<require from="./debug"></require>
<label>
Address 1:
<input value.bind="model & dynamicExpression:'address.address1'">
</label>
<label>
Address 2:
<input value.bind="model & dynamicExpression:'address.address2'">
</label>
<label>
City:
<input value.bind="model & dynamicExpression:key">
</label>
<label>
State:
<input value.bind="model & dynamicExpression:'address.state'">
</label>
<label>
Zip:
<input value.bind="model & dynamicExpression:'address.zip'">
</label>
<label>
Address 1:
<input value.bind="model & dynamicExpression:'address.address1'">
</label>
<label>
Address 2:
<input value.bind="model & dynamicExpression:'address.address2'">
</label>
<label>
City:
<input value.bind="model & dynamicExpression:key">
</label>
<label>
State:
<input value.bind="model & dynamicExpression:'address.state'">
</label>
<label>
Zip:
<input value.bind="model & dynamicExpression:'address.zip'">
</label>
<debug></debug>
</template>
export class App {
model = {
address: {
address1: '1 Main Street',
address2: '',
city: 'Burlington',
state: 'VT',
zip: '05401'
}
};
key = 'address.city';
}
<template>
<pre><code>${json}</code></pre>
</template>
export class Debug {
bindingContext = null;
updateJson() {
if (this.bindingContext === null) {
this.json = 'null';
} else if (this.bindingContext === undefined) {
this.json = 'undefined'
} else {
// todo: use a stringify function that can handle circular references.
this.json = JSON.stringify(this.bindingContext, null, 2);
}
}
bind(bindingContext) {
this.bindingContext = bindingContext;
this.updateJson();
this.interval = setInterval(::this.updateJson, 150);
}
unbind() {
this.bindingContext = null;
clearInterval(this.interval);
}
}
import {inject} from 'aurelia-dependency-injection';
import {Parser} from 'aurelia-binding';
import './temporary-fix-for-binding';
@inject(Parser)
export class DynamicExpressionBindingBehavior {
constructor(parser) {
this.parser = parser;
}
bind(binding, source, rawExpression) {
// Quick and dirty expression concatenation. A more robust way to do this is
// with a visitor, but this is good enough for the OP's use case.
// When I get some time I will update this with an expression visitor.
let combinedRawExpression = binding.sourceExpression.expression.toString() + '.' + rawExpression;
// parse the combined concatenated expression.
let expression = this.parser.parse(combinedRawExpression);
// replace the binding's expression.
binding.replaceSourceExpression(expression);
}
unbind(binding, source) {
}
}
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
label, input {
display: block;
}
</style>
</head>
<body aurelia-app>
<h1>Loading...</h1>
<script src="https://cdn.rawgit.com/jdanyow/aurelia-bundle/v1.0.3/jspm_packages/system.js"></script>
<script src="https://cdn.rawgit.com/jdanyow/aurelia-bundle/v1.0.3/config.js"></script>
<script>
System.import('aurelia-bootstrapper');
</script>
</body>
</html>
import {Binding, bindingMode} from 'aurelia-binding';
// monkey patch the Binding class to enable
// binding behaviors to entirely replace the sourceExpression
const targetContext = 'Binding:target';
Binding.prototype.bind = function(source) {
if (this.isBound) {
if (this.source === source) {
return;
}
this.unbind();
}
this.isBound = true;
this.source = source;
let sourceExpression = this.sourceExpression;
if (sourceExpression.bind) {
sourceExpression.bind(this, source, this.lookupFunctions);
sourceExpression = this.sourceExpression;
}
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 = sourceExpression.evaluate(source, this.lookupFunctions);
this.updateTarget(value);
if (mode === bindingMode.oneWay) {
enqueueBindingConnect(this);
} else if (mode === bindingMode.twoWay) {
sourceExpression.connect(this, source);
this.targetObserver.subscribe(targetContext, this);
}
};
Binding.prototype.unbind = function() {
if (!this.isBound) {
return;
}
this.isBound = false;
if (this.sourceExpression.unbind) {
this.sourceExpression.unbind(this, this.source);
}
if (this.originalSourceExpression) {
if (this.originalSourceExpression.unbind) {
this.originalSourceExpression.unbind(this, this.source);
}
this.sourceExpression = this.originalSourceExpression;
this.originalSourceExpression = null;
}
this.source = null;
if ('unbind' in this.targetObserver) {
this.targetObserver.unbind();
}
if (this.targetObserver.unsubscribe) {
this.targetObserver.unsubscribe(targetContext, this);
}
this.unobserve(true);
}
Binding.prototype.replaceSourceExpression = function(expression) {
this.originalSourceExpression = this.sourceExpression;
this.sourceExpression = expression;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment