Added named parameters support for xml binding definition.

This commit is contained in:
Nedyalko Nikolov
2015-03-17 16:01:48 +02:00
parent 0118d5f6d1
commit b569451bc7
6 changed files with 209 additions and 46 deletions

View File

@@ -302,6 +302,7 @@
<TypeScriptCompile Include="trace\trace.ts">
<DependentUpon>trace.d.ts</DependentUpon>
</TypeScriptCompile>
<TypeScriptCompile Include="ui\builder\binding-builder.ts" />
<TypeScriptCompile Include="ui\list-picker\list-picker-common.ts">
<DependentUpon>list-picker.d.ts</DependentUpon>
</TypeScriptCompile>

View File

@@ -9,6 +9,7 @@ import buttonModule = require("ui/button");
import utils = require("utils/utils");
import pageModule = require("ui/page");
import stackLayoutModule = require("ui/layouts/stack-layout");
import bindingBuilder = require("ui/builder/binding-builder");
// <snippet module="ui/core/bindable" title="bindable">
// For information and examples how to use bindings please refer to special [**Data binding**](../../../../bindings.md) topic.
@@ -383,7 +384,7 @@ export var test_Bindable_BindingContext_String_DoesNotThrow = function () {
export var test_getBindableOptionsFromStringFullFormat = function () {
var bindingExpression = "bindProperty, bindProperty * 2, false";
var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
@@ -393,7 +394,7 @@ export var test_getBindableOptionsFromStringFullFormat = function () {
export var test_getBindableOptionsFromStringShortFormatExpression = function () {
var bindingExpression = "bindProperty * 2";
var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
@@ -403,17 +404,56 @@ export var test_getBindableOptionsFromStringShortFormatExpression = function ()
export var test_getBindableOptionsFromStringShortFormatProperty = function () {
var bindingExpression = "bindProperty";
var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
TKUnit.assert(bindOptions.expression === null, "Expected: null, Actual: " + bindOptions.expression);
TKUnit.assert(bindOptions.expression === undefined, "Expected: null, Actual: " + bindOptions.expression);
TKUnit.assert(bindOptions.twoWay === true, "Expected: true, Actual: " + bindOptions.twoWay);
}
export var test_getBindableOptionsFromStringTwoParamsFormat = function () {
var bindingExpression = "bindProperty, bindProperty * 2";
var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
TKUnit.assert(bindOptions.expression === "bindProperty * 2", "Expected: bindProperty * 2, Actual:" + bindOptions.expression);
TKUnit.assert(bindOptions.twoWay === true, "Expected: true, Actual: " + bindOptions.twoWay);
}
export var test_getBindableOptionsFromStringFullNamedFormat = function () {
var bindingExpression = "bindProperty, expression = bindProperty * 2, twoWay = false";
var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
TKUnit.assert(bindOptions.expression === "bindProperty * 2", "Expected: bindProperty * 2, Actual:" + bindOptions.expression);
TKUnit.assert(bindOptions.twoWay === false, "Expected: false, Actual: " + bindOptions.twoWay);
}
export var test_getBindableOptionsFromStringShortNamedFormatExpression = function () {
var bindingExpression = "sourceProperty = bindProperty, expression = bindProperty * 2";
var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
TKUnit.assert(bindOptions.expression === "bindProperty * 2", "Expected: bindProperty * 2, Actual: " + bindOptions.expression);
TKUnit.assert(bindOptions.twoWay === true, "Expected: true, Actual: " + bindOptions.twoWay);
}
export var test_getBindableOptionsFromStringShortNamedFormatProperty = function () {
var bindingExpression = "sourceProperty = bindProperty";
var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
TKUnit.assert(bindOptions.expression === undefined, "Expected: null, Actual: " + bindOptions.expression);
TKUnit.assert(bindOptions.twoWay === true, "Expected: true, Actual: " + bindOptions.twoWay);
}
export var test_getBindableOptionsFromStringTwoParamsNamedFormat = function () {
var bindingExpression = "bindProperty, expression = bindProperty * 2";
var bindOptions = bindingBuilder.getBindingOptions("targetBindProperty", bindingExpression);
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);

View File

@@ -0,0 +1,154 @@
var expressionSymbolsRegex = /[ \+\-\*%\?:<>=!\|&\(\)\[\]]/;
export module bindingConstants {
export var sourceProperty = "sourceProperty";
export var targetProperty = "targetProperty";
export var expression = "expression";
export var twoWay = "twoWay";
export var source = "source";
};
var hasEqualSignRegex = /=+/;
var equalSignComparisionOperatorsRegex = /(==|===|>=|<=|!=|!==)/;
function isNamedParam(value) {
var equalSignIndex = value.search(hasEqualSignRegex);
if (equalSignIndex > -1) {
var equalSignSurround = value.substr(equalSignIndex > 0 ? equalSignIndex - 1 : 0, 3);
if (equalSignSurround.search(equalSignComparisionOperatorsRegex) === -1) {
return true;
}
}
return false;
}
function areNamedParams(params: Array<any>) {
var i;
for (i = 0; i < params.length; i++) {
if (isNamedParam(params[i])) {
return true;
}
}
return false;
}
var namedParamConstants = {
propName: "propName",
propValue: "propValue"
}
function getPropertyNameValuePair(param, knownOptions, callback) {
var nameValuePair = {};
var propertyName = param.substr(0, param.indexOf("=")).trim();
var propertyValue = param.substr(param.indexOf("=") + 1).trim();
if (knownOptions) {
if (!propertyName) {
propertyName = knownOptions.defaultProperty;
}
else {
propertyName = propertyName in knownOptions ? propertyName : null;
}
}
if (propertyName) {
if (callback) {
nameValuePair = callback(propertyName, propertyValue);
}
else {
nameValuePair[namedParamConstants.propName] = propertyName;
nameValuePair[namedParamConstants.propValue] = propertyValue;
}
return nameValuePair;
}
return null;
}
function parseNamedProperties(parameterList, knownOptions, callback) {
var result = {};
var i;
for (i = 0; i < parameterList.length; i++) {
var nameValuePair = getPropertyNameValuePair(parameterList[i], knownOptions, callback);
if (nameValuePair) {
result[nameValuePair[namedParamConstants.propName]] = nameValuePair[namedParamConstants.propValue];
}
}
return result;
}
function extractPropertyNameFromExpression(expression: string): string {
var firstExpressionSymbolIndex = expression.search(expressionSymbolsRegex);
if (firstExpressionSymbolIndex > -1) {
return expression.substr(0, firstExpressionSymbolIndex).trim();
}
else {
return expression;
}
}
export function getBindingOptions(name: string, value: string): any {
var namedParams = [];
var params = value.split(",");
if (!areNamedParams(params)) {
if (params.length === 1) {
namedParams.push(bindingConstants.sourceProperty + " = " + extractPropertyNameFromExpression(params[0].trim()));
var expression = params[0].search(expressionSymbolsRegex) > -1 ? params[0].trim() : null;
if (expression) {
namedParams.push(bindingConstants.expression + " = " + expression);
}
namedParams.push(bindingConstants.twoWay + " = true");
}
else {
namedParams.push(bindingConstants.sourceProperty + " = " + extractPropertyNameFromExpression(params[0].trim()));
namedParams.push(bindingConstants.expression + " = " + params[1].trim());
var twoWay = params[2] ? params[2].toLowerCase().trim() === "true" : true;
namedParams.push(bindingConstants.twoWay + " = " + twoWay);
}
}
else {
namedParams = params;
}
var bindingPropertyHandler = function (prop, value) {
var result = {};
result[namedParamConstants.propName] = prop;
if (prop === bindingConstants.twoWay) {
// create a real boolean value
if (value === "true") {
result[namedParamConstants.propValue] = true;
}
else {
result[namedParamConstants.propValue] = false;
}
}
else {
result[namedParamConstants.propValue] = value;
}
return result;
};
var bindingOptionsParameters = parseNamedProperties(namedParams, xmlBindingProperties, bindingPropertyHandler);
var bindOptions = {
targetProperty: name
};
for (var prop in bindingOptionsParameters) {
if (bindingOptionsParameters.hasOwnProperty(prop)) {
bindOptions[prop] = bindingOptionsParameters[prop];
}
}
if (bindOptions[bindingConstants.twoWay] === undefined) {
bindOptions[bindingConstants.twoWay] = true;
}
return bindOptions;
}
var xmlBindingProperties = {
sourceProperty: true,
expression: true,
twoWay: true,
source: true,
defaultProperty: bindingConstants.sourceProperty
};

View File

@@ -8,6 +8,7 @@ import types = require("utils/types");
import definition = require("ui/builder/component-builder");
import fs = require("file-system");
import gestures = require("ui/gestures");
import bindingBuilder = require("ui/builder/binding-builder");
var KNOWNEVENTS = "knownEvents";
var UI_PATH = "ui/";
@@ -76,7 +77,13 @@ export function getComponentModule(elementName: string, namespace: string, attri
if (isKnownEvent(attr, instanceModule)) {
attachEventBinding(instance, attr, attrValue);
} else {
instance.bind(getBinding(instance, attr, attrValue));
var bindOptions = bindingBuilder.getBindingOptions(attr, getBindingExpressionFromAttribute(attrValue));
instance.bind({
sourceProperty : bindOptions[bindingBuilder.bindingConstants.sourceProperty],
targetProperty: bindOptions[bindingBuilder.bindingConstants.targetProperty],
expression: bindOptions[bindingBuilder.bindingConstants.expression],
twoWay: bindOptions[bindingBuilder.bindingConstants.twoWay]
}, bindOptions[bindingBuilder.bindingConstants.source]);
}
} else if (isKnownEvent(attr, instanceModule)) {
// Get the event handler from page module exports.
@@ -160,10 +167,6 @@ function isKnownEvent(name: string, exports: any): boolean {
(KNOWNEVENTS in view && name in view[KNOWNEVENTS]);
}
function getBinding(instance: view.View, name: string, value: string): bindable.BindingOptions {
return bindable.Bindable._getBindingOptions(name, getBindingExpressionFromAttribute(value));
}
function getBindingExpressionFromAttribute(value: string): string {
return value.replace("{{", "").replace("}}", "").trim();
}
@@ -177,4 +180,4 @@ function isBinding(value: string): boolean {
}
return isBinding;
}
}

View File

@@ -66,7 +66,6 @@
unbind(property: string);
//@private
static _getBindingOptions(name: string, bindingExpression: string): BindingOptions;
_updateTwoWayBinding(propertyName: string, value: any);
_onBindingContextChanged(oldValue: any, newValue: any);
//@endprivate

View File

@@ -6,8 +6,6 @@ import types = require("utils/types");
import trace = require("trace");
import polymerExpressions = require("js-libs/polymer-expressions");
var expressionSymbolsRegex = /[ \+\-\*%\?:<>=!\|&\(\)\[\]]/;
var bindingContextProperty = new dependencyObservable.Property(
"bindingContext",
"Bindable",
@@ -116,38 +114,6 @@ export class Bindable extends dependencyObservable.DependencyObservable implemen
}
}
}
private static extractPropertyNameFromExpression(expression: string): string {
var firstExpressionSymbolIndex = expression.search(expressionSymbolsRegex);
if (firstExpressionSymbolIndex > -1) {
return expression.substr(0, firstExpressionSymbolIndex).trim();
}
else {
return expression;
}
}
public static _getBindingOptions(name: string, bindingExpression: string): definition.BindingOptions {
var result: definition.BindingOptions;
result = {
targetProperty: name,
sourceProperty: ""
};
if (types.isString(bindingExpression)) {
var params = bindingExpression.split(",");
if (params.length === 1) {
result.sourceProperty = Bindable.extractPropertyNameFromExpression(params[0].trim());
result.expression = params[0].search(expressionSymbolsRegex) > -1 ? params[0].trim() : null;
result.twoWay = true;
}
else {
result.sourceProperty = Bindable.extractPropertyNameFromExpression(params[0].trim());
result.expression = params[1].trim();
result.twoWay = params[2] ? params[2].toLowerCase().trim() === "true" : true;
}
}
return result;
}
}
export class Binding {