Permalink
Browse files

feat(Let): support let element

  • Loading branch information...
bigopon committed Sep 24, 2017
1 parent c1a9792 commit bef80fcdeac68d4d510987a03b37811667919b7b
@@ -1,9 +1,12 @@
/*eslint indent:0*/
import {BindingLanguage, BehaviorInstruction} from 'aurelia-templating';
import {Parser, ObserverLocator, NameExpression, bindingMode} from 'aurelia-binding';
import {Parser, ObserverLocator, NameExpression, bindingMode, camelCase, LiteralString} from 'aurelia-binding';
import {InterpolationBindingExpression} from './interpolation-binding-expression';
import {SyntaxInterpreter} from './syntax-interpreter';
import {AttributeMap} from './attribute-map';
import {LetExpression} from './let-expression';
import {LetInterpolationBindingExpression} from './let-interpolation-binding-expression';
import * as LogManager from 'aurelia-logging';
let info = {};
@@ -86,6 +89,67 @@ export class TemplatingBindingLanguage extends BindingLanguage {
return instruction;
}
/**
* @param {ViewResources} resources
* @param {Element} letElement
* @param {(LetExpression | LetInterpolationBindingExpression)[]} existingLetExpressions
*/
createLetExpressions(resources, letElement, existingLetExpressions) {
existingLetExpressions = existingLetExpressions || [];
let attributes = letElement.attributes;
/**@type {Attr} */
let attr;
/**@type {string[]} */
let parts;
let attrName;
let attrValue;
let command;
for (let i = 0, ii = attributes.length; ii > i; ++i) {
attr = attributes[i];
attrName = attr.name;
attrValue = attr.nodeValue;
parts = attrName.split('.');
if (parts.length === 2) {
command = parts[1];
if (command !== 'bind') {
LogManager.getLogger('templating-binding-language')
.warn(`Detected invalid let command. Expected "${part[0]}.bind", given "${attrName}"`);
continue;
}
existingLetExpressions.push(new LetExpression(
this.observerLocator,
camelCase(parts[0]),
this.parser.parse(attrValue),
resources.lookupFunctions
));
} else {
attrName = camelCase(attrName);
parts = this.parseInterpolation(resources, attrValue);
if (parts === null) {
LogManager.getLogger('templating-binding-language')
.warn(`Detected string literal in let bindings. Did you mean "${ attrName }.bind=${ attrValue }" or "${ attrName }=\${${ attrValue }}" ?`);
}
if (parts) {
existingLetExpressions.push(new LetInterpolationBindingExpression(
this.observerLocator,
attrName,
parts,
resources.lookupFunctions
));
} else {
existingLetExpressions.push(new LetExpression(
this.observerLocator,
attrName,
new LiteralString(attrValue),
resources.lookupFunctions
));
}
}
}
return existingLetExpressions;
}
inspectTextContent(resources, value) {
const parts = this.parseInterpolation(resources, value);
if (parts === null) {
@@ -0,0 +1,114 @@
import {
connectable,
enqueueBindingConnect,
createOverrideContext,
sourceContext
} from 'aurelia-binding';
export class LetExpression {
/**
* @param {ObserverLocator} observerLocator
* @param {string} targetProperty
* @param {Expression} sourceExpression
* @param {{}} lookupFunctions
*/
constructor(observerLocator, targetProperty, sourceExpression, lookupFunctions) {
this.observerLocator = observerLocator;
this.sourceExpression = sourceExpression;
this.targetProperty = targetProperty;
this.lookupFunctions = lookupFunctions;
this.discrete = false;
}
createBinding() {
return new Let(
this.observerLocator,
this.sourceExpression,
this.targetProperty,
this.lookupFunctions
);
}
}
@connectable()
export class Let {
/**
*
* @param {ObserverLocator} observerLocator
* @param {Expression} sourceExpression
* @param {Function | Element} target
* @param {string} targetProperty
* @param {*} lookupFunctions
*/
constructor(observerLocator, sourceExpression, targetProperty, lookupFunctions) {
this.observerLocator = observerLocator;
this.sourceExpression = sourceExpression;
this.targetProperty = targetProperty;
this.lookupFunctions = lookupFunctions;
this.source = null;
this.target = null;
}
updateSource() {
const value = this.sourceExpression.evaluate(this.source, this.lookupFunctions);
this.target[this.targetProperty] = value;
}
call(context, newValue, oldValue) {
if (!this.isBound) {
return;
}
if (context === sourceContext) {
this.updateSource();
return;
}
throw new Error(`Unexpected call context ${context}`);
}
/**
* @param {Scope} source Binding context
*/
bind(source) {
if (this.isBound) {
if (this.originalSource === source) {
return;
}
this.unbind();
}
let { bindingContext, parentOverrideContext } = source.overrideContext;
this.isBound = true;
this.originalSource = source;
this.source = createOverrideContext(bindingContext, parentOverrideContext);
this.target = bindingContext;
if (this.sourceExpression.bind) {
this.sourceExpression.bind(this, this.source, this.lookupFunctions);
}
enqueueBindingConnect(this);
}
unbind() {
if (!this.isBound) {
return;
}
this.isBound = false;
if (this.sourceExpression.unbind) {
this.sourceExpression.unbind(this, this.source);
}
this.source = null;
this.originalSource = null;
this.target = null;
this.unobserve(true);
}
connect() {
if (!this.isBound) {
return;
}
this.updateSource();
this.sourceExpression.connect(this, this.source);
}
}
@@ -0,0 +1,103 @@
import {bindingMode, createOverrideContext} from 'aurelia-binding';
import {
InterpolationBinding,
ChildInterpolationBinding
} from './interpolation-binding-expression';
export class LetInterpolationBindingExpression {
/**
* @param {ObserverLocator} observerLocator
* @param {string} targetProperty
* @param {string[]} parts
* @param {Lookups} lookupFunctions
*/
constructor(observerLocator, targetProperty, parts, lookupFunctions) {
this.observerLocator = observerLocator;
this.targetProperty = targetProperty;
this.parts = parts;
this.lookupFunctions = lookupFunctions;
}
createBinding() {
return new LetInterpolationBinding(
this.observerLocator,
this.parts,
this.targetProperty,
this.lookupFunctions
);
}
}
export class LetInterpolationBinding {
/**
*
* @param {ObserverLocator} observerLocator
* @param {strign} targetProperty
* @param {string[]} parts
* @param {Lookups} lookupFunctions
*/
constructor(observerLocator, targetProperty, parts, lookupFunctions) {
this.observerLocator = observerLocator;
this.parts = parts;
this.targetProperty = targetProperty;
this.lookupFunctions = lookupFunctions;
this.target = null;
}
/**
* @param {Scope} source
*/
bind(source) {
if (this.isBound) {
if (this.originalSource === source) {
return;
}
this.unbind();
}
let { bindingContext, parentOverrideContext } = source.overrideContext;
this.isBound = true;
this.originalSource = source;
this.source = createOverrideContext(bindingContext, parentOverrideContext);
this.target = bindingContext;
this.interpolationBinding = this.createInterpolationBinding();
this.interpolationBinding.bind(this.source);
}
unbind() {
if (!this.isBound) {
return;
}
this.isBound = false;
this.source = null;
this.originalSource = null;
this.target = null;
this.interpolationBinding.unbind();
this.interpolationBinding = null;
}
createInterpolationBinding() {
if (this.parts.length === 3) {
return new ChildInterpolationBinding(
this.target,
this.observerLocator,
this.parts[1],
bindingMode.oneWay,
this.lookupFunctions,
this.targetProperty,
this.parts[0],
this.parts[2]
);
}
return new InterpolationBinding(this.observerLocator,
this.parts,
this.target,
this.targetProperty,
bindingMode.oneWay,
this.lookupFunctions
);
}
}
@@ -3,7 +3,18 @@ import {TemplatingBindingLanguage} from '../src/binding-language';
import {Container} from 'aurelia-dependency-injection';
import {NameExpression} from 'aurelia-binding';
import {
LetExpression,
Let
} from '../src/let-expression';
import {
LetInterpolationBindingExpression,
LetInterpolationBinding
} from '../src/let-interpolation-binding-expression';
describe('BindingLanguage', () => {
/**@type {TemplatingBindingLanguage} */
let language;
beforeAll(() => {
@@ -28,4 +39,32 @@ describe('BindingLanguage', () => {
expect(expression instanceof NameExpression).toBe(true);
expect(expression.lookupFunctions).toBe(resources.lookupFunctions);
});
describe('createLetExpressions', () => {
let resources;
beforeEach(() => {
resources = { lookupFunctions: {} };
});
it('creates correct let expressions', () => {
let el1 = div();
el1.setAttribute('foo.bind', 'bar');
expect(language.createLetExpressions(resources, el1)[0] instanceof LetExpression).toBe(true);
let el2 = div();
el2.setAttribute('foo', '${bar}');
expect(language.createLetExpressions(resources, el2)[0] instanceof LetInterpolationBindingExpression).toBe(true);
let el3 = div();
el3.setAttribute('foo', 'bar');
expect(language.createLetExpressions(resources, el3)[0] instanceof LetExpression).toBe(true);
});
function div() {
return document.createElement('div');
}
});
});
Oops, something went wrong.

0 comments on commit bef80fc

Please sign in to comment.