Skip to content

Commit

Permalink
Merge pull request #117 from bigopon/templating-binding-let
Browse files Browse the repository at this point in the history
feat(Let): support let element
  • Loading branch information
EisenbergEffect authored Sep 29, 2018
2 parents 557ddd7 + 863b6ef commit 18bec79
Show file tree
Hide file tree
Showing 7 changed files with 614 additions and 6 deletions.
42 changes: 38 additions & 4 deletions dist/aurelia-templating-binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,25 @@ import {
} from 'aurelia-binding';
import {
BehaviorInstruction,
BindingLanguage
BindingLanguage,
ViewResources
} from 'aurelia-templating';
export declare class AttributeMap {
static inject: any;
elements: any;
allElements: any;
constructor(svg?: any);

/**
* Maps a specific HTML element attribute to a javascript property.
*/
register(elementName?: any, attributeName?: any, propertyName?: any): any;

/**
* Maps an HTML attribute to a javascript property.
*/
registerUniversal(attributeName?: any, propertyName?: any): any;

/**
* Returns the javascript property name for a particlar HTML attribute.
*/
Expand Down Expand Up @@ -76,12 +77,45 @@ export declare class SyntaxInterpreter {
'two-way'(resources?: any, element?: any, info?: any, existingInstruction?: any): any;
'to-view'(resources?: any, element?: any, info?: any, existingInstruction?: any): any;
'from-view'(resources?: any, element?: any, info?: any, existingInstruction?: any): any;
'one-way'(resources?: any, element?: any, info?: any, existingInstruction?: any): any;
'one-time'(resources?: any, element?: any, info?: any, existingInstruction?: any): any;
}

export declare class LetExpression {
createBinding(): LetBinding
}

export declare class LetBinding {
constructor();
updateSource(): any;
call(context): any;
bind(source?: any): any;
unbind(): any;
connect(): any;
}

export declare class LetInterpolationBindingExpression {
createBinding(): LetInterpolationBinding
}

export declare class LetInterpolationBinding {
constructor();
updateSource(): any;
call(context): any;
bind(source?: any): any;
unbind(): any;
connect(): any;
}

export declare class TemplatingBindingLanguage extends BindingLanguage {
static inject: any;
constructor(parser?: any, observerLocator?: any, syntaxInterpreter?: any, attributeMap?: any);
inspectAttribute(resources?: any, elementName?: any, attrName?: any, attrValue?: any): any;
createLetExpressions(
resources: ViewResources,
letElement: HTMLElement,
existingLetExpressions: (LetExpression | LetInterpolationBindingExpression)[]
): (LetExpression | LetInterpolationBindingExpression)[]
createAttributeInstruction(resources?: any, element?: any, theInfo?: any, existingInstruction?: any, context?: any): any;
inspectTextContent(resources?: any, value?: any): any;
parseInterpolation(resources?: any, value?: any): any;
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
"type": "git",
"url": "http://github.com/aurelia/templating-binding"
},
"scripts": {
"test": "karma start"
},
"jspm": {
"registry": "npm",
"jspmPackage": true,
Expand Down
75 changes: 74 additions & 1 deletion src/binding-language.js
Original file line number Diff line number Diff line change
@@ -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-expression';
import * as LogManager from 'aurelia-logging';

let info = {};

Expand All @@ -18,6 +21,7 @@ export class TemplatingBindingLanguage extends BindingLanguage {
this.emptyStringExpression = this.parser.parse('\'\'');
syntaxInterpreter.language = this;
this.attributeMap = attributeMap;
this.toBindingContextAttr = 'to-binding-context';
}

inspectAttribute(resources, elementName, attrName, attrValue) {
Expand Down Expand Up @@ -86,6 +90,75 @@ export class TemplatingBindingLanguage extends BindingLanguage {
return instruction;
}

/**
* @param {ViewResources} resources
* @param {Element} letElement
*/
createLetExpressions(resources, letElement) {
let expressions = [];
let attributes = letElement.attributes;
/**@type {Attr} */
let attr;
/**@type {string[]} */
let parts;
let attrName;
let attrValue;
let command;
let toBindingContextAttr = this.toBindingContextAttr;
let toBindingContext = letElement.hasAttribute(toBindingContextAttr);
for (let i = 0, ii = attributes.length; ii > i; ++i) {
attr = attributes[i];
attrName = attr.name;
attrValue = attr.nodeValue;
parts = attrName.split('.');

if (attrName === toBindingContextAttr) {
continue;
}

if (parts.length === 2) {
command = parts[1];
if (command !== 'bind') {
LogManager.getLogger('templating-binding-language')
.warn(`Detected invalid let command. Expected "${parts[0]}.bind", given "${attrName}"`);
continue;
}
expressions.push(new LetExpression(
this.observerLocator,
camelCase(parts[0]),
this.parser.parse(attrValue),
resources.lookupFunctions,
toBindingContext
));
} 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) {
expressions.push(new LetInterpolationBindingExpression(
this.observerLocator,
attrName,
parts,
resources.lookupFunctions,
toBindingContext
));
} else {
expressions.push(new LetExpression(
this.observerLocator,
attrName,
new LiteralString(attrValue),
resources.lookupFunctions,
toBindingContext
));
}
}
}
return expressions;
}

inspectTextContent(resources, value) {
const parts = this.parseInterpolation(resources, value);
if (parts === null) {
Expand Down
112 changes: 112 additions & 0 deletions src/let-expression.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import {
connectable,
enqueueBindingConnect,
sourceContext
} from 'aurelia-binding';

export class LetExpression {
/**
* @param {ObserverLocator} observerLocator
* @param {string} targetProperty
* @param {Expression} sourceExpression
* @param {any} lookupFunctions
* @param {boolean} toBindingContext indicates let binding result should be assigned to binding context
*/
constructor(observerLocator, targetProperty, sourceExpression, lookupFunctions, toBindingContext) {
this.observerLocator = observerLocator;
this.sourceExpression = sourceExpression;
this.targetProperty = targetProperty;
this.lookupFunctions = lookupFunctions;
this.toBindingContext = toBindingContext;
}

createBinding() {
return new Let(
this.observerLocator,
this.sourceExpression,
this.targetProperty,
this.lookupFunctions,
this.toBindingContext
);
}
}

@connectable()
export class Let {
/**
* @param {ObserverLocator} observerLocator
* @param {Expression} sourceExpression
* @param {Function | Element} target
* @param {string} targetProperty
* @param {*} lookupFunctions
* @param {boolean} toBindingContext indicates let binding result should be assigned to binding context
*/
constructor(observerLocator, sourceExpression, targetProperty, lookupFunctions, toBindingContext) {
this.observerLocator = observerLocator;
this.sourceExpression = sourceExpression;
this.targetProperty = targetProperty;
this.lookupFunctions = lookupFunctions;
this.source = null;
this.target = null;
this.toBindingContext = toBindingContext;
}

updateTarget() {
const value = this.sourceExpression.evaluate(this.source, this.lookupFunctions);
this.target[this.targetProperty] = value;
}

call(context) {
if (!this.isBound) {
return;
}
if (context === sourceContext) {
this.updateTarget();
return;
}
throw new Error(`Unexpected call context ${context}`);
}

/**
* @param {Scope} source Binding context
*/
bind(source) {
if (this.isBound) {
if (this.source === source) {
return;
}
this.unbind();
}

this.isBound = true;
this.source = source;
this.target = this.toBindingContext ? source.bindingContext : source.overrideContext;

if (this.sourceExpression.bind) {
this.sourceExpression.bind(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.target = null;
this.unobserve(true);
}

connect() {
if (!this.isBound) {
return;
}
this.updateTarget();
this.sourceExpression.connect(this, this.source);
}
}
Loading

0 comments on commit 18bec79

Please sign in to comment.