mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-17 04:41:36 +08:00
Added binding convert option.
This commit is contained in:
@ -94,6 +94,9 @@
|
|||||||
<TypeScriptCompile Include="apps\template-master-detail\main-page.ts">
|
<TypeScriptCompile Include="apps\template-master-detail\main-page.ts">
|
||||||
<DependentUpon>main-page.xml</DependentUpon>
|
<DependentUpon>main-page.xml</DependentUpon>
|
||||||
</TypeScriptCompile>
|
</TypeScriptCompile>
|
||||||
|
<TypeScriptCompile Include="apps\tests\app\binding_tests.ts">
|
||||||
|
<DependentUpon>binding_tests.xml</DependentUpon>
|
||||||
|
</TypeScriptCompile>
|
||||||
<TypeScriptCompile Include="apps\tests\app\location-example.ts" />
|
<TypeScriptCompile Include="apps\tests\app\location-example.ts" />
|
||||||
<TypeScriptCompile Include="apps\tests\app\style_props.ts" />
|
<TypeScriptCompile Include="apps\tests\app\style_props.ts" />
|
||||||
<TypeScriptCompile Include="apps\tests\console-tests.ts" />
|
<TypeScriptCompile Include="apps\tests\console-tests.ts" />
|
||||||
@ -491,6 +494,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="apps\gallery-app\layouts\dock-layout.xml" />
|
<Content Include="apps\gallery-app\layouts\dock-layout.xml" />
|
||||||
|
<Content Include="apps\tests\app\binding_tests.xml" />
|
||||||
<Content Include="apps\ui-tests-app\pages\i86.xml" />
|
<Content Include="apps\ui-tests-app\pages\i86.xml" />
|
||||||
<Content Include="apps\template-blank\app.css" />
|
<Content Include="apps\template-blank\app.css" />
|
||||||
<Content Include="apps\template-hello-world\app.css" />
|
<Content Include="apps\template-hello-world\app.css" />
|
||||||
@ -1456,7 +1460,7 @@
|
|||||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||||
</WebProjectProperties>
|
</WebProjectProperties>
|
||||||
</FlavorProperties>
|
</FlavorProperties>
|
||||||
<UserProperties ui_2scroll-view_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2editable-text-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2absolute-layout-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2gallery-app_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2content-view_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2web-view_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2linear-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2absolute-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2dock-layout_2package_1json__JSONSchema="" ui_2layouts_2grid-layout_2package_1json__JSONSchema="" ui_2layouts_2wrap-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" />
|
<UserProperties ui_2layouts_2wrap-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2grid-layout_2package_1json__JSONSchema="" ui_2layouts_2dock-layout_2package_1json__JSONSchema="" ui_2layouts_2absolute-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2linear-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2web-view_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2content-view_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2gallery-app_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2absolute-layout-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2editable-text-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2scroll-view_2package_1json__JSONSchema="http://json.schemastore.org/package" />
|
||||||
</VisualStudio>
|
</VisualStudio>
|
||||||
</ProjectExtensions>
|
</ProjectExtensions>
|
||||||
</Project>
|
</Project>
|
61
apps/tests/app/binding_tests.ts
Normal file
61
apps/tests/app/binding_tests.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import pageModule = require("ui/page");
|
||||||
|
//import stackLayoutModule = require("ui/layouts/stack-layout");
|
||||||
|
//import textFieldModule = require("ui/text-field");
|
||||||
|
import observableModule = require("data/observable");
|
||||||
|
import bindableModule = require("ui/core/bindable");
|
||||||
|
//import enums = require("ui/enums");
|
||||||
|
import trace = require("trace");
|
||||||
|
trace.setCategories(trace.categories.Test);
|
||||||
|
trace.enable();
|
||||||
|
|
||||||
|
export function pageLoaded(args: observableModule.EventData) {
|
||||||
|
var page: pageModule.Page = <pageModule.Page>args.object;
|
||||||
|
var model = new observableModule.Observable();
|
||||||
|
//var model = page.bindingContext;
|
||||||
|
model.set("paramProperty", "%%%");
|
||||||
|
var toUpperConverter: bindableModule.ValueConverter = {
|
||||||
|
toModel: function (value, param1) {
|
||||||
|
return param1 + value.toLowerCase();
|
||||||
|
},
|
||||||
|
toView: function (value, param1) {
|
||||||
|
return value.toUpperCase();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
model.set("toUpper", toUpperConverter);
|
||||||
|
model.set("testProperty", "Alabala");
|
||||||
|
|
||||||
|
page.bindingContext = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
//export function createPage() {
|
||||||
|
// var stackLayout = new stackLayoutModule.StackLayout();
|
||||||
|
// var firstTextField = new textFieldModule.TextField();
|
||||||
|
// firstTextField.updateTextTrigger = enums.UpdateTextTrigger.textChanged;
|
||||||
|
// var secondTextField = new textFieldModule.TextField();
|
||||||
|
// secondTextField.updateTextTrigger = enums.UpdateTextTrigger.textChanged;
|
||||||
|
|
||||||
|
// var model = new observableModule.Observable();
|
||||||
|
|
||||||
|
// var bindOptions: bindableModule.BindingOptions = {
|
||||||
|
// sourceProperty: "testProperty",
|
||||||
|
// targetProperty: "text",
|
||||||
|
// twoWay: true,
|
||||||
|
// expression: "testProperty | toUpper('$$$')"
|
||||||
|
// };
|
||||||
|
|
||||||
|
// firstTextField.bind(bindOptions, model);
|
||||||
|
// secondTextField.bind({
|
||||||
|
// sourceProperty: "testProperty",
|
||||||
|
// targetProperty: "text",
|
||||||
|
// twoWay: true
|
||||||
|
// }, model);
|
||||||
|
|
||||||
|
// stackLayout.addChild(firstTextField);
|
||||||
|
// stackLayout.addChild(secondTextField);
|
||||||
|
|
||||||
|
// var page = new pageModule.Page();
|
||||||
|
// page.on("loaded", pageLoaded);
|
||||||
|
// page.content = stackLayout;
|
||||||
|
// page.bindingContext = model;
|
||||||
|
// return page;
|
||||||
|
//}
|
7
apps/tests/app/binding_tests.xml
Normal file
7
apps/tests/app/binding_tests.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<Page xmlns="http://www.nativescript.org/tns.xsd" loaded="pageLoaded">
|
||||||
|
<StackLayout padding="7">
|
||||||
|
<TextField text="{{ testProperty, testProperty | toUpper(paramProperty) }}" />
|
||||||
|
<!--<TextField text="{{ testProperty | toUpper }}" />-->
|
||||||
|
<TextField text="{{ testProperty }}" />
|
||||||
|
</StackLayout>
|
||||||
|
</Page>
|
@ -5,7 +5,13 @@ declare module "js-libs/polymer-expressions" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Expression {
|
class Expression {
|
||||||
getValue(model);
|
/**
|
||||||
|
* Evaluates a value for an expression.
|
||||||
|
* @param model - Context of the expression.
|
||||||
|
* @param isBackConvert - Denotes if the convertion is forward (from model to ui) or back (ui to model).
|
||||||
|
* @param changedModel - A property bag which contains all changed properties (in case of two way binding).
|
||||||
|
*/
|
||||||
|
getValue(model, isBackConvert, changedModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,10 +53,17 @@ var Path = require("js-libs/polymer-expressions/path-parser").Path;
|
|||||||
if (!this.valueFn_) {
|
if (!this.valueFn_) {
|
||||||
var name = this.name;
|
var name = this.name;
|
||||||
var path = this.path;
|
var path = this.path;
|
||||||
this.valueFn_ = function (model, observer) {
|
this.valueFn_ = function (model, observer, changedModel) {
|
||||||
if (observer)
|
if (observer)
|
||||||
observer.addPath(model, path);
|
observer.addPath(model, path);
|
||||||
|
|
||||||
|
if (changedModel) {
|
||||||
|
var result = path.getValueFrom(changedModel);
|
||||||
|
if (result !== undefined) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return path.getValueFrom(model);
|
return path.getValueFrom(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,8 +192,8 @@ var Path = require("js-libs/polymer-expressions/path-parser").Path;
|
|||||||
// object.
|
// object.
|
||||||
if (toModelDirection) {
|
if (toModelDirection) {
|
||||||
fn = fn.toModel;
|
fn = fn.toModel;
|
||||||
} else if (typeof fn.toDOM == 'function') {
|
} else if (typeof fn.toView == 'function') {
|
||||||
fn = fn.toDOM;
|
fn = fn.toView;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof fn != 'function') {
|
if (typeof fn != 'function') {
|
||||||
@ -394,11 +401,10 @@ var Path = require("js-libs/polymer-expressions/path-parser").Path;
|
|||||||
}
|
}
|
||||||
|
|
||||||
Expression.prototype = {
|
Expression.prototype = {
|
||||||
getValue: function (model, observer, filterRegistry) {
|
getValue: function (model, isBackConvert, changedModel, observer) {
|
||||||
var value = getFn(this.expression)(model, observer, filterRegistry);
|
var value = getFn(this.expression)(model, observer, changedModel);
|
||||||
for (var i = 0; i < this.filters.length; i++) {
|
for (var i = 0; i < this.filters.length; i++) {
|
||||||
value = this.filters[i].transform(model, observer, filterRegistry,
|
value = this.filters[i].transform(model, observer, model, isBackConvert, [value]);
|
||||||
false, [value]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
@ -47,19 +47,24 @@ export class Span extends bindable.Bindable implements definition.Span, view.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getColorValue(value: any): colorModule.Color {
|
||||||
|
var result;
|
||||||
|
if (types.isString(value) && (<any>value).indexOf("#") === 0) {
|
||||||
|
result = new colorModule.Color(<any>value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = value;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
get foregroundColor(): colorModule.Color {
|
get foregroundColor(): colorModule.Color {
|
||||||
return this._foregroundColor;
|
return this._foregroundColor;
|
||||||
}
|
}
|
||||||
set foregroundColor(value: colorModule.Color) {
|
set foregroundColor(value: colorModule.Color) {
|
||||||
var foreColor;
|
var convertedColor = this._getColorValue(value);
|
||||||
if (types.isString(value) && (<any>value).indexOf("#") === 0) {
|
if (this._foregroundColor !== convertedColor) {
|
||||||
foreColor = new colorModule.Color(<any>value);
|
this._foregroundColor = convertedColor;
|
||||||
}
|
|
||||||
else {
|
|
||||||
foreColor = value;
|
|
||||||
}
|
|
||||||
if (this._foregroundColor !== foreColor) {
|
|
||||||
this._foregroundColor = foreColor;
|
|
||||||
this.updateAndNotify();
|
this.updateAndNotify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,8 +73,9 @@ export class Span extends bindable.Bindable implements definition.Span, view.App
|
|||||||
return this._backgroundColor;
|
return this._backgroundColor;
|
||||||
}
|
}
|
||||||
set backgroundColor(value: colorModule.Color) {
|
set backgroundColor(value: colorModule.Color) {
|
||||||
if (this._backgroundColor !== value) {
|
var convertedColor = this._getColorValue(value);
|
||||||
this._backgroundColor = value;
|
if (this._backgroundColor !== convertedColor) {
|
||||||
|
this._backgroundColor = convertedColor;
|
||||||
this.updateAndNotify();
|
this.updateAndNotify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ export function getComponentModule(elementName: string, namespace: string, attri
|
|||||||
// Get the event handler from instance.bindingContext.
|
// Get the event handler from instance.bindingContext.
|
||||||
var propertyChangeHandler = (args: observable.PropertyChangeData) => {
|
var propertyChangeHandler = (args: observable.PropertyChangeData) => {
|
||||||
if (args.propertyName === "bindingContext") {
|
if (args.propertyName === "bindingContext") {
|
||||||
var handler = instance.bindingContext && instance.bindingContext[getPropertyNameFromBinding(attrValue)];
|
var handler = instance.bindingContext && instance.bindingContext[getBindingExpressionFromAttribute(attrValue)];
|
||||||
// Check if the handler is function and add it to the instance for specified event name.
|
// Check if the handler is function and add it to the instance for specified event name.
|
||||||
if (types.isFunction(handler)) {
|
if (types.isFunction(handler)) {
|
||||||
instance.on(attr, handler, instance.bindingContext);
|
instance.on(attr, handler, instance.bindingContext);
|
||||||
@ -157,10 +157,10 @@ function isKnownEvent(name: string, exports: any): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getBinding(instance: view.View, name: string, value: string): bindable.BindingOptions {
|
function getBinding(instance: view.View, name: string, value: string): bindable.BindingOptions {
|
||||||
return { targetProperty: name, sourceProperty: getPropertyNameFromBinding(value), twoWay: true };
|
return bindable.Bindable._getBindingOptions(name, getBindingExpressionFromAttribute(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPropertyNameFromBinding(value: string): string {
|
function getBindingExpressionFromAttribute(value: string): string {
|
||||||
return value.replace("{{", "").replace("}}", "").trim();
|
return value.replace("{{", "").replace("}}", "").trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
ui/core/bindable.d.ts
vendored
22
ui/core/bindable.d.ts
vendored
@ -17,6 +17,27 @@
|
|||||||
* True to establish a two-way binding, false otherwise. A two-way binding will synchronize both the source and the target property values regardless of which one initiated the change.
|
* True to establish a two-way binding, false otherwise. A two-way binding will synchronize both the source and the target property values regardless of which one initiated the change.
|
||||||
*/
|
*/
|
||||||
twoWay?: boolean;
|
twoWay?: boolean;
|
||||||
|
/**
|
||||||
|
* An expression used for calculations (convertions) based on the value of the property.
|
||||||
|
*/
|
||||||
|
expression?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface which defines methods need to create binding value converter.
|
||||||
|
*/
|
||||||
|
export interface ValueConverter {
|
||||||
|
/**
|
||||||
|
* A method that will be executed when a value (of the binding property) should be converted to the observable model.
|
||||||
|
* For example: user types in a text field, but our business logic requires to store data in a different manner (e.g. in lower case).
|
||||||
|
* @param params - An array of parameters where first element is the value of the property and next elements are parameters send to converter.
|
||||||
|
*/
|
||||||
|
toModel: (...params: any[]) => any;
|
||||||
|
/**
|
||||||
|
* A method that will be executed when a value should be converted to the UI view. For example we have a date object which should be displayed to the end user in a specific date format.
|
||||||
|
* @param params - An array of parameters where first element is the value of the property and next elements are parameters send to converter.
|
||||||
|
*/
|
||||||
|
toView: (...params: any[]) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,6 +66,7 @@
|
|||||||
unbind(property: string);
|
unbind(property: string);
|
||||||
|
|
||||||
//@private
|
//@private
|
||||||
|
static _getBindingOptions(name: string, bindingExpression: string): BindingOptions;
|
||||||
_updateTwoWayBinding(propertyName: string, value: any);
|
_updateTwoWayBinding(propertyName: string, value: any);
|
||||||
_onBindingContextChanged(oldValue: any, newValue: any);
|
_onBindingContextChanged(oldValue: any, newValue: any);
|
||||||
//@endprivate
|
//@endprivate
|
||||||
|
@ -6,6 +6,8 @@ import types = require("utils/types");
|
|||||||
import trace = require("trace");
|
import trace = require("trace");
|
||||||
import polymerExpressions = require("js-libs/polymer-expressions");
|
import polymerExpressions = require("js-libs/polymer-expressions");
|
||||||
|
|
||||||
|
var expressionSymbolsRegex = /[ \+\-\*%\?:<>=!\|&\(\)\[\]]/;
|
||||||
|
|
||||||
var bindingContextProperty = new dependencyObservable.Property(
|
var bindingContextProperty = new dependencyObservable.Property(
|
||||||
"bindingContext",
|
"bindingContext",
|
||||||
"Bindable",
|
"Bindable",
|
||||||
@ -114,6 +116,31 @@ 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);
|
||||||
|
}
|
||||||
|
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(",");
|
||||||
|
result.sourceProperty = Bindable.extractPropertyNameFromExpression(params[0]);
|
||||||
|
result.expression = params[1];
|
||||||
|
result.twoWay = params[2] ? params[2].toLowerCase() === "true" : true;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Binding {
|
export class Binding {
|
||||||
@ -188,34 +215,52 @@ export class Binding {
|
|||||||
|
|
||||||
public updateTwoWay(value: any) {
|
public updateTwoWay(value: any) {
|
||||||
if (this.options.twoWay) {
|
if (this.options.twoWay) {
|
||||||
|
if (this._isExpression(this.options.expression)) {
|
||||||
|
var changedModel = {};
|
||||||
|
changedModel[this.options.sourceProperty] = value;
|
||||||
|
this.updateSource(this._getExpressionValue(this.options.expression, true, changedModel));
|
||||||
|
}
|
||||||
|
else {
|
||||||
this.updateSource(value);
|
this.updateSource(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _isExpression(expression: string): boolean {
|
private _isExpression(expression: string): boolean {
|
||||||
return expression.indexOf(" ") !== -1;
|
if (expression) {
|
||||||
|
var result = expression.indexOf(" ") !== -1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getExpressionValue(expression: string): any {
|
private _getExpressionValue(expression: string, isBackConvert: boolean, changedModel: any): any {
|
||||||
|
try {
|
||||||
var exp = polymerExpressions.PolymerExpressions.getExpression(expression);
|
var exp = polymerExpressions.PolymerExpressions.getExpression(expression);
|
||||||
if (exp) {
|
if (exp) {
|
||||||
return exp.getValue(this.source && this.source.get && this.source.get() || global);
|
var context = this.source && this.source.get && this.source.get() || global;
|
||||||
|
return exp.getValue(context, isBackConvert, changedModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public onSourcePropertyChanged(data: observable.PropertyChangeData) {
|
public onSourcePropertyChanged(data: observable.PropertyChangeData) {
|
||||||
if (this._isExpression(this.options.sourceProperty)) {
|
if (this._isExpression(this.options.expression)) {
|
||||||
this.updateTarget(this._getExpressionValue(this.options.sourceProperty));
|
this.updateTarget(this._getExpressionValue(this.options.expression, false, undefined));
|
||||||
} else if (data.propertyName === this.options.sourceProperty) {
|
} else if (data.propertyName === this.options.sourceProperty) {
|
||||||
this.updateTarget(data.value);
|
this.updateTarget(data.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSourceProperty() {
|
private getSourceProperty() {
|
||||||
if (this._isExpression(this.options.sourceProperty)) {
|
if (this._isExpression(this.options.expression)) {
|
||||||
return this._getExpressionValue(this.options.sourceProperty);
|
return this._getExpressionValue(this.options.expression, false, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.sourceOptions) {
|
if (!this.sourceOptions) {
|
||||||
|
Reference in New Issue
Block a user