From 188a1c2c34ed363fee96ad43d62219ea7e89df57 Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Tue, 16 May 2017 13:33:02 +0300 Subject: [PATCH] Binding to ViewBase as source now works (#4195) * Fix 4127 * Move back Binding class to bindable module --- tests/app/app/mainPage.ts | 4 +-- tests/app/ui/core/bindable/bindable-tests.ts | 9 ++++++ .../data/observable/observable.d.ts | 7 +++++ .../data/observable/observable.ts | 4 ++- .../ui/core/bindable/bindable.d.ts | 2 +- tns-core-modules/ui/core/bindable/bindable.ts | 28 ++++++++++--------- .../ui/core/view-base/view-base.d.ts | 9 +++++- .../ui/core/view-base/view-base.ts | 1 + 8 files changed, 45 insertions(+), 19 deletions(-) diff --git a/tests/app/app/mainPage.ts b/tests/app/app/mainPage.ts index 14e9260a6..81dff086d 100644 --- a/tests/app/app/mainPage.ts +++ b/tests/app/app/mainPage.ts @@ -24,9 +24,7 @@ page.id = "mainPage"; page.on(Page.navigatedToEvent, onNavigatedTo); function runTests() { - setTimeout(function () { - tests.runAll(); - }, 10); + setTimeout(() => tests.runAll(), 10); } function onNavigatedTo(args) { diff --git a/tests/app/ui/core/bindable/bindable-tests.ts b/tests/app/ui/core/bindable/bindable-tests.ts index 560a6b4cd..254ab1d58 100644 --- a/tests/app/ui/core/bindable/bindable-tests.ts +++ b/tests/app/ui/core/bindable/bindable-tests.ts @@ -29,6 +29,15 @@ export function test_Bindable_Members() { TKUnit.assert(types.isDefined(obj.unbind), "Bindable.unbind not defined"); }; +export function test_Binding_to_bindingContext_of_View() { + const target = new Button(); + const source = new Button(); + + target.bind({ targetProperty: "bindingContext", sourceProperty: "text" }, source); + source.text = 'a'; + TKUnit.assertEqual(target.bindingContext, 'a'); +}; + export function test_Bindable_Bind_ToTarget_OneWay() { const model = new Observable(); model.set("name", "John"); diff --git a/tns-core-modules/data/observable/observable.d.ts b/tns-core-modules/data/observable/observable.d.ts index e8ebffbc9..f2cdf9150 100644 --- a/tns-core-modules/data/observable/observable.d.ts +++ b/tns-core-modules/data/observable/observable.d.ts @@ -150,6 +150,13 @@ export class Observable { */ _createPropertyChangeData(name: string, value: any, oldValue?: any): PropertyChangeData; + //@private + /** + * Filed to use instead of instanceof ViewBase. + * @private + */ + public _isViewBase: boolean; + /** * @private */ diff --git a/tns-core-modules/data/observable/observable.ts b/tns-core-modules/data/observable/observable.ts index db8d1692c..329faeeb1 100644 --- a/tns-core-modules/data/observable/observable.ts +++ b/tns-core-modules/data/observable/observable.ts @@ -32,6 +32,8 @@ let _wrappedValues = [ export class Observable implements ObservableDefinition { public static propertyChangeEvent = "propertyChange"; + public _isViewBase: boolean; + private _observers = {}; public get(name: string): any { @@ -233,4 +235,4 @@ export function fromObjectRecursive(source: any): Observable { let observable = new ObservableFromObject(); addPropertiesFromObject(observable, source, true); return observable; -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/core/bindable/bindable.d.ts b/tns-core-modules/ui/core/bindable/bindable.d.ts index b4bbd6ddf..b626fbca6 100644 --- a/tns-core-modules/ui/core/bindable/bindable.d.ts +++ b/tns-core-modules/ui/core/bindable/bindable.d.ts @@ -59,4 +59,4 @@ export class Binding { } export function getEventOrGestureName(name: string): string; -export function isEventOrGesture(name: string, view: ViewBase): boolean; +export function isEventOrGesture(name: string, view: ViewBase): boolean; \ No newline at end of file diff --git a/tns-core-modules/ui/core/bindable/bindable.ts b/tns-core-modules/ui/core/bindable/bindable.ts index 857d9387f..bdcba2ccc 100644 --- a/tns-core-modules/ui/core/bindable/bindable.ts +++ b/tns-core-modules/ui/core/bindable/bindable.ts @@ -22,10 +22,9 @@ const contextKey = "context"; // from $parents['ListView'] will return 'ListView' // from $parents[1] will return 1 const paramsRegex = /\[\s*(['"])*(\w*)\1\s*\]/; - const bc = bindingConstants; - const emptyArray = []; + function getProperties(property: string): Array { let result: Array = emptyArray; if (property) { @@ -55,7 +54,7 @@ export function getEventOrGestureName(name: string): string { // NOTE: method fromString from "ui/gestures"; export function isGesture(eventOrGestureName: string): boolean { let t = eventOrGestureName.trim().toLowerCase(); - return t === "tap" + return t === "tap" || t === "doubletap" || t === "pinch" || t === "pan" @@ -169,8 +168,9 @@ export class Binding { return; } - if (data.value) { - this.update(data.value); + const value = data.value; + if (value !== null && value !== undefined) { + this.update(value); } else { // TODO: Is this correct? // What should happen when bindingContext is null/undefined? @@ -279,14 +279,16 @@ export class Binding { let prop = parentProperies || ""; for (let i = 0, length = objectsAndProperties.length; i < length; i++) { - prop += "$" + objectsAndProperties[i].property; + const propName = objectsAndProperties[i].property; + prop += "$" + propName; let currentObject = objectsAndProperties[i].instance; - if (!this.propertyChangeListeners.has(prop) && currentObject instanceof Observable) { - addWeakEventListener( - currentObject, - Observable.propertyChangeEvent, - this.onSourcePropertyChanged, - this); + if (!this.propertyChangeListeners.has(prop) && currentObject instanceof Observable && currentObject._isViewBase) { + // Add listener for properties created with after 3.0 version + addWeakEventListener(currentObject, `${propName}Change`, this.onSourcePropertyChanged, this); + addWeakEventListener(currentObject, Observable.propertyChangeEvent, this.onSourcePropertyChanged, this); + this.propertyChangeListeners.set(prop, currentObject); + } else if (!this.propertyChangeListeners.has(prop) && currentObject instanceof Observable) { + addWeakEventListener(currentObject, Observable.propertyChangeEvent, this.onSourcePropertyChanged, this); this.propertyChangeListeners.set(prop, currentObject); } } @@ -601,4 +603,4 @@ export class Binding { this.updating = false; } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/core/view-base/view-base.d.ts b/tns-core-modules/ui/core/view-base/view-base.d.ts index 38cbfb4f3..7251af47d 100644 --- a/tns-core-modules/ui/core/view-base/view-base.d.ts +++ b/tns-core-modules/ui/core/view-base/view-base.d.ts @@ -17,6 +17,7 @@ import { Order, FlexGrow, FlexShrink, FlexWrapBefore, AlignSelf } from "../../la import { Length } from "../../styling/style-properties"; export { isIOS, isAndroid, layout, Color }; + export * from "../properties"; export * from "../bindable"; @@ -291,6 +292,12 @@ export abstract class ViewBase extends Observable { //@endprivate } +export class Binding { + constructor(target: ViewBase, options: BindingOptions); + public bind(source: Object): void; + public unbind(); +} + export const idProperty: Property; export const classNameProperty: Property; export const bindingContextProperty: InheritedProperty; @@ -299,4 +306,4 @@ export const bindingContextProperty: InheritedProperty; * Converts string into boolean value. * Throws error if value is not 'true' or 'false'. */ -export function booleanConverter(v: string): boolean; +export function booleanConverter(v: string): boolean; \ No newline at end of file diff --git a/tns-core-modules/ui/core/view-base/view-base.ts b/tns-core-modules/ui/core/view-base/view-base.ts index 4aec7516b..276d52872 100644 --- a/tns-core-modules/ui/core/view-base/view-base.ts +++ b/tns-core-modules/ui/core/view-base/view-base.ts @@ -863,6 +863,7 @@ ViewBase.prototype._defaultPaddingTop = 0; ViewBase.prototype._defaultPaddingRight = 0; ViewBase.prototype._defaultPaddingBottom = 0; ViewBase.prototype._defaultPaddingLeft = 0; +ViewBase.prototype._isViewBase = true; ViewBase.prototype._batchUpdateScope = 0;