Fix binding support (#3489)

* Fix binding support

* fix tslint
This commit is contained in:
Hristo Hristov
2017-01-13 18:08:18 +02:00
committed by GitHub
parent 7f21eb06ac
commit 8cec512397
14 changed files with 492 additions and 503 deletions

View File

@@ -1,5 +1,5 @@
// >> observable-require // >> observable-require
import * as observable from "data/observable"; import { Observable, fromObject, fromObjectRecursive, PropertyChangeData, EventData, WrappedValue } from "data/observable";
// << observable-require // << observable-require
import * as dependencyObservable from "ui/core/dependency-observable"; import * as dependencyObservable from "ui/core/dependency-observable";
@@ -9,7 +9,7 @@ import * as proxy from "ui/core/proxy";
import { ObservableArray } from "data/observable-array"; import { ObservableArray } from "data/observable-array";
var TESTED_NAME = "tested"; var TESTED_NAME = "tested";
class TestObservable extends observable.Observable { class TestObservable extends Observable {
public test() { public test() {
this._emit(TESTED_NAME); this._emit(TESTED_NAME);
} }
@@ -22,7 +22,7 @@ export var test_Observable_Constructor = function () {
Age: 34, Age: 34,
Married: true Married: true
}; };
var person = new observable.Observable(json); var person = fromObject(json);
var name = person.get("Name"); var name = person.get("Name");
var age = person.get("Age"); var age = person.get("Age");
var married = person.get("Married"); var married = person.get("Married");
@@ -35,11 +35,11 @@ export var test_Observable_Constructor = function () {
export var tests_DummyTestForCodeSnippet = function () { export var tests_DummyTestForCodeSnippet = function () {
// >> observable-property-change // >> observable-property-change
var person = new observable.Observable(); var person = new Observable();
person.set("Name", "John"); person.set("Name", "John");
person.set("Age", 34); person.set("Age", 34);
person.set("Married", true); person.set("Married", true);
person.addEventListener(observable.Observable.propertyChangeEvent, function (pcd: observable.PropertyChangeData) { person.addEventListener(Observable.propertyChangeEvent, function (pcd: PropertyChangeData) {
//console.log(pcd.eventName.toString() + " " + pcd.propertyName.toString() + " " + pcd.value.toString()); //console.log(pcd.eventName.toString() + " " + pcd.propertyName.toString() + " " + pcd.value.toString());
}); });
person.set("Age", 35); person.set("Age", 35);
@@ -51,7 +51,7 @@ export var tests_DummyTestForCodeSnippet = function () {
} }
export var test_Observable_Members = function () { export var test_Observable_Members = function () {
var obj = new observable.Observable(); var obj = new Observable();
TKUnit.assert(types.isDefined(obj.addEventListener), "Observable.addEventListener not defined"); TKUnit.assert(types.isDefined(obj.addEventListener), "Observable.addEventListener not defined");
TKUnit.assert(types.isDefined(obj._createPropertyChangeData), "Observable.createPropertyChangeData not defined"); TKUnit.assert(types.isDefined(obj._createPropertyChangeData), "Observable.createPropertyChangeData not defined");
TKUnit.assert(types.isDefined(obj._emit), "Observable.emit not defined"); TKUnit.assert(types.isDefined(obj._emit), "Observable.emit not defined");
@@ -62,19 +62,18 @@ export var test_Observable_Members = function () {
TKUnit.assert(types.isDefined(obj.on), "Observable.on not defined"); TKUnit.assert(types.isDefined(obj.on), "Observable.on not defined");
TKUnit.assert(types.isDefined(obj.removeEventListener), "Observable.removeEventListener not defined"); TKUnit.assert(types.isDefined(obj.removeEventListener), "Observable.removeEventListener not defined");
TKUnit.assert(types.isDefined(obj.set), "Observable.set not defined"); TKUnit.assert(types.isDefined(obj.set), "Observable.set not defined");
TKUnit.assert(types.isDefined(obj.typeName), "Observable.typeName not defined");
} }
export var test_Observable_UpdateAnotherPropertyWithinChangedCallback = function () { export var test_Observable_UpdateAnotherPropertyWithinChangedCallback = function () {
var obj = new observable.Observable(); var obj = new Observable();
var changedCallback = function (pcd: observable.PropertyChangeData) { var changedCallback = function (pcd: PropertyChangeData) {
if (pcd.propertyName === "name") { if (pcd.propertyName === "name") {
pcd.object.set("test", "Changed test"); pcd.object.set("test", "Changed test");
} }
} }
obj.addEventListener(observable.Observable.propertyChangeEvent, changedCallback); obj.addEventListener(Observable.propertyChangeEvent, changedCallback);
obj.set("name", "Initial name"); obj.set("name", "Initial name");
obj.set("test", "Initial test"); obj.set("test", "Initial test");
@@ -129,18 +128,18 @@ export var test_DependencyObservable_UpdateAnotherPropertyWithinChangedCallback
} }
export var test_Observable_addEventListener_SingleEvent = function () { export var test_Observable_addEventListener_SingleEvent = function () {
var obj = new observable.Observable(); var obj = new Observable();
var receivedCount = 0; var receivedCount = 0;
var callback = function (data: observable.PropertyChangeData) { var callback = function (data: PropertyChangeData) {
receivedCount++; receivedCount++;
TKUnit.assert(data.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); TKUnit.assert(data.eventName === Observable.propertyChangeEvent, "Expected event name " + Observable.propertyChangeEvent);
TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid."); TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid.");
TKUnit.assert(data.propertyName === "testName", "PropertyChangeData.propertyName value not valid."); TKUnit.assert(data.propertyName === "testName", "PropertyChangeData.propertyName value not valid.");
TKUnit.assert(data.value === 1, "PropertyChangeData.value value not valid."); TKUnit.assert(data.value === 1, "PropertyChangeData.value value not valid.");
} }
obj.addEventListener(observable.Observable.propertyChangeEvent, callback); obj.addEventListener(Observable.propertyChangeEvent, callback);
obj.set("testName", 1); obj.set("testName", 1);
TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly."); TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly.");
} }
@@ -149,13 +148,13 @@ export var test_Observable_addEventListener_MultipleEvents = function () {
var obj = new TestObservable(); var obj = new TestObservable();
var receivedCount = 0; var receivedCount = 0;
var callback = function (data: observable.EventData) { var callback = function (data: EventData) {
receivedCount++; receivedCount++;
TKUnit.assert(data.object === obj, "EventData.object value not valid."); TKUnit.assert(data.object === obj, "EventData.object value not valid.");
if (data.eventName === observable.Observable.propertyChangeEvent) { if (data.eventName === Observable.propertyChangeEvent) {
var propertyData = <observable.PropertyChangeData>data; var propertyData = <PropertyChangeData>data;
TKUnit.assert(propertyData.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); TKUnit.assert(propertyData.eventName === Observable.propertyChangeEvent, "Expected event name " + Observable.propertyChangeEvent);
TKUnit.assert(propertyData.propertyName === "testName", "PropertyChangeData.propertyName value not valid."); TKUnit.assert(propertyData.propertyName === "testName", "PropertyChangeData.propertyName value not valid.");
TKUnit.assert(propertyData.value === 1, "PropertyChangeData.value value not valid."); TKUnit.assert(propertyData.value === 1, "PropertyChangeData.value value not valid.");
} else { } else {
@@ -163,7 +162,7 @@ export var test_Observable_addEventListener_MultipleEvents = function () {
} }
} }
var events = observable.Observable.propertyChangeEvent + "," + TESTED_NAME; var events = Observable.propertyChangeEvent + "," + TESTED_NAME;
obj.addEventListener(events, callback); obj.addEventListener(events, callback);
obj.set("testName", 1); obj.set("testName", 1);
obj.test(); obj.test();
@@ -174,13 +173,13 @@ export var test_Observable_addEventListener_MultipleEvents_ShouldTrim = function
var obj = new TestObservable(); var obj = new TestObservable();
var receivedCount = 0; var receivedCount = 0;
var callback = function (data: observable.EventData) { var callback = function (data: EventData) {
receivedCount++; receivedCount++;
} }
var events = observable.Observable.propertyChangeEvent + " , " + TESTED_NAME; var events = Observable.propertyChangeEvent + " , " + TESTED_NAME;
obj.addEventListener(events, callback); obj.addEventListener(events, callback);
TKUnit.assert(obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.addEventListener for multiple events should trim each event name."); TKUnit.assert(obj.hasListeners(Observable.propertyChangeEvent), "Observable.addEventListener for multiple events should trim each event name.");
TKUnit.assert(obj.hasListeners(TESTED_NAME), "Observable.addEventListener for multiple events should trim each event name."); TKUnit.assert(obj.hasListeners(TESTED_NAME), "Observable.addEventListener for multiple events should trim each event name.");
obj.set("testName", 1); obj.set("testName", 1);
@@ -193,16 +192,16 @@ export var test_Observable_addEventListener_MultipleCallbacks = function () {
var obj = new TestObservable(); var obj = new TestObservable();
var receivedCount = 0; var receivedCount = 0;
var callback1 = function (data: observable.EventData) { var callback1 = function (data: EventData) {
receivedCount++; receivedCount++;
} }
var callback2 = function (data: observable.EventData) { var callback2 = function (data: EventData) {
receivedCount++; receivedCount++;
} }
obj.addEventListener(observable.Observable.propertyChangeEvent, callback1); obj.addEventListener(Observable.propertyChangeEvent, callback1);
obj.addEventListener(observable.Observable.propertyChangeEvent, callback2); obj.addEventListener(Observable.propertyChangeEvent, callback2);
obj.set("testName", 1); obj.set("testName", 1);
TKUnit.assert(receivedCount === 2, "The propertyChanged notification should be raised twice."); TKUnit.assert(receivedCount === 2, "The propertyChanged notification should be raised twice.");
@@ -212,15 +211,15 @@ export var test_Observable_addEventListener_MultipleCallbacks_MultipleEvents = f
var obj = new TestObservable(); var obj = new TestObservable();
var receivedCount = 0; var receivedCount = 0;
var callback1 = function (data: observable.EventData) { var callback1 = function (data: EventData) {
receivedCount++; receivedCount++;
} }
var callback2 = function (data: observable.EventData) { var callback2 = function (data: EventData) {
receivedCount++; receivedCount++;
} }
var events = observable.Observable.propertyChangeEvent + " , " + TESTED_NAME; var events = Observable.propertyChangeEvent + " , " + TESTED_NAME;
obj.addEventListener(events, callback1); obj.addEventListener(events, callback1);
obj.addEventListener(events, callback2); obj.addEventListener(events, callback2);
@@ -231,47 +230,47 @@ export var test_Observable_addEventListener_MultipleCallbacks_MultipleEvents = f
} }
export var test_Observable_removeEventListener_SingleEvent_SingleCallback = function () { export var test_Observable_removeEventListener_SingleEvent_SingleCallback = function () {
var obj = new observable.Observable(); var obj = new Observable();
var receivedCount = 0; var receivedCount = 0;
var callback = function (data: observable.EventData) { var callback = function (data: EventData) {
receivedCount++; receivedCount++;
} }
obj.addEventListener(observable.Observable.propertyChangeEvent, callback); obj.addEventListener(Observable.propertyChangeEvent, callback);
obj.set("testName", 1); obj.set("testName", 1);
obj.removeEventListener(observable.Observable.propertyChangeEvent, callback); obj.removeEventListener(Observable.propertyChangeEvent, callback);
TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.removeEventListener not working properly."); TKUnit.assert(!obj.hasListeners(Observable.propertyChangeEvent), "Observable.removeEventListener not working properly.");
obj.set("testName", 2); obj.set("testName", 2);
TKUnit.assert(receivedCount === 1, "Observable.removeEventListener not working properly."); TKUnit.assert(receivedCount === 1, "Observable.removeEventListener not working properly.");
} }
export var test_Observable_removeEventListener_SingleEvent_MultipleCallbacks = function () { export var test_Observable_removeEventListener_SingleEvent_MultipleCallbacks = function () {
var obj = new observable.Observable(); var obj = new Observable();
var receivedCount = 0; var receivedCount = 0;
var callback1 = function (data: observable.EventData) { var callback1 = function (data: EventData) {
receivedCount++; receivedCount++;
} }
var callback2 = function (data: observable.EventData) { var callback2 = function (data: EventData) {
receivedCount++; receivedCount++;
} }
obj.addEventListener(observable.Observable.propertyChangeEvent, callback1); obj.addEventListener(Observable.propertyChangeEvent, callback1);
obj.addEventListener(observable.Observable.propertyChangeEvent, callback2); obj.addEventListener(Observable.propertyChangeEvent, callback2);
obj.set("testName", 1); obj.set("testName", 1);
obj.removeEventListener(observable.Observable.propertyChangeEvent, callback1); obj.removeEventListener(Observable.propertyChangeEvent, callback1);
TKUnit.assert(obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.removeEventListener not working properly with multiple listeners."); TKUnit.assert(obj.hasListeners(Observable.propertyChangeEvent), "Observable.removeEventListener not working properly with multiple listeners.");
obj.set("testName", 2); obj.set("testName", 2);
TKUnit.assert(receivedCount === 3, "Observable.removeEventListener not working properly with multiple listeners."); TKUnit.assert(receivedCount === 3, "Observable.removeEventListener not working properly with multiple listeners.");
obj.removeEventListener(observable.Observable.propertyChangeEvent, callback2); obj.removeEventListener(Observable.propertyChangeEvent, callback2);
TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Observable.removeEventListener not working properly with multiple listeners."); TKUnit.assert(!obj.hasListeners(Observable.propertyChangeEvent), "Observable.removeEventListener not working properly with multiple listeners.");
obj.set("testName", 3); obj.set("testName", 3);
TKUnit.assert(receivedCount === 3, "Observable.removeEventListener not working properly with multiple listeners."); TKUnit.assert(receivedCount === 3, "Observable.removeEventListener not working properly with multiple listeners.");
@@ -281,11 +280,11 @@ export var test_Observable_removeEventListener_MutlipleEvents_SingleCallback = f
var obj = new TestObservable(); var obj = new TestObservable();
var receivedCount = 0; var receivedCount = 0;
var callback = function (data: observable.EventData) { var callback = function (data: EventData) {
receivedCount++; receivedCount++;
} }
var events = observable.Observable.propertyChangeEvent + " , " + TESTED_NAME; var events = Observable.propertyChangeEvent + " , " + TESTED_NAME;
obj.addEventListener(events, callback); obj.addEventListener(events, callback);
obj.set("testName", 1); obj.set("testName", 1);
@@ -293,7 +292,7 @@ export var test_Observable_removeEventListener_MutlipleEvents_SingleCallback = f
obj.removeEventListener(events, callback); obj.removeEventListener(events, callback);
TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Expected result for hasObservers is false"); TKUnit.assert(!obj.hasListeners(Observable.propertyChangeEvent), "Expected result for hasObservers is false");
TKUnit.assert(!obj.hasListeners(TESTED_NAME), "Expected result for hasObservers is false."); TKUnit.assert(!obj.hasListeners(TESTED_NAME), "Expected result for hasObservers is false.");
obj.set("testName", 2); obj.set("testName", 2);
@@ -306,21 +305,21 @@ export var test_Observable_removeEventListener_SingleEvent_NoCallbackSpecified =
var obj = new TestObservable(); var obj = new TestObservable();
var receivedCount = 0; var receivedCount = 0;
var callback1 = function (data: observable.EventData) { var callback1 = function (data: EventData) {
receivedCount++; receivedCount++;
} }
var callback2 = function (data: observable.EventData) { var callback2 = function (data: EventData) {
receivedCount++; receivedCount++;
} }
obj.addEventListener(observable.Observable.propertyChangeEvent, callback1); obj.addEventListener(Observable.propertyChangeEvent, callback1);
obj.addEventListener(observable.Observable.propertyChangeEvent, callback2); obj.addEventListener(Observable.propertyChangeEvent, callback2);
obj.set("testName", 1); obj.set("testName", 1);
obj.removeEventListener(observable.Observable.propertyChangeEvent); obj.removeEventListener(Observable.propertyChangeEvent);
TKUnit.assert(!obj.hasListeners(observable.Observable.propertyChangeEvent), "Expected result for hasObservers is false."); TKUnit.assert(!obj.hasListeners(Observable.propertyChangeEvent), "Expected result for hasObservers is false.");
obj.set("testName", 2); obj.set("testName", 2);
TKUnit.assert(receivedCount === 2, "Expected receive count is 2"); TKUnit.assert(receivedCount === 2, "Expected receive count is 2");
@@ -330,18 +329,18 @@ export var test_Observable_WhenCreatedWithJSON_PropertyChangedWithDotNotation_Ra
var json = { var json = {
count: 5 count: 5
}; };
var obj = new observable.Observable(json); var obj = fromObject(json);
var receivedCount = 0; var receivedCount = 0;
var callback = function (data: observable.PropertyChangeData) { var callback = function (data: PropertyChangeData) {
receivedCount++; receivedCount++;
TKUnit.assert(data.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); TKUnit.assert(data.eventName === Observable.propertyChangeEvent, "Expected event name " + Observable.propertyChangeEvent);
TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid."); TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid.");
TKUnit.assert(data.propertyName === "count", "PropertyChangeData.propertyName value not valid."); TKUnit.assert(data.propertyName === "count", "PropertyChangeData.propertyName value not valid.");
TKUnit.assert(data.value === 6, "PropertyChangeData.value value not valid."); TKUnit.assert(data.value === 6, "PropertyChangeData.value value not valid.");
} }
obj.addEventListener(observable.Observable.propertyChangeEvent, callback); obj.addEventListener(Observable.propertyChangeEvent, callback);
(<any>obj).count++; (<any>obj).count++;
@@ -352,18 +351,18 @@ export var test_Observable_WhenCreatedWithJSON_PropertyChangedWithBracketsNotati
var json = { var json = {
count: 5 count: 5
}; };
var obj = new observable.Observable(json); var obj = fromObject(json);
var receivedCount = 0; var receivedCount = 0;
var callback = function (data: observable.PropertyChangeData) { var callback = function (data: PropertyChangeData) {
receivedCount++; receivedCount++;
TKUnit.assert(data.eventName === observable.Observable.propertyChangeEvent, "Expected event name " + observable.Observable.propertyChangeEvent); TKUnit.assert(data.eventName === Observable.propertyChangeEvent, "Expected event name " + Observable.propertyChangeEvent);
TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid."); TKUnit.assert(data.object === obj, "PropertyChangeData.object value not valid.");
TKUnit.assert(data.propertyName === "count", "PropertyChangeData.propertyName value not valid."); TKUnit.assert(data.propertyName === "count", "PropertyChangeData.propertyName value not valid.");
TKUnit.assert(data.value === 6, "PropertyChangeData.value value not valid."); TKUnit.assert(data.value === 6, "PropertyChangeData.value value not valid.");
} }
obj.addEventListener(observable.Observable.propertyChangeEvent, callback); obj.addEventListener(Observable.propertyChangeEvent, callback);
obj["count"]++; obj["count"]++;
@@ -371,25 +370,25 @@ export var test_Observable_WhenCreatedWithJSON_PropertyChangedWithBracketsNotati
} }
export var test_AddingTwoEventHandlersAndRemovingWithinHandlerShouldRaiseAllEvents = function () { export var test_AddingTwoEventHandlersAndRemovingWithinHandlerShouldRaiseAllEvents = function () {
var observableInstance = new observable.Observable(); var observableInstance = new Observable();
var firstHandlerCalled = false; var firstHandlerCalled = false;
var secondHandlerCalled = false; var secondHandlerCalled = false;
var firstHandler = function (args) { var firstHandler = function (args) {
observableInstance.off(observable.Observable.propertyChangeEvent, firstHandler, firstObserver); observableInstance.off(Observable.propertyChangeEvent, firstHandler, firstObserver);
firstHandlerCalled = true; firstHandlerCalled = true;
} }
var secondHandler = function (args) { var secondHandler = function (args) {
observableInstance.off(observable.Observable.propertyChangeEvent, secondHandler, secondObserver); observableInstance.off(Observable.propertyChangeEvent, secondHandler, secondObserver);
secondHandlerCalled = true; secondHandlerCalled = true;
} }
var firstObserver = new observable.Observable(); var firstObserver = new Observable();
var secondObserver = new observable.Observable(); var secondObserver = new Observable();
observableInstance.on(observable.Observable.propertyChangeEvent, firstHandler, firstObserver); observableInstance.on(Observable.propertyChangeEvent, firstHandler, firstObserver);
observableInstance.on(observable.Observable.propertyChangeEvent, secondHandler, secondObserver); observableInstance.on(Observable.propertyChangeEvent, secondHandler, secondObserver);
observableInstance.set("someProperty", "some value"); observableInstance.set("someProperty", "some value");
@@ -400,8 +399,8 @@ export var test_AddingTwoEventHandlersAndRemovingWithinHandlerShouldRaiseAllEven
export var test_ObservableCreatedWithJSON_shouldDistinguishSeparateObjects = function () { export var test_ObservableCreatedWithJSON_shouldDistinguishSeparateObjects = function () {
var obj1 = { val: 1 }; var obj1 = { val: 1 };
var obj2 = { val: 2 }; var obj2 = { val: 2 };
var observable1 = new observable.Observable(obj1); var observable1 = fromObject(obj1);
var observable2 = new observable.Observable(obj2); var observable2 = fromObject(obj2);
var val1 = observable1.get("val"); var val1 = observable1.get("val");
var val2 = observable2.get("val"); var val2 = observable2.get("val");
@@ -409,14 +408,14 @@ export var test_ObservableCreatedWithJSON_shouldDistinguishSeparateObjects = fun
var propName1; var propName1;
var newValue1; var newValue1;
observable1.on(observable.Observable.propertyChangeEvent, (data: observable.PropertyChangeData) => { observable1.on(Observable.propertyChangeEvent, (data: PropertyChangeData) => {
propName1 = data.propertyName; propName1 = data.propertyName;
newValue1 = data.value; newValue1 = data.value;
}); });
var propName2; var propName2;
var newValue2; var newValue2;
observable2.on(observable.Observable.propertyChangeEvent, (data: observable.PropertyChangeData) => { observable2.on(Observable.propertyChangeEvent, (data: PropertyChangeData) => {
propName2 = data.propertyName; propName2 = data.propertyName;
newValue2 = data.value; newValue2 = data.value;
}); });
@@ -435,8 +434,8 @@ export var test_ObservableCreatedWithJSON_shouldDistinguishSeparateObjects = fun
}; };
export var test_ObservablesCreatedWithJSON_shouldNotInterfereWithOneAnother = function () { export var test_ObservablesCreatedWithJSON_shouldNotInterfereWithOneAnother = function () {
var observable1 = new observable.Observable({ property1: 1 }); var observable1 = fromObject({ property1: 1 });
var observable2 = new observable.Observable({ property2: 2 }); var observable2 = fromObject({ property2: 2 });
TKUnit.assert(observable1.get("property1") === 1, `Expected: 1; Actual: ${observable1.get("property1")}`); TKUnit.assert(observable1.get("property1") === 1, `Expected: 1; Actual: ${observable1.get("property1")}`);
TKUnit.assert(observable1.get("property2") === undefined, `Expected: undefined; Actual: ${observable1.get("property2")}`); TKUnit.assert(observable1.get("property2") === undefined, `Expected: undefined; Actual: ${observable1.get("property2")}`);
@@ -446,14 +445,14 @@ export var test_ObservablesCreatedWithJSON_shouldNotInterfereWithOneAnother = fu
var propName1; var propName1;
var newValue1; var newValue1;
observable1.on(observable.Observable.propertyChangeEvent, (data: observable.PropertyChangeData) => { observable1.on(Observable.propertyChangeEvent, (data: PropertyChangeData) => {
propName1 = data.propertyName; propName1 = data.propertyName;
newValue1 = data.value; newValue1 = data.value;
}); });
var propName2; var propName2;
var newValue2; var newValue2;
observable2.on(observable.Observable.propertyChangeEvent, (data: observable.PropertyChangeData) => { observable2.on(Observable.propertyChangeEvent, (data: PropertyChangeData) => {
propName2 = data.propertyName; propName2 = data.propertyName;
newValue2 = data.value; newValue2 = data.value;
}); });
@@ -468,12 +467,12 @@ export var test_ObservablesCreatedWithJSON_shouldNotInterfereWithOneAnother = fu
}; };
export function test_ObservablesCreatedWithJSON_shouldNotEmitTwoTimesPropertyChangeEvent() { export function test_ObservablesCreatedWithJSON_shouldNotEmitTwoTimesPropertyChangeEvent() {
var testObservable = new observable.Observable({ "property1": 1 }); var testObservable = fromObject({ "property1": 1 });
var propertyChangeCounter = 0; var propertyChangeCounter = 0;
var propertyChangeHandler = function (args) { var propertyChangeHandler = function (args) {
propertyChangeCounter++; propertyChangeCounter++;
} }
testObservable.on(observable.Observable.propertyChangeEvent, propertyChangeHandler); testObservable.on(Observable.propertyChangeEvent, propertyChangeHandler);
testObservable.set("property1", 2); testObservable.set("property1", 2);
TKUnit.assertEqual(propertyChangeCounter, 1, "PropertyChange event should be fired only once for a single change."); TKUnit.assertEqual(propertyChangeCounter, 1, "PropertyChange event should be fired only once for a single change.");
@@ -481,46 +480,46 @@ export function test_ObservablesCreatedWithJSON_shouldNotEmitTwoTimesPropertyCha
export function test_ObservableShouldEmitPropertyChangeWithSameObjectUsingWrappedValue() { export function test_ObservableShouldEmitPropertyChangeWithSameObjectUsingWrappedValue() {
var testArray = [1]; var testArray = [1];
var testObservable = new observable.Observable({ "property1": testArray}); var testObservable = fromObject({ "property1": testArray });
var propertyChangeCounter = 0; var propertyChangeCounter = 0;
var propertyChangeHandler = function (args) { var propertyChangeHandler = function (args) {
propertyChangeCounter++; propertyChangeCounter++;
} }
testObservable.on(observable.Observable.propertyChangeEvent, propertyChangeHandler); testObservable.on(Observable.propertyChangeEvent, propertyChangeHandler);
testArray.push(2); testArray.push(2);
testObservable.set("property1", testArray); testObservable.set("property1", testArray);
TKUnit.assertEqual(propertyChangeCounter, 0, "PropertyChange event should not be fired when the same object instance is passed."); TKUnit.assertEqual(propertyChangeCounter, 0, "PropertyChange event should not be fired when the same object instance is passed.");
testObservable.set("property1", observable.WrappedValue.wrap(testArray)); testObservable.set("property1", WrappedValue.wrap(testArray));
TKUnit.assertEqual(propertyChangeCounter, 1, "PropertyChange event should be fired only once for a single change."); TKUnit.assertEqual(propertyChangeCounter, 1, "PropertyChange event should be fired only once for a single change.");
} }
export function test_CorrectEventArgsWhenWrappedValueIsUsed() { export function test_CorrectEventArgsWhenWrappedValueIsUsed() {
let testArray = [1]; let testArray = [1];
let testObservable = new observable.Observable({ "property1": testArray}); let testObservable = fromObject({ "property1": testArray });
let actualArgsValue; let actualArgsValue;
let propertyChangeHandler = function (args) { let propertyChangeHandler = function (args) {
actualArgsValue = args.value; actualArgsValue = args.value;
} }
testObservable.on(observable.Observable.propertyChangeEvent, propertyChangeHandler); testObservable.on(Observable.propertyChangeEvent, propertyChangeHandler);
testArray.push(2); testArray.push(2);
let wrappedArray = observable.WrappedValue.wrap(testArray); let wrappedArray = WrappedValue.wrap(testArray);
testObservable.set("property1", wrappedArray); testObservable.set("property1", wrappedArray);
TKUnit.assertEqual(actualArgsValue, wrappedArray, "PropertyChange event should be fired with correct value in arguments."); TKUnit.assertEqual(actualArgsValue, testArray, "PropertyChange event should be fired with correct value in arguments.");
} }
export function test_CorrectPropertyValueAfterUsingWrappedValue() { export function test_CorrectPropertyValueAfterUsingWrappedValue() {
let testArray = [1]; let testArray = [1];
let testObservable = new observable.Observable({ "property1": testArray}); let testObservable = fromObject({ "property1": testArray });
let wrappedArray = observable.WrappedValue.wrap(testArray); let wrappedArray = WrappedValue.wrap(testArray);
testObservable.set("property1", wrappedArray); testObservable.set("property1", wrappedArray);
@@ -529,7 +528,7 @@ export function test_CorrectPropertyValueAfterUsingWrappedValue() {
export function test_NestedObservablesWithObservableArrayShouldNotCrash() { export function test_NestedObservablesWithObservableArrayShouldNotCrash() {
let someObservableArray = new ObservableArray<any>(); let someObservableArray = new ObservableArray<any>();
let testObservable = observable.fromObjectRecursive({ let testObservable = fromObjectRecursive({
firstProp: "test string", firstProp: "test string",
secondProp: someObservableArray secondProp: someObservableArray
}); });
@@ -537,7 +536,7 @@ export function test_NestedObservablesWithObservableArrayShouldNotCrash() {
} }
export function test_NestedObservableWithNullShouldNotCrash() { export function test_NestedObservableWithNullShouldNotCrash() {
let testObservable = observable.fromObjectRecursive({ let testObservable = fromObjectRecursive({
someProperty: null someProperty: null
}); });
TKUnit.assert(testObservable !== undefined); TKUnit.assert(testObservable !== undefined);

View File

@@ -44,7 +44,7 @@ allTests["TIMER"] = require("./timer-tests");
allTests["COLOR"] = require("./color-tests"); allTests["COLOR"] = require("./color-tests");
allTests["DEPENDENCY-OBSERVABLE"] = require("./ui/dependency-observable-tests"); allTests["DEPENDENCY-OBSERVABLE"] = require("./ui/dependency-observable-tests");
// allTests["BINDABLE"] = require("./ui/bindable-tests"); allTests["BINDABLE"] = require("./ui/bindable-tests");
allTests["BINDING-EXPRESSIONS"] = require("./ui/binding-expressions-tests"); allTests["BINDING-EXPRESSIONS"] = require("./ui/binding-expressions-tests");
allTests["XML-PARSER"] = require("./xml-parser-tests/xml-parser-tests"); allTests["XML-PARSER"] = require("./xml-parser-tests/xml-parser-tests");
allTests["FORMATTEDSTRING"] = require("./text/formatted-string-tests"); allTests["FORMATTEDSTRING"] = require("./text/formatted-string-tests");

View File

@@ -6,7 +6,7 @@ import { Label } from "ui/label";
import { Button } from "ui/button"; import { Button } from "ui/button";
import { Page } from "ui/page"; import { Page } from "ui/page";
import { View } from "ui/core/view"; import { View } from "ui/core/view";
import { Observable } from "data/observable"; import { fromObject } from "data/observable";
// >> actionbar-common-require // >> actionbar-common-require
import * as actionBarModule from "ui/action-bar"; import * as actionBarModule from "ui/action-bar";
@@ -164,7 +164,7 @@ export function test_ActionBarItemBindingToEvent() {
const firstHandler = function () { firstHandlerCallCounter++; }; const firstHandler = function () { firstHandlerCallCounter++; };
const secondHandler = function () { secondHandlerCallCounter++; }; const secondHandler = function () { secondHandlerCallCounter++; };
page.bindingContext = new Observable({ "test": firstHandler }); page.bindingContext = fromObject({ "test": firstHandler });
const actionBarItem = page.actionBar.actionItems.getItemAt(0); const actionBarItem = page.actionBar.actionItems.getItemAt(0);

View File

@@ -1,4 +1,4 @@
import { Observable, fromObjectRecursive } from "data/observable"; import { Observable, fromObject, fromObjectRecursive } from "data/observable";
import { ViewBase } from "ui/core/view-base"; import { ViewBase } from "ui/core/view-base";
import { BindingOptions } from "ui/core/bindable"; import { BindingOptions } from "ui/core/bindable";
import * as TKUnit from "../TKUnit"; import * as TKUnit from "../TKUnit";
@@ -25,7 +25,7 @@ import { TextField } from "ui/text-field";
// </snippet> // </snippet>
export function test_Bindable_Members() { export function test_Bindable_Members() {
const obj = new ViewBase(); const obj = new Label();
TKUnit.assert(types.isDefined(obj.bind), "Bindable.bind not defined"); TKUnit.assert(types.isDefined(obj.bind), "Bindable.bind not defined");
TKUnit.assert(types.isDefined(obj.unbind), "Bindable.unbind not defined"); TKUnit.assert(types.isDefined(obj.unbind), "Bindable.unbind not defined");
}; };
@@ -36,17 +36,17 @@ export function test_Bindable_Bind_ToTarget_OneWay() {
const options: BindingOptions = { const options: BindingOptions = {
sourceProperty: "name", sourceProperty: "name",
targetProperty: "test" targetProperty: "text"
}; };
const obj = new ViewBase(); const obj = new Label();
obj.bind(options, model); obj.bind(options, model);
TKUnit.assert(obj.get("test") === "John", "Expected result after binding is [test value] === 'John'"); TKUnit.assert(obj.get("text") === "John", "Expected result after binding is [test value] === 'John'");
model.set("name", "Changed"); model.set("name", "Changed");
TKUnit.assert(obj.get("test") === "Changed", "Expected result after binding is [test value] === 'Changed'"); TKUnit.assert(obj.get("text") === "Changed", "Expected result after binding is [test value] === 'Changed'");
}; };
export function test_Bindable_Bind_ToTarget_TwoWay() { export function test_Bindable_Bind_ToTarget_TwoWay() {
@@ -55,19 +55,19 @@ export function test_Bindable_Bind_ToTarget_TwoWay() {
const options: BindingOptions = { const options: BindingOptions = {
sourceProperty: "name", sourceProperty: "name",
targetProperty: "test", targetProperty: "text",
twoWay: true twoWay: true
}; };
const obj = new ViewBase(); const obj = new Label();
obj.bind(options, model); obj.bind(options, model);
obj.set("test", "Changed"); obj.set("text", "Changed");
TKUnit.assertEqual(model.get("name"), "Changed", "Two-way binding not updating the source when target is changed."); TKUnit.assertEqual(model.get("name"), "Changed", "Two-way binding not updating the source when target is changed.");
model.set("name", "John"); model.set("name", "John");
TKUnit.assertEqual(obj.get("test"), "John", "Two-way binding not updating the target when source is changed."); TKUnit.assertEqual(obj.get("text"), "John", "Two-way binding not updating the target when source is changed.");
}; };
export function test_Bindable_Bind_ToBindingContext_OneWay() { export function test_Bindable_Bind_ToBindingContext_OneWay() {
@@ -76,15 +76,15 @@ export function test_Bindable_Bind_ToBindingContext_OneWay() {
const options: BindingOptions = { const options: BindingOptions = {
sourceProperty: "name", sourceProperty: "name",
targetProperty: "test" targetProperty: "text"
}; };
const obj = new ViewBase(); const obj = new Label();
obj.bind(options); obj.bind(options);
obj.set("test", "local"); obj.set("text", "local");
obj.bindingContext = model; obj.bindingContext = model;
TKUnit.assert(obj.get("test") === "John", "Binding to a context does not update the target property."); TKUnit.assert(obj.get("text") === "John", "Binding to a context does not update the target property.");
}; };
export function test_Bindable_Bind_ToBindingContext_TwoWay() { export function test_Bindable_Bind_ToBindingContext_TwoWay() {
@@ -93,19 +93,19 @@ export function test_Bindable_Bind_ToBindingContext_TwoWay() {
const options: BindingOptions = { const options: BindingOptions = {
sourceProperty: "name", sourceProperty: "name",
targetProperty: "test", targetProperty: "text",
twoWay: true twoWay: true
}; };
const obj = new ViewBase(); const obj = new Label();
obj.bind(options); obj.bind(options);
obj.set("test", "local"); obj.set("text", "local");
obj.bindingContext = model; obj.bindingContext = model;
TKUnit.assertEqual(obj.get("test"), "John", "Binding to a context does not update the target property."); TKUnit.assertEqual(obj.get("text"), "John", "Binding to a context does not update the target property.");
obj.set("test", "local"); obj.set("text", "local");
TKUnit.assertEqual(model.get("name"), "local", "Two-way binding to a context does not update the source property."); TKUnit.assertEqual(model.get("name"), "local", "Two-way binding to a context does not update the source property.");
}; };
@@ -115,20 +115,20 @@ export function test_Bindable_Unbind() {
const options: BindingOptions = { const options: BindingOptions = {
sourceProperty: "name", sourceProperty: "name",
targetProperty: "test" targetProperty: "text"
}; };
const obj = new ViewBase(); const obj = new Label();
obj.bind(options, model); obj.bind(options, model);
model.set("name", "John"); model.set("name", "John");
TKUnit.assert(obj.get("test") === "John", "Binding does not updates target property."); TKUnit.assert(obj.get("text") === "John", "Binding does not updates target property.");
obj.unbind("test"); obj.unbind("text");
model.set("name", "Changed"); model.set("name", "Changed");
TKUnit.assert(obj.get("test") === "John", "Unbind does not remove binding."); TKUnit.assert(obj.get("text") === "John", "Unbind does not remove binding.");
}; };
export function test_bind_NoSource_WillUse_BindingContext() { export function test_bind_NoSource_WillUse_BindingContext() {
@@ -233,7 +233,7 @@ export function test_OneBindableToBindMoreThanOneProperty_ToSameSource() {
const firstPropertyOptions: BindingOptions = { const firstPropertyOptions: BindingOptions = {
sourceProperty: "name", sourceProperty: "name",
targetProperty: "test" targetProperty: "text"
}; };
const secondPropertyOptions: BindingOptions = { const secondPropertyOptions: BindingOptions = {
@@ -241,14 +241,14 @@ export function test_OneBindableToBindMoreThanOneProperty_ToSameSource() {
targetProperty: "targetProperty" targetProperty: "targetProperty"
}; };
const obj = new ViewBase(); const obj = new Label();
obj.bind(firstPropertyOptions, model); obj.bind(firstPropertyOptions, model);
obj.bind(secondPropertyOptions, model); obj.bind(secondPropertyOptions, model);
model.set("name", "John"); model.set("name", "John");
model.set("sourceProperty", "testValue"); model.set("sourceProperty", "testValue");
TKUnit.assertEqual(obj.get("test"), "John", "Binding does not updates target property."); TKUnit.assertEqual(obj.get("text"), "John", "Binding does not updates target property.");
TKUnit.assertEqual(obj.get("targetProperty"), "testValue", "Binding does not updates target property1."); TKUnit.assertEqual(obj.get("targetProperty"), "testValue", "Binding does not updates target property1.");
} }
@@ -260,10 +260,10 @@ export function test_MoreThanOneBindables_BindToASameSourceAndProperty() {
targetProperty: "targetProperty" targetProperty: "targetProperty"
}; };
const obj1 = new ViewBase(); const obj1 = new Label();
obj1.bind(bindingOptions, model); obj1.bind(bindingOptions, model);
const obj2 = new ViewBase(); const obj2 = new Label();
obj2.bind(bindingOptions, model); obj2.bind(bindingOptions, model);
model.set("sourceProperty", "testValue"); model.set("sourceProperty", "testValue");
@@ -285,7 +285,7 @@ class TestClass extends ViewBase {
} }
}; };
export function test_WhenBindingSetsInvalidValue_NoExptionIsThrown() { export function test_WhenBindingSetsInvalidValue_NoExceptionIsThrown() {
const model = new Observable(); const model = new Observable();
const options: BindingOptions = { const options: BindingOptions = {
@@ -357,27 +357,27 @@ export function test_binding_bindingContext_setBindingFirst() {
}; };
export function test_Bindable_BindingContext_Number_DoesNotThrow() { export function test_Bindable_BindingContext_Number_DoesNotThrow() {
const obj = new ViewBase(); const obj = new Label();
obj.bindingContext = 42; obj.bindingContext = 42;
}; };
export function test_Bindable_BindingContext_Boolean_DoesNotThrow() { export function test_Bindable_BindingContext_Boolean_DoesNotThrow() {
const obj = new ViewBase(); const obj = new Label();
obj.bindingContext = true; obj.bindingContext = true;
}; };
export function test_Bindable_BindingContext_String_DoesNotThrow() { export function test_Bindable_BindingContext_String_DoesNotThrow() {
const options: BindingOptions = { const options: BindingOptions = {
sourceProperty: "length", sourceProperty: "length",
targetProperty: "test" targetProperty: "text"
}; };
const obj = new ViewBase(); const obj = new Label();
obj.bind(options); obj.bind(options);
obj.set("test", "local"); obj.set("text", "local");
obj.bindingContext = "string"; obj.bindingContext = "string";
TKUnit.assert(obj.get("test") === 6, "Expected: 6; Actual: " + obj.get("test")); TKUnit.assert(obj.get("text") === 6, "Expected: 6; Actual: " + obj.get("text"));
}; };
export function test_getBindableOptionsFromStringFullFormat() { export function test_getBindableOptionsFromStringFullFormat() {
@@ -483,7 +483,7 @@ export function test_bindingToNestedPropertyWithValueSyntax() {
const bindingSource = new Observable(); const bindingSource = new Observable();
bindingSource.set("testProperty", "testValue"); bindingSource.set("testProperty", "testValue");
const testElement = new ViewBase(); const testElement = new Label();
testElement.bind({ testElement.bind({
sourceProperty: "$value.testProperty", sourceProperty: "$value.testProperty",
targetProperty: "targetPropertyName" targetProperty: "targetPropertyName"
@@ -499,8 +499,8 @@ export function test_TwoElementsBindingToSameBindingContext() {
const label1 = <Label>(page.getViewById("label1")); const label1 = <Label>(page.getViewById("label1"));
const label2 = <Label>(page.getViewById("label2")); const label2 = <Label>(page.getViewById("label2"));
TKUnit.assertEqual(upperStackLabel.text, label1.text); TKUnit.assertEqual(upperStackLabel.text, label1.text, "label1");
TKUnit.assertEqual(upperStackLabel.text, label2.text); TKUnit.assertEqual(upperStackLabel.text, label2.text, "label2");
}; };
const moduleName = __dirname.substr(fs.knownFolders.currentApp().path.length); const moduleName = __dirname.substr(fs.knownFolders.currentApp().path.length);
helper.navigateToModuleAndRunTest(("." + moduleName + "/bindingContext_testPage"), null, testFunc); helper.navigateToModuleAndRunTest(("." + moduleName + "/bindingContext_testPage"), null, testFunc);
@@ -662,28 +662,28 @@ export function test_UpdatingNestedPropertyViaBinding() {
viewModel.set("parentView", parentViewModel); viewModel.set("parentView", parentViewModel);
parentViewModel.set("name", expectedValue1); parentViewModel.set("name", expectedValue1);
const testElement: ViewBase = new ViewBase(); const testElement = new Label();
testElement.bind({ testElement.bind({
sourceProperty: "parentView.name", sourceProperty: "parentView.name",
targetProperty: "targetName", targetProperty: "text",
twoWay: true twoWay: true
}, viewModel); }, viewModel);
const testElement2: ViewBase = new ViewBase(); const testElement2 = new Label();
testElement2.bind({ testElement2.bind({
sourceProperty: "parentView.name", sourceProperty: "parentView.name",
targetProperty: "targetProperty", targetProperty: "text",
twoWay: true twoWay: true
}, viewModel); }, viewModel);
TKUnit.assertEqual(testElement.get("targetName"), expectedValue1); TKUnit.assertEqual(testElement.get("text"), expectedValue1);
testElement.set("targetName", expectedValue2); testElement.set("text", expectedValue2);
TKUnit.assertEqual(parentViewModel.get("name"), expectedValue2); TKUnit.assertEqual(parentViewModel.get("name"), expectedValue2);
TKUnit.assertEqual(testElement2.get("targetProperty"), expectedValue2); TKUnit.assertEqual(testElement2.get("text"), expectedValue2);
} }
class Person extends Observable { class Person extends Observable {
@@ -754,7 +754,7 @@ export function test_NestedPropertiesBinding() {
const viewModel = new Observable(); const viewModel = new Observable();
viewModel.set("activity", new Activity(expectedValue, "Default First Name", "Default Last Name")); viewModel.set("activity", new Activity(expectedValue, "Default First Name", "Default Last Name"));
const target1 = new ViewBase(); const target1 = new Label();
target1.bind({ target1.bind({
sourceProperty: "activity.Text", sourceProperty: "activity.Text",
targetProperty: "targetProperty", targetProperty: "targetProperty",
@@ -783,7 +783,7 @@ export function test_WrongNestedPropertiesBinding() {
}; };
trace.addWriter(traceWriter); trace.addWriter(traceWriter);
const target1 = new ViewBase(); const target1 = new Label();
target1.bind({ target1.bind({
sourceProperty: "activity.", sourceProperty: "activity.",
targetProperty: "targetProperty", targetProperty: "targetProperty",
@@ -801,14 +801,14 @@ export function test_NestedPropertiesBindingTwoTargets() {
const viewModel = new Observable(); const viewModel = new Observable();
viewModel.set("activity", new Activity(expectedText, expectedFirstName, expectedLastName)); viewModel.set("activity", new Activity(expectedText, expectedFirstName, expectedLastName));
const target1 = new ViewBase(); const target1 = new Label();
target1.bind({ target1.bind({
sourceProperty: "activity.Text", sourceProperty: "activity.Text",
targetProperty: "targetProperty", targetProperty: "targetProperty",
twoWay: true twoWay: true
}, viewModel); }, viewModel);
const target2 = new ViewBase(); const target2 = new Label();
target2.bind({ target2.bind({
sourceProperty: "activity.Owner.FirstName", sourceProperty: "activity.Owner.FirstName",
targetProperty: "targetProp", targetProperty: "targetProp",
@@ -836,14 +836,14 @@ export function test_NestedPropertiesBindingTwoTargetsAndSecondChange() {
const viewModel = new Observable(); const viewModel = new Observable();
viewModel.set("activity", new Activity(expectedText, expectedFirstName, expectedLastName)); viewModel.set("activity", new Activity(expectedText, expectedFirstName, expectedLastName));
const target1 = new ViewBase(); const target1 = new Label();
target1.bind({ target1.bind({
sourceProperty: "activity.Text", sourceProperty: "activity.Text",
targetProperty: "targetProperty", targetProperty: "targetProperty",
twoWay: true twoWay: true
}, viewModel); }, viewModel);
const target2 = new ViewBase(); const target2 = new Label();
target2.bind({ target2.bind({
sourceProperty: "activity.Owner.FirstName", sourceProperty: "activity.Owner.FirstName",
targetProperty: "targetProp", targetProperty: "targetProp",
@@ -881,14 +881,14 @@ export function test_NestedPropertiesBindingTwoTargetsAndRegularChange() {
const viewModel = new Observable(); const viewModel = new Observable();
viewModel.set("activity", new Activity(expectedText, expectedFirstName, expectedLastName)); viewModel.set("activity", new Activity(expectedText, expectedFirstName, expectedLastName));
const target1 = new ViewBase(); const target1 = new Label();
target1.bind({ target1.bind({
sourceProperty: "activity.Text", sourceProperty: "activity.Text",
targetProperty: "targetProperty", targetProperty: "targetProperty",
twoWay: true twoWay: true
}, viewModel); }, viewModel);
const target2 = new ViewBase(); const target2 = new Label();
target2.bind({ target2.bind({
sourceProperty: "activity.Owner.FirstName", sourceProperty: "activity.Owner.FirstName",
targetProperty: "targetProp", targetProperty: "targetProp",
@@ -925,14 +925,14 @@ export function test_NestedPropertiesBindingTwoTargetsAndReplacingSomeNestedObje
const viewModel = new Observable(); const viewModel = new Observable();
viewModel.set("activity", new Activity(expectedText, expectedFirstName, expectedLastName)); viewModel.set("activity", new Activity(expectedText, expectedFirstName, expectedLastName));
const target1 = new ViewBase(); const target1 = new Label();
target1.bind({ target1.bind({
sourceProperty: "activity.Text", sourceProperty: "activity.Text",
targetProperty: "targetProperty", targetProperty: "targetProperty",
twoWay: true twoWay: true
}, viewModel); }, viewModel);
const target2 = new ViewBase(); const target2 = new Label();
target2.bind({ target2.bind({
sourceProperty: "activity.Owner.FirstName", sourceProperty: "activity.Owner.FirstName",
targetProperty: "targetProp", targetProperty: "targetProp",
@@ -965,7 +965,7 @@ export function test_NestedPropertiesBindingTwoTargetsAndReplacingSomeNestedObje
export function test_NullSourcePropertyShouldNotCrash() { export function test_NullSourcePropertyShouldNotCrash() {
const expectedValue = "Expected Value"; const expectedValue = "Expected Value";
const target = new ViewBase(); const target = new Label();
const convFunc = function (value) { const convFunc = function (value) {
return value + "Converted"; return value + "Converted";
}; };
@@ -1024,7 +1024,7 @@ export function test_BindingHitsGetterTooManyTimes() {
const model = new Dummy(); const model = new Dummy();
model.dummyProperty = "OPA"; model.dummyProperty = "OPA";
const bindableObj = new ViewBase(); const bindableObj = new Label();
bindableObj.bind({ sourceProperty: "dummyProperty", targetProperty: "dummyTarget" }, model); bindableObj.bind({ sourceProperty: "dummyProperty", targetProperty: "dummyTarget" }, model);
@@ -1032,53 +1032,53 @@ export function test_BindingHitsGetterTooManyTimes() {
}; };
export function test_SupportFunctionsInExpressions() { export function test_SupportFunctionsInExpressions() {
const model = new Observable({ const model = fromObject({
"anyColor": "red", "anyColor": "red",
"isVisible": function () { "isVisible": function () {
return this.get("anyColor") === "red"; return this.get("anyColor") === "red";
} }
}); });
const bindableObj = new ViewBase(); const bindableObj = new Label();
bindableObj.bind({ bindableObj.bind({
"sourceProperty": "$value", "sourceProperty": "$value",
"targetProperty": "test", "targetProperty": "text",
"expression": "isVisible() ? 'visible' : 'collapsed'" "expression": "isVisible() ? 'visible' : 'collapsed'"
}, model); }, model);
model.set("anyColor", "blue"); model.set("anyColor", "blue");
TKUnit.assertEqual(bindableObj.get("test"), "collapsed", "When anyColor is blue test property should be collapsed."); TKUnit.assertEqual(bindableObj.get("text"), "collapsed", "When anyColor is blue test property should be collapsed.");
model.set("anyColor", "red"); model.set("anyColor", "red");
TKUnit.assertEqual(bindableObj.get("test"), "visible", "When anyColor is red test property should be visible."); TKUnit.assertEqual(bindableObj.get("text"), "visible", "When anyColor is red test property should be visible.");
} }
export function test_$ValueSupportWithinExpression() { export function test_$ValueSupportWithinExpression() {
const model = new Observable({ const model = fromObject({
"anyColor": "red", "anyColor": "red",
"isVisible": function () { "isVisible": function () {
return this.get("anyColor") === "red"; return this.get("anyColor") === "red";
} }
}); });
const bindableObj = new ViewBase(); const bindableObj = new Label();
bindableObj.bind({ bindableObj.bind({
"sourceProperty": "$value", "sourceProperty": "$value",
"targetProperty": "test", "targetProperty": "text",
"expression": "$value.anyColor === 'red' ? 'red' : 'blue'" "expression": "$value.anyColor === 'red' ? 'red' : 'blue'"
}, model); }, model);
model.set("anyColor", "blue"); model.set("anyColor", "blue");
TKUnit.assertEqual(bindableObj.get("test"), "blue", "When anyColor is blue test property should be blue too."); TKUnit.assertEqual(bindableObj.get("text"), "blue", "When anyColor is blue test property should be blue too.");
model.set("anyColor", "red"); model.set("anyColor", "red");
TKUnit.assertEqual(bindableObj.get("test"), "red", "When anyColor is red test property should be red too."); TKUnit.assertEqual(bindableObj.get("text"), "red", "When anyColor is red test property should be red too.");
TKUnit.assertTrue(model['$value'] === undefined, "We should not add $value to binding context."); TKUnit.assertTrue(model['$value'] === undefined, "We should not add $value to binding context.");
} }
@@ -1141,14 +1141,14 @@ export function test_BindingToPropertiesWithSameNames() {
secondsobject.secondsobject = 1; secondsobject.secondsobject = 1;
model.item.secondsobject = secondsobject; model.item.secondsobject = secondsobject;
const target1 = new ViewBase(); const target1 = new Label();
target1.bind({ target1.bind({
sourceProperty: "item.seconds", sourceProperty: "item.seconds",
targetProperty: "targetProperty", targetProperty: "targetProperty",
twoWay: true twoWay: true
}, model); }, model);
const target2 = new ViewBase(); const target2 = new Label();
target2.bind({ target2.bind({
sourceProperty: "item.secondsobject.secondsobject", sourceProperty: "item.secondsobject.secondsobject",
targetProperty: "targetProp", targetProperty: "targetProp",
@@ -1178,14 +1178,14 @@ export function test_BindingToPropertiesWithSameNamesSecondCase() {
secondsobject.secondsobject = 1; secondsobject.secondsobject = 1;
model.item.secondsobject = secondsobject; model.item.secondsobject = secondsobject;
const target1 = new ViewBase(); const target1 = new Label();
target1.bind({ target1.bind({
sourceProperty: "item.seconds", sourceProperty: "item.seconds",
targetProperty: "targetProperty", targetProperty: "targetProperty",
twoWay: true twoWay: true
}, model); }, model);
const target2 = new ViewBase(); const target2 = new Label();
target2.bind({ target2.bind({
sourceProperty: "item.secondsobject.secondsobject", sourceProperty: "item.secondsobject.secondsobject",
targetProperty: "targetProp", targetProperty: "targetProp",
@@ -1239,10 +1239,10 @@ export function test_BindingToRelatedProps() {
model.prop1 = false; model.prop1 = false;
model.prop2 = "Alabala"; model.prop2 = "Alabala";
let target1 = new ViewBase(); let target1 = new Label();
target1.bind({ sourceProperty: 'prop1', targetProperty: 'targetProp1' }, model); target1.bind({ sourceProperty: 'prop1', targetProperty: 'targetProp1' }, model);
let target2 = new ViewBase(); let target2 = new Label();
target2.bind({ sourceProperty: 'prop2', targetProperty: 'targetProp2' }, model); target2.bind({ sourceProperty: 'prop2', targetProperty: 'targetProp2' }, model);
model.prop2 = "Tralala"; model.prop2 = "Tralala";
@@ -1253,11 +1253,11 @@ export function test_BindingToRelatedProps() {
export function test_only_Bindable_BindingContext_Null_DoesNotThrow() { export function test_only_Bindable_BindingContext_Null_DoesNotThrow() {
const options: BindingOptions = { const options: BindingOptions = {
sourceProperty: "a.b", sourceProperty: "a.b",
targetProperty: "test" targetProperty: "text"
}; };
const obj = new ViewBase(); const obj = new Label();
obj.bind(options); obj.bind(options);
obj.bindingContext = new Observable({ a: "b" }); obj.bindingContext = fromObject({ a: "b" });
obj.bindingContext = null; obj.bindingContext = null;
} }
@@ -1271,15 +1271,15 @@ export function test_Observable_from_nested_json_binds_correctly() {
} }
}); });
const obj = new ViewBase(); const obj = new Label();
obj.bind({ obj.bind({
sourceProperty: "firstObject.secondObject.dummyProperty", sourceProperty: "firstObject.secondObject.dummyProperty",
targetProperty: "test" targetProperty: "text"
}, model); }, model);
model.get("firstObject").get("secondObject").set("dummyProperty", expectedValue); model.get("firstObject").get("secondObject").set("dummyProperty", expectedValue);
TKUnit.assertEqual(obj.get("test"), expectedValue); TKUnit.assertEqual(obj.get("text"), expectedValue);
} }
export function test_Observable_from_nested_json_binds_correctly_when_upper_object_is_changed() { export function test_Observable_from_nested_json_binds_correctly_when_upper_object_is_changed() {
@@ -1292,15 +1292,15 @@ export function test_Observable_from_nested_json_binds_correctly_when_upper_obje
} }
}); });
const obj = new ViewBase(); const obj = new Label();
obj.bind({ obj.bind({
sourceProperty: "firstObject.secondObject.dummyProperty", sourceProperty: "firstObject.secondObject.dummyProperty",
targetProperty: "test" targetProperty: "text"
}, model); }, model);
model.get("firstObject").set("secondObject", new Observable({ "dummyProperty": expectedValue })); model.get("firstObject").set("secondObject", fromObject({ "dummyProperty": expectedValue }));
TKUnit.assertEqual(obj.get("test"), expectedValue); TKUnit.assertEqual(obj.get("text"), expectedValue);
} }
export function test_BindingToBindingContextProperty_ShouldUseNewContext() { export function test_BindingToBindingContextProperty_ShouldUseNewContext() {

View File

@@ -70,16 +70,6 @@ declare module "data/observable" {
*/ */
public static propertyChangeEvent: string; public static propertyChangeEvent: string;
/**
* [Deprecated please use static functions fromJSON or fromJSONRecursive instead] Creates an Observable instance and sets its properties according to the supplied JSON object.
*/
constructor(json?: any);
/**
* Gets the name of the constructor function for this instance. E.g. for a Button class this will return "Button".
*/
typeName: string;
/** /**
* A basic method signature to hook an event listener (shortcut alias to the addEventListener method). * A basic method signature to hook an event listener (shortcut alias to the addEventListener method).
* @param eventNames - String corresponding to events (e.g. "propertyChange"). Optionally could be used more events separated by `,` (e.g. "propertyChange", "change"). * @param eventNames - String corresponding to events (e.g. "propertyChange"). Optionally could be used more events separated by `,` (e.g. "propertyChange", "change").
@@ -145,7 +135,6 @@ declare module "data/observable" {
/** /**
* This method is intended to be overriden by inheritors to provide additional implementation. * This method is intended to be overriden by inheritors to provide additional implementation.
*/ */
_setCore(data: PropertyChangeData);
_createPropertyChangeData(name: string, value: any): PropertyChangeData; _createPropertyChangeData(name: string, value: any): PropertyChangeData;
_emit(eventNames: string); _emit(eventNames: string);
//@endprivate //@endprivate

View File

@@ -1,43 +1,28 @@
import * as types from "utils/types"; import { Observable as ObservableDefinition, WrappedValue as WrappedValueDefinition, EventData, PropertyChangeData } from "data/observable";
import * as definition from "data/observable";
interface ListenerEntry { interface ListenerEntry {
callback: (data: definition.EventData) => void; callback: (data: EventData) => void;
thisArg: any; thisArg: any;
} }
var _wrappedIndex = 0; let _wrappedIndex = 0;
export class WrappedValue implements definition.WrappedValue { export class WrappedValue implements WrappedValueDefinition {
private _wrapped: any; constructor(public wrapped: any) {
public get wrapped(): any {
return this._wrapped;
}
public set wrapped(value) {
this._wrapped = value;
}
constructor(value: any) {
this._wrapped = value;
} }
public static unwrap(value: any) { public static unwrap(value: any) {
if (value && value.wrapped) { return (value && value.wrapped) ? value.wrapped : value;
return value.wrapped;
}
return value;
} }
public static wrap(value: any) { public static wrap(value: any) {
var w = _wrappedValues[_wrappedIndex++ % 5]; const w = _wrappedValues[_wrappedIndex++ % 5];
w.wrapped = value; w.wrapped = value;
return w; return w;
} }
} }
var _wrappedValues = [ let _wrappedValues = [
new WrappedValue(null), new WrappedValue(null),
new WrappedValue(null), new WrappedValue(null),
new WrappedValue(null), new WrappedValue(null),
@@ -45,37 +30,26 @@ var _wrappedValues = [
new WrappedValue(null) new WrappedValue(null)
] ]
export class Observable implements definition.Observable { export class Observable implements ObservableDefinition {
public static propertyChangeEvent = "propertyChange"; public static propertyChangeEvent = "propertyChange";
_map: Map<string, Object>;
private _observers = {}; private _observers = {};
constructor(source?: any) { public get(name: string): any {
if (source) { return this[name];
addPropertiesFromObject(this, source);
}
} }
_defineNewProperty(propertyName: string): void { public set(name: string, value: any) {
Object.defineProperty(this, propertyName, { // TODO: Parameter validation
get: function () { if (this[name] === value) {
return this._map.get(propertyName); return;
},
set: function (value) {
this._map.set(propertyName, value);
this.notify(this._createPropertyChangeData(propertyName, value));
},
enumerable: true,
configurable: true
});
} }
get typeName(): string { const newValue = WrappedValue.unwrap(value);
return types.getClass(this); this[name] = newValue;
this.notifyPropertyChange(name, newValue);
} }
public on(eventNames: string, callback: (data: definition.EventData) => void, thisArg?: any) { public on(eventNames: string, callback: (data: EventData) => void, thisArg?: any) {
this.addEventListener(eventNames, callback, thisArg); this.addEventListener(eventNames, callback, thisArg);
} }
@@ -83,18 +57,19 @@ export class Observable implements definition.Observable {
this.removeEventListener(eventNames, callback, thisArg); this.removeEventListener(eventNames, callback, thisArg);
} }
public addEventListener(eventNames: string, callback: (data: definition.EventData) => void, thisArg?: any) { public addEventListener(eventNames: string, callback: (data: EventData) => void, thisArg?: Object) {
if (!types.isString(eventNames)) { if (typeof eventNames !== "string") {
throw new TypeError("Events name(s) must be string."); throw new TypeError("Events name(s) must be string.");
} }
types.verifyCallback(callback); if (typeof callback !== "function") {
throw new TypeError("callback must be function.");
}
var events: Array<string> = eventNames.split(","); const events = eventNames.split(",");
for (let i = 0, l = events.length; i < l; i++) {
for (var i = 0, l = events.length; i < l; i++) { const event = events[i].trim();
var event = events[i].trim(); const list = this._getEventList(event, true);
var list = this._getEventList(event, true);
// TODO: Performance optimization - if we do not have the thisArg specified, do not wrap the callback in additional object (ObserveEntry) // TODO: Performance optimization - if we do not have the thisArg specified, do not wrap the callback in additional object (ObserveEntry)
list.push({ list.push({
callback: callback, callback: callback,
@@ -103,19 +78,22 @@ export class Observable implements definition.Observable {
} }
} }
public removeEventListener(eventNames: string, callback?: any, thisArg?: any) { public removeEventListener(eventNames: string, callback?: any, thisArg?: Object) {
if (!types.isString(eventNames)) { if (typeof eventNames !== "string") {
throw new TypeError("Events name(s) must be string."); throw new TypeError("Events name(s) must be string.");
} }
var events: Array<string> = eventNames.split(","); if (callback && typeof callback !== "function") {
throw new TypeError("callback must be function.");
}
for (var i = 0, l = events.length; i < l; i++) { const events = eventNames.split(",");
var event = events[i].trim(); for (let i = 0, l = events.length; i < l; i++) {
const event = events[i].trim();
if (callback) { if (callback) {
var list = this._getEventList(event, false); const list = this._getEventList(event, false);
if (list) { if (list) {
var index = this._indexOfListener(list, callback, thisArg); const index = this._indexOfListener(list, callback, thisArg);
if (index >= 0) { if (index >= 0) {
list.splice(index, 1); list.splice(index, 1);
} }
@@ -131,54 +109,14 @@ export class Observable implements definition.Observable {
} }
} }
public notifyPropertyChange(propertyName: string, newValue: any) { public notify<T extends EventData>(data: T) {
this.notify(this._createPropertyChangeData(propertyName, newValue)); const observers = this._getEventList(data.eventName);
}
public set(name: string, value: any) {
// TODO: Parameter validation
if (this[name] === value) {
return;
}
// create data for the change
var data = this._createPropertyChangeData(name, value);
this._setCore(data);
this.notify(data);
// TODO: Maybe we need to update source object used in the constructor as well?
}
public get(name: string): any {
return this[name];
}
//private disableNotifications = false;
private disableNotifications = {};
public _setCore(data: definition.PropertyChangeData) {
this.disableNotifications[data.propertyName] = true;
let newValue = WrappedValue.unwrap(data.value);
this[data.propertyName] = newValue;
delete this.disableNotifications[data.propertyName];
}
public notify<T extends definition.EventData>(data: T) {
if (this.disableNotifications[(<any>data).propertyName]) {
return;
}
var observers = this._getEventList(data.eventName);
if (!observers) { if (!observers) {
return; return;
} }
var i; for (let i = observers.length - 1; i >= 0; i--) {
var entry: ListenerEntry; let entry = observers[i];
var observersLength = observers.length;
for (i = observersLength - 1; i >= 0; i--) {
entry = observers[i];
if (entry.thisArg) { if (entry.thisArg) {
entry.callback.apply(entry.thisArg, [data]); entry.callback.apply(entry.thisArg, [data]);
} else { } else {
@@ -187,11 +125,15 @@ export class Observable implements definition.Observable {
} }
} }
public notifyPropertyChange(name: string, newValue: any) {
this.notify(this._createPropertyChangeData(name, newValue));
}
public hasListeners(eventName: string) { public hasListeners(eventName: string) {
return eventName in this._observers; return eventName in this._observers;
} }
public _createPropertyChangeData(name: string, value: any): definition.PropertyChangeData { public _createPropertyChangeData(name: string, value: any): PropertyChangeData {
return { return {
eventName: Observable.propertyChangeEvent, eventName: Observable.propertyChangeEvent,
propertyName: name, propertyName: name,
@@ -201,10 +143,10 @@ export class Observable implements definition.Observable {
} }
public _emit(eventNames: string) { public _emit(eventNames: string) {
var events: Array<string> = eventNames.split(","); const events = eventNames.split(",");
for (var i = 0, l = events.length; i < l; i++) { for (let i = 0, l = events.length; i < l; i++) {
var event = events[i].trim(); const event = events[i].trim();
this.notify({ eventName: event, object: this }); this.notify({ eventName: event, object: this });
} }
} }
@@ -214,7 +156,7 @@ export class Observable implements definition.Observable {
throw new TypeError("EventName must be valid string."); throw new TypeError("EventName must be valid string.");
} }
var list = <Array<ListenerEntry>>this._observers[eventName]; let list = <Array<ListenerEntry>>this._observers[eventName];
if (!list && createIfNeeded) { if (!list && createIfNeeded) {
list = []; list = [];
this._observers[eventName] = list; this._observers[eventName] = list;
@@ -223,12 +165,9 @@ export class Observable implements definition.Observable {
return list; return list;
} }
private _indexOfListener(list: Array<ListenerEntry>, callback: (data: definition.EventData) => void, thisArg?: any): number { private _indexOfListener(list: Array<ListenerEntry>, callback: (data: EventData) => void, thisArg?: any): number {
var i; for (let i = 0; i < list.length; i++) {
var entry: ListenerEntry; const entry = list[i];
for (i = 0; i < list.length; i++) {
entry = list[i];
if (thisArg) { if (thisArg) {
if (entry.callback === callback && entry.thisArg === thisArg) { if (entry.callback === callback && entry.thisArg === thisArg) {
return i; return i;
@@ -243,36 +182,59 @@ export class Observable implements definition.Observable {
return -1; return -1;
} }
}
public toString(): string { class ObservableFromObject extends Observable {
return this.typeName; public _map: Map<string, Object> = new Map<string, Object>();
public set(name: string, value: any) {
const currentValue = this._map.get(name);
if (currentValue === value) {
return;
}
const newValue = WrappedValue.unwrap(value);
this._map.set(name, newValue);
this.notifyPropertyChange(name, newValue);
} }
} }
function addPropertiesFromObject(observable: Observable, source: any, recursive?: boolean) { function defineNewProperty(target: ObservableFromObject, propertyName: string): void {
let isRecursive = recursive || false; Object.defineProperty(target, propertyName, {
observable._map = new Map<string, Object>(); get: function () {
return target._map.get(propertyName);
},
set: function (value) {
target.set(propertyName, value);
},
enumerable: true,
configurable: true
});
}
function addPropertiesFromObject(observable: ObservableFromObject, source: any, recursive: boolean = false) {
let isRecursive = recursive;
for (let prop in source) { for (let prop in source) {
if (source.hasOwnProperty(prop)) { if (source.hasOwnProperty(prop)) {
if (isRecursive) { if (isRecursive) {
if (!Array.isArray(source[prop]) && source[prop] && typeof source[prop] === 'object' && types.getClass(source[prop]) !== 'ObservableArray') { if (!Array.isArray(source[prop]) && source[prop] && typeof source[prop] === 'object' && !(source[prop] instanceof Observable)) {
source[prop] = fromObjectRecursive(source[prop]); source[prop] = fromObjectRecursive(source[prop]);
} }
} }
observable._defineNewProperty(prop); defineNewProperty(observable, prop);
observable.set(prop, source[prop]); observable.set(prop, source[prop]);
} }
} }
} }
export function fromObject(source: any): Observable { export function fromObject(source: any): Observable {
let observable = new Observable(); let observable = new ObservableFromObject();
addPropertiesFromObject(observable, source, false); addPropertiesFromObject(observable, source, false);
return observable; return observable;
} }
export function fromObjectRecursive(source: any): Observable { export function fromObjectRecursive(source: any): Observable {
let observable = new Observable(); let observable = new ObservableFromObject();
addPropertiesFromObject(observable, source, true); addPropertiesFromObject(observable, source, true);
return observable; return observable;
} }

View File

@@ -1,5 +1,4 @@
import { ActivityIndicatorBase, busyProperty, colorProperty } from "./activity-indicator-common"; import { ActivityIndicatorBase, busyProperty, colorProperty } from "./activity-indicator-common";
import { ios } from "utils/utils";
import { Color } from "color"; import { Color } from "color";
export * from "./activity-indicator-common"; export * from "./activity-indicator-common";

View File

@@ -1,27 +1,15 @@
import * as definition from "ui/core/bindable"; import * as definition from "ui/core/bindable";
import { Observable, PropertyChangeData } from "data/observable"; import { Observable, PropertyChangeData } from "data/observable";
import { unsetValue, DependencyObservable, Property } from "ui/core/dependency-observable"; import { unsetValue, DependencyObservable } from "ui/core/dependency-observable";
import { addWeakEventListener, removeWeakEventListener } from "ui/core/weak-event-listener"; import { addWeakEventListener, removeWeakEventListener } from "ui/core/weak-event-listener";
import types = require("utils/types"); import types = require("utils/types");
import bindingBuilder = require("../builder/binding-builder"); import bindingBuilder = require("../builder/binding-builder");
import { ViewBase, isEventOrGesture, bindingContextProperty } from "ui/core/view-base"; import { ViewBase, isEventOrGesture, bindingContextProperty } from "ui/core/view-base";
import * as application from "application"; import * as application from "application";
import * as polymerExpressions from "js-libs/polymer-expressions"; import * as polymerExpressions from "js-libs/polymer-expressions";
import * as specialProperties from "ui/builder/special-properties";
import * as utils from "utils/utils"; import * as utils from "utils/utils";
import { enabled as traceEnabled, write as traceWrite, categories as traceCategories, messageType as traceMessageType } from "trace"; import { write as traceWrite, categories as traceCategories, messageType as traceMessageType } from "trace";
// let bindingContextProperty = new Property(
// "bindingContext",
// "Bindable",
// new PropertyMetadata(undefined, PropertyMetadataSettings.Inheritable, onBindingContextChanged)
// );
// function onBindingContextChanged(data: DependencyPropertyChangeData) {
// let bindable = <Bindable>data.object;
// bindable._onBindingContextChanged(data.oldValue, data.newValue);
// }
let contextKey = "context"; let contextKey = "context";
// this regex is used to get parameters inside [] for example: // this regex is used to get parameters inside [] for example:
@@ -75,72 +63,72 @@ export class Bindable extends DependencyObservable implements definition.Bindabl
} }
} }
public _updateTwoWayBinding(propertyName: string, value: any) { // public _updateTwoWayBinding(propertyName: string, value: any) {
let binding: Binding = this.bindings.get(propertyName); // let binding: Binding = this.bindings.get(propertyName);
if (binding) { // if (binding) {
binding.updateTwoWay(value); // binding.updateTwoWay(value);
}
}
public _setCore(data: PropertyChangeData) {
super._setCore(data);
this._updateTwoWayBinding(data.propertyName, data.value);
}
public _onPropertyChanged(property: Property, oldValue: any, newValue: any) {
if (traceEnabled) {
traceWrite(`${this}._onPropertyChanged(${property.name}, ${oldValue}, ${newValue})`, traceCategories.Binding);
}
super._onPropertyChanged(property, oldValue, newValue);
// if (this instanceof viewModule.View) {
// if (property.inheritable && (<viewModule.View>(<any>this))._isInheritedChange() === true) {
// return;
// } // }
// } // }
let binding = this.bindings.get(property.name); // public _setCore(data: PropertyChangeData) {
if (binding && !binding.updating) { // super._setCore(data);
if (binding.options.twoWay) { // this._updateTwoWayBinding(data.propertyName, data.value);
if (traceEnabled) { // }
traceWrite(`${this}._updateTwoWayBinding(${property.name}, ${newValue});` + property.name, traceCategories.Binding);
} // public _onPropertyChanged(property: Property, oldValue: any, newValue: any) {
this._updateTwoWayBinding(property.name, newValue); // if (traceEnabled) {
} // traceWrite(`${this}._onPropertyChanged(${property.name}, ${oldValue}, ${newValue})`, traceCategories.Binding);
else { // }
if (traceEnabled) { // super._onPropertyChanged(property, oldValue, newValue);
traceWrite(`${this}.unbind(${property.name});`, traceCategories.Binding); // // if (this instanceof viewModule.View) {
} // // if (property.inheritable && (<viewModule.View>(<any>this))._isInheritedChange() === true) {
this.unbind(property.name); // // return;
} // // }
} // // }
// let binding = this.bindings.get(property.name);
// if (binding && !binding.updating) {
// if (binding.options.twoWay) {
// if (traceEnabled) {
// traceWrite(`${this}._updateTwoWayBinding(${property.name}, ${newValue});` + property.name, traceCategories.Binding);
// }
// this._updateTwoWayBinding(property.name, newValue);
// }
// else {
// if (traceEnabled) {
// traceWrite(`${this}.unbind(${property.name});`, traceCategories.Binding);
// }
// this.unbind(property.name);
// }
// }
// }
// public _onBindingContextChanged(oldValue: any, newValue: any) {
// let bindingContextBinding = this.bindings.get("bindingContext");
// if (bindingContextBinding) {
// if (!bindingContextBinding.updating) {
// bindingContextBinding.bind(newValue);
// }
// }
// let bindingContextSource = this.bindingContext;
// this.bindings.forEach((binding, index, bindings) => {
// if (!binding.updating && binding.sourceIsBindingContext && binding.options.targetProperty !== "bindingContext") {
// if (traceEnabled) {
// traceWrite(`Binding ${binding.target.get()}.${binding.options.targetProperty} to new context ${bindingContextSource}`, traceCategories.Binding);
// }
// if (!types.isNullOrUndefined(bindingContextSource)) {
// binding.bind(bindingContextSource);
// } else {
// binding.clearBinding();
// }
// }
// });
// }
} }
public _onBindingContextChanged(oldValue: any, newValue: any) { const emptyArray = [];
let bindingContextBinding = this.bindings.get("bindingContext");
if (bindingContextBinding) {
if (!bindingContextBinding.updating) {
bindingContextBinding.bind(newValue);
}
}
let bindingContextSource = this.bindingContext;
this.bindings.forEach((binding, index, bindings) => {
if (!binding.updating && binding.sourceIsBindingContext && binding.options.targetProperty !== "bindingContext") {
if (traceEnabled) {
traceWrite(`Binding ${binding.target.get()}.${binding.options.targetProperty} to new context ${bindingContextSource}`, traceCategories.Binding);
}
if (!types.isNullOrUndefined(bindingContextSource)) {
binding.bind(bindingContextSource);
} else {
binding.clearBinding();
}
}
});
}
}
let emptyArray = [];
function getProperties(property: string): Array<string> { function getProperties(property: string): Array<string> {
let result: Array<string> = emptyArray; let result: Array<string> = emptyArray;
if (property) { if (property) {
@@ -188,13 +176,24 @@ export class Binding {
if (!this.targetOptions) { if (!this.targetOptions) {
throw new Error(`Invalid property: ${options.targetProperty} for target: ${target}`); throw new Error(`Invalid property: ${options.targetProperty} for target: ${target}`);
} }
if (options.twoWay) {
const target = this.targetOptions.instance.get();
if (target instanceof Observable) {
target.on(`${this.targetOptions.property}Change`, this.onTargetPropertyChanged, this);
}
}
}
private onTargetPropertyChanged(data: PropertyChangeData): void {
this.updateTwoWay(data.value);
} }
public loadedHandlerVisualTreeBinding(args) { public loadedHandlerVisualTreeBinding(args) {
let target = args.object; let target = args.object;
target.off("loaded", this.loadedHandlerVisualTreeBinding, this); target.off("loaded", this.loadedHandlerVisualTreeBinding, this);
if (!types.isNullOrUndefined(target.bindingContext)) { if (!types.isNullOrUndefined(target.bindingContext)) {
this.bind(target.bindingContext); this.update(target.bindingContext);
} }
}; };
@@ -237,25 +236,39 @@ export class Binding {
} }
private bindingContextChanged(data: PropertyChangeData): void { private bindingContextChanged(data: PropertyChangeData): void {
let target = this.target.get(); const target = this.targetOptions.instance.get();
if (!target) { if (!target) {
this.unbind(); this.unbind();
return; return;
} }
if (data.value) { if (data.value) {
this.bind(data.value); this.update(data.value);
} else { } else {
// TODO: Is this correct?
// What should happen when bindingContext is null/undefined?
this.clearBinding(); this.clearBinding();
} }
// TODO: if oneWay - call target.unbind();
} }
public bind(source: Object): void { public bind(source: any): void {
const target = this.targetOptions.instance.get();
if (this.sourceIsBindingContext && target instanceof Observable && this.targetOptions.property !== "bindingContext") {
target.on("bindingContextChange", this.bindingContextChanged, this);
}
this.update(source);
}
private update(source: any): void {
this.clearSource(); this.clearSource();
source = this.sourceAsObject(source); source = this.sourceAsObject(source);
if (!types.isNullOrUndefined(source)) { if (!types.isNullOrUndefined(source)) {
// TODO: if oneWay - call target.unbind();
this.source = new WeakRef(source); this.source = new WeakRef(source);
this.sourceOptions = this.resolveOptions(source, this.sourceProperties); this.sourceOptions = this.resolveOptions(source, this.sourceProperties);
@@ -263,14 +276,36 @@ export class Binding {
this.updateTarget(sourceValue); this.updateTarget(sourceValue);
this.addPropertyChangeListeners(this.source, this.sourceProperties); this.addPropertyChangeListeners(this.source, this.sourceProperties);
} else if (!this.sourceIsBindingContext) { } else if (!this.sourceIsBindingContext) {
// TODO: if oneWay - call target.unbind();
let sourceValue = this.getSourcePropertyValue(); let sourceValue = this.getSourcePropertyValue();
this.updateTarget(sourceValue ? sourceValue : source); this.updateTarget(sourceValue ? sourceValue : source);
} else if (this.sourceIsBindingContext && (source === undefined || source === null)) {
this.target.get().off("bindingContextChange", this.bindingContextChanged, this);
this.target.get().on("bindingContextChange", this.bindingContextChanged, this);
} }
} }
public unbind() {
const target = this.targetOptions.instance.get();
if (target instanceof Observable) {
if (this.options.twoWay) {
target.off(`${this.targetOptions.property}Change`, this.onTargetPropertyChanged, this);
}
if (this.sourceIsBindingContext && this.targetOptions.property !== "bindingContext") {
target.off("bindingContextChange", this.bindingContextChanged, this);
}
}
if (this.targetOptions) {
this.targetOptions = undefined;
}
this.sourceProperties = undefined;
if (!this.source) {
return;
}
this.clearSource();
}
// Consider returning single {} instead of array for performance. // Consider returning single {} instead of array for performance.
private resolveObjectsAndProperties(source: Object, properties: Array<string>): Array<{ instance: Object; property: string }> { private resolveObjectsAndProperties(source: Object, properties: Array<string>): Array<{ instance: Object; property: string }> {
let result = []; let result = [];
@@ -330,24 +365,6 @@ export class Binding {
} }
} }
public unbind() {
if (!this.source) {
return;
}
this.clearSource();
let target = this.target.get();
if (target) {
target.off(`${bindingContextProperty}Change`, this.bindingContextChanged, this);
}
if (this.targetOptions) {
this.targetOptions = undefined;
}
this.sourceProperties = undefined;
}
private prepareExpressionForUpdate(): string { private prepareExpressionForUpdate(): string {
// this regex is used to create a valid RegExp object from a string that has some special regex symbols like [,(,$ and so on. // this regex is used to create a valid RegExp object from a string that has some special regex symbols like [,(,$ and so on.
// Basically this method replaces all matches of 'source property' in expression with '$newPropertyValue'. // Basically this method replaces all matches of 'source property' in expression with '$newPropertyValue'.
@@ -361,7 +378,7 @@ export class Binding {
return resultExp; return resultExp;
} }
public updateTwoWay(value: any) { private updateTwoWay(value: any) {
if (this.updating || !this.options.twoWay) { if (this.updating || !this.options.twoWay) {
return; return;
} }
@@ -432,8 +449,8 @@ export class Binding {
} }
public onSourcePropertyChanged(data: PropertyChangeData) { public onSourcePropertyChanged(data: PropertyChangeData) {
let sourceProps = this.sourceProperties; const sourceProps = this.sourceProperties;
let sourcePropsLength = sourceProps.length; const sourcePropsLength = sourceProps.length;
let changedPropertyIndex = sourceProps.indexOf(data.propertyName); let changedPropertyIndex = sourceProps.indexOf(data.propertyName);
let parentProps = ""; let parentProps = "";
if (changedPropertyIndex > -1) { if (changedPropertyIndex > -1) {
@@ -445,16 +462,16 @@ export class Binding {
} }
if (this.options.expression) { if (this.options.expression) {
let expressionValue = this._getExpressionValue(this.options.expression, false, undefined); const expressionValue = this._getExpressionValue(this.options.expression, false, undefined);
if (expressionValue instanceof Error) { if (expressionValue instanceof Error) {
traceWrite((<Error>expressionValue).message, traceCategories.Binding, traceMessageType.error); traceWrite(expressionValue.message, traceCategories.Binding, traceMessageType.error);
} else { } else {
this.updateTarget(expressionValue); this.updateTarget(expressionValue);
} }
} else { } else {
if (changedPropertyIndex > -1) { if (changedPropertyIndex > -1) {
let props = sourceProps.slice(changedPropertyIndex + 1); const props = sourceProps.slice(changedPropertyIndex + 1);
let propsLength = props.length; const propsLength = props.length;
if (propsLength > 0) { if (propsLength > 0) {
let value = data.value; let value = data.value;
for (let i = 0; i < propsLength; i++) { for (let i = 0; i < propsLength; i++) {
@@ -468,15 +485,15 @@ export class Binding {
} }
} }
// we need to do this only if nested objects are used as source and some middle object is changed. // we need to do this only if nested objects are used as source and some middle object has changed.
if (changedPropertyIndex > -1 && changedPropertyIndex < sourcePropsLength - 1) { if (changedPropertyIndex > -1 && changedPropertyIndex < sourcePropsLength - 1) {
var probablyChangedObject = this.propertyChangeListeners.get(parentProps); const probablyChangedObject = this.propertyChangeListeners.get(parentProps);
if (probablyChangedObject && if (probablyChangedObject &&
probablyChangedObject !== data.object[sourceProps[changedPropertyIndex]]) { probablyChangedObject !== data.object[sourceProps[changedPropertyIndex]]) {
// remove all weakevent listeners after change, because changed object replaces object that is hooked for // remove all weakevent listeners after change, because changed object replaces object that is hooked for
// propertyChange event // propertyChange event
for (let i = sourcePropsLength - 1; i > changedPropertyIndex; i--) { for (let i = sourcePropsLength - 1; i > changedPropertyIndex; i--) {
let prop = "$" + sourceProps.slice(0, i + 1).join("$"); const prop = "$" + sourceProps.slice(0, i + 1).join("$");
if (this.propertyChangeListeners.has(prop)) { if (this.propertyChangeListeners.has(prop)) {
removeWeakEventListener( removeWeakEventListener(
this.propertyChangeListeners.get(prop), this.propertyChangeListeners.get(prop),
@@ -487,9 +504,9 @@ export class Binding {
} }
} }
let newProps = sourceProps.slice(changedPropertyIndex + 1); const newProps = sourceProps.slice(changedPropertyIndex + 1);
// add new weak event listeners // add new weak event listeners
var newObject = data.object[sourceProps[changedPropertyIndex]] const newObject = data.object[sourceProps[changedPropertyIndex]]
if (!types.isNullOrUndefined(newObject) && typeof newObject === 'object') { if (!types.isNullOrUndefined(newObject) && typeof newObject === 'object') {
this.addPropertyChangeListeners(new WeakRef(newObject), newProps, parentProps); this.addPropertyChangeListeners(new WeakRef(newObject), newProps, parentProps);
} }
@@ -559,7 +576,7 @@ export class Binding {
public clearBinding() { public clearBinding() {
this.clearSource(); this.clearSource();
this.updateTarget(undefined); this.updateTarget(unsetValue);
} }
private updateTarget(value: any) { private updateTarget(value: any) {
@@ -638,25 +655,19 @@ export class Binding {
this.updating = true; this.updating = true;
try { try {
if (optionsInstance instanceof ViewBase && const isView = optionsInstance instanceof ViewBase
if (isView &&
isEventOrGesture(options.property, <any>optionsInstance) && isEventOrGesture(options.property, <any>optionsInstance) &&
types.isFunction(value)) { types.isFunction(value)) {
// calling off method with null as handler will remove all handlers for options.property event // calling off method with null as handler will remove all handlers for options.property event
optionsInstance.off(options.property, null, optionsInstance.bindingContext); optionsInstance.off(options.property, null, optionsInstance.bindingContext);
optionsInstance.on(options.property, value, optionsInstance.bindingContext); optionsInstance.on(options.property, value, optionsInstance.bindingContext);
} else { } else if (!isView && optionsInstance instanceof Observable) {
let specialSetter = specialProperties.getSpecialPropertySetter(options.property);
if (specialSetter) {
specialSetter(optionsInstance, value);
} else {
if (optionsInstance instanceof Observable) {
optionsInstance.set(options.property, value); optionsInstance.set(options.property, value);
} else { } else {
optionsInstance[options.property] = value; optionsInstance[options.property] = value;
} }
} }
}
}
catch (ex) { catch (ex) {
traceWrite("Binding error while setting property " + options.property + " of " + optionsInstance + ": " + ex, traceWrite("Binding error while setting property " + options.property + " of " + optionsInstance + ": " + ex,
traceCategories.Binding, traceCategories.Binding,

View File

@@ -286,8 +286,7 @@ export class DependencyObservable extends Observable implements DependencyObserv
let propName = property.name; let propName = property.name;
if (this.hasListeners(Observable.propertyChangeEvent)) { if (this.hasListeners(Observable.propertyChangeEvent)) {
let changeData = super._createPropertyChangeData(propName, newValue); this.notifyPropertyChange(propName, newValue);
this.notify(changeData);
} }
let eventName = property.nameEvent; let eventName = property.nameEvent;
@@ -325,10 +324,6 @@ export class DependencyObservable extends Observable implements DependencyObserv
} }
} }
public toString(): string {
return this.typeName;
}
private _setValueInternal(property: Property, value: any, source: number) { private _setValueInternal(property: Property, value: any, source: number) {
if (value === unsetValue) { if (value === unsetValue) {
this._resetValue(property, source); this._resetValue(property, source);

View File

@@ -322,8 +322,6 @@ export class InheritedProperty<T extends ViewBase, U> extends Property<T, U> imp
const key = this.key; const key = this.key;
const defaultValue = options.defaultValue; const defaultValue = options.defaultValue;
const eventName = name + "Change";
const sourceKey = Symbol(name + ":valueSourceKey"); const sourceKey = Symbol(name + ":valueSourceKey");
this.sourceKey = sourceKey; this.sourceKey = sourceKey;
@@ -360,16 +358,6 @@ export class InheritedProperty<T extends ViewBase, U> extends Property<T, U> imp
that[sourceKey] = newValueSource; that[sourceKey] = newValueSource;
if (currentValue !== newValue) { if (currentValue !== newValue) {
if (this.hasListeners(eventName)) {
this.notify({
eventName: eventName,
propertyName: name,
object: this,
value: unboxedValue
});
}
const reset = newValueSource === ValueSource.Default; const reset = newValueSource === ValueSource.Default;
that.eachChild((child) => { that.eachChild((child) => {
const childValueSource = child[sourceKey] || ValueSource.Default; const childValueSource = child[sourceKey] || ValueSource.Default;
@@ -379,7 +367,7 @@ export class InheritedProperty<T extends ViewBase, U> extends Property<T, U> imp
} }
} else { } else {
if (childValueSource <= ValueSource.Inherited) { if (childValueSource <= ValueSource.Inherited) {
setInheritedValue.call(child, child.parent[key]); setInheritedValue.call(child, newValue);
} }
} }
return true; return true;

View File

@@ -1,5 +1,5 @@
import { ViewBase as ViewBaseDefinition } from "ui/core/view-base"; import { ViewBase as ViewBaseDefinition } from "ui/core/view-base";
import { Observable, EventData } from "data/observable"; import { Observable, EventData, PropertyChangeData } from "data/observable";
import { Property, InheritedProperty, Style, clearInheritedProperties, propagateInheritedProperties, resetCSSProperties, applyNativeSetters, resetStyleProperties } from "./properties"; import { Property, InheritedProperty, Style, clearInheritedProperties, propagateInheritedProperties, resetCSSProperties, applyNativeSetters, resetStyleProperties } from "./properties";
import { Binding, BindingOptions, Bindable } from "ui/core/bindable"; import { Binding, BindingOptions, Bindable } from "ui/core/bindable";
import { isIOS, isAndroid } from "platform"; import { isIOS, isAndroid } from "platform";
@@ -9,6 +9,9 @@ import { KeyframeAnimation } from "ui/animation/keyframe-animation";
import { enabled as traceEnabled, write as traceWrite, categories as traceCategories, notifyEvent as traceNotifyEvent, isCategorySet } from "trace"; import { enabled as traceEnabled, write as traceWrite, categories as traceCategories, notifyEvent as traceNotifyEvent, isCategorySet } from "trace";
// TODO: Remove this import!
import * as types from "utils/types";
import * as ssm from "ui/styling/style-scope"; import * as ssm from "ui/styling/style-scope";
let styleScopeModule: typeof ssm; let styleScopeModule: typeof ssm;
function ensureStyleScopeModule() { function ensureStyleScopeModule() {
@@ -46,6 +49,8 @@ export function getEventOrGestureName(name: string): string {
return name.indexOf("on") === 0 ? name.substr(2, name.length - 2) : name; return name.indexOf("on") === 0 ? name.substr(2, name.length - 2) : name;
} }
// TODO: Make this instance function so that we dont need public statc tapEvent = "tap"
// in controls. They will just override this one and provide their own event support.
export function isEventOrGesture(name: string, view: ViewBaseDefinition): boolean { export function isEventOrGesture(name: string, view: ViewBaseDefinition): boolean {
if (typeof name === "string") { if (typeof name === "string") {
let eventOrGestureName = getEventOrGestureName(name); let eventOrGestureName = getEventOrGestureName(name);
@@ -127,6 +132,10 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
this._style = new Style(this); this._style = new Style(this);
} }
get typeName(): string {
return types.getClass(this);
}
get style(): Style { get style(): Style {
return this._style; return this._style;
} }
@@ -348,39 +357,63 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
} }
} }
private bindings = new Map<string, Binding>(); private bindingContextChanged(data: PropertyChangeData): void {
public bind(options: BindingOptions, source: Object = defaultBindingSource): void { this.bindings.get("bindingContext").bind(data.value);
let binding: Binding = this.bindings.get(options.targetProperty);
if (binding) {
binding.unbind();
} }
binding = new Binding(this, options); private bindings: Map<string, Binding>;
this.bindings.set(options.targetProperty, binding); private shouldAddHandlerToParentBindingContextChanged: boolean;
private bindingContextBoundToParentBindingContextChanged: boolean;
public bind(options: BindingOptions, source: Object = defaultBindingSource): void {
const targetProperty = options.targetProperty;
this.unbind(targetProperty);
if (!this.bindings) {
this.bindings = new Map<string, Binding>();
}
const binding = new Binding(this, options);
this.bindings.set(targetProperty, binding);
let bindingSource = source; let bindingSource = source;
if (bindingSource === defaultBindingSource) { if (bindingSource === defaultBindingSource) {
bindingSource = this.bindingContext; bindingSource = this.bindingContext;
binding.sourceIsBindingContext = true; binding.sourceIsBindingContext = true;
if (targetProperty === "bindingContext") {
this.bindingContextBoundToParentBindingContextChanged = true;
const parent = this.parent;
if (parent) {
parent.on("bindingContextChange", this.bindingContextChanged, this);
} else {
this.shouldAddHandlerToParentBindingContextChanged = true;
}
}
} }
// if (!types.isNullOrUndefined(bindingSource)) {
binding.bind(bindingSource); binding.bind(bindingSource);
// }
} }
public unbind(property: string): void { public unbind(property: string): void {
let binding: Binding = this.bindings.get(property); const bindings = this.bindings;
if (binding) { if (!bindings) {
binding.unbind(); return;
this.bindings.delete(property);
}
} }
public _updateTwoWayBinding(propertyName: string, value: any) { const binding = bindings.get(property);
let binding: Binding = this.bindings.get(propertyName);
if (binding) { if (binding) {
binding.updateTwoWay(value); binding.unbind();
bindings.delete(property);
if (binding.sourceIsBindingContext) {
if (property === "bindingContext") {
this.shouldAddHandlerToParentBindingContextChanged = false;
this.bindingContextBoundToParentBindingContextChanged = false;
const parent = this.parent;
if (parent) {
parent.off("bindingContextChange", this.bindingContextChanged, this);
}
}
}
} }
} }
@@ -593,8 +626,14 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
public _parentChanged(oldParent: ViewBase): void { public _parentChanged(oldParent: ViewBase): void {
//Overridden //Overridden
if (oldParent) { if (oldParent) {
// Move these method in property class.
clearInheritedProperties(this); clearInheritedProperties(this);
if (this.bindingContextBoundToParentBindingContextChanged) {
oldParent.parent.off("bindingContextChange", this.bindingContextChanged, this);
}
} else if (this.shouldAddHandlerToParentBindingContextChanged) {
const parent = this.parent;
parent.on("bindingContextChange", this.bindingContextChanged, this);
this.bindings.get("bindingContext").bind(parent.bindingContext);
} }
} }

View File

@@ -54,6 +54,11 @@ declare module "ui/core/view-base" {
public nativeView: any; public nativeView: any;
public bindingContext: any; public bindingContext: any;
/**
* Gets the name of the constructor function for this instance. E.g. for a Button class this will return "Button".
*/
public typeName: string;
/** /**
* Gets the parent view. This property is read-only. * Gets the parent view. This property is read-only.
*/ */

View File

@@ -52,7 +52,7 @@ export class Progress extends ProgressBase {
} }
} }
get [backgroundColorProperty.native](): UIColor { get [backgroundColorProperty.native](): number {
return null; return null;
} }
set [backgroundColorProperty.native](value: Color) { set [backgroundColorProperty.native](value: Color) {
@@ -73,7 +73,7 @@ export class Progress extends ProgressBase {
} }
} }
get [backgroundInternalProperty.native](): UIColor { get [backgroundInternalProperty.native](): number {
return null; return null;
} }
set [backgroundInternalProperty.native](value: Color) { set [backgroundInternalProperty.native](value: Color) {

View File

@@ -117,6 +117,8 @@ export class ProxyViewContainer extends LayoutBase implements ProxyViewContainer
* Register/unregister existing children with the parent layout. * Register/unregister existing children with the parent layout.
*/ */
public _parentChanged(oldParent: View): void { public _parentChanged(oldParent: View): void {
// call super in order to execute base logic like clear inherited properties, etc.
super._parentChanged(oldParent);
const addingToParent = this.parent && !oldParent; const addingToParent = this.parent && !oldParent;
const newLayout = <LayoutBase>this.parent; const newLayout = <LayoutBase>this.parent;
const oldLayout = <LayoutBase>oldParent; const oldLayout = <LayoutBase>oldParent;