Skip to content

Commit 4152e66

Browse files
author
Nedyalko Nikolov
committed
Observable array now fires property change for length property.
1 parent b04d1fc commit 4152e66

7 files changed

Lines changed: 125 additions & 45 deletions

File tree

apps/tests/app/binding_tests.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
11
import pageModule = require("ui/page");
22
//import stackLayoutModule = require("ui/layouts/stack-layout");
33
//import textFieldModule = require("ui/text-field");
4+
import buttonModule = require("ui/button");
45
import observableModule = require("data/observable");
6+
import observableArray = require("data/observable-array");
57
import bindableModule = require("ui/core/bindable");
68
//import enums = require("ui/enums");
79
import trace = require("trace");
8-
trace.setCategories(trace.categories.Test);
10+
trace.setCategories(trace.categories.Test + "," + trace.categories.Binding);
911
trace.enable();
1012

1113
export function pageLoaded(args: observableModule.EventData) {
1214
var page: pageModule.Page = <pageModule.Page>args.object;
1315
var model = new observableModule.Observable();
16+
var tasks = new observableArray.ObservableArray();
17+
//tasks.push("tralala");
1418
//var model = page.bindingContext;
19+
model.set("tasks", tasks);
1520
model.set("paramProperty", "%%%");
1621
var toUpperConverter: bindableModule.ValueConverter = {
1722
toModel: function (value, param1) {
1823
return param1 + value.toLowerCase();
1924
},
2025
toView: function (value, param1) {
21-
return value.toUpperCase();
26+
if (value === 0) {
27+
return "no items";
28+
}
29+
return value + " items";
2230
}
2331
};
2432
model.set("toUpper", toUpperConverter);
@@ -27,6 +35,12 @@ export function pageLoaded(args: observableModule.EventData) {
2735
page.bindingContext = model;
2836
}
2937

38+
export function onTap(args: observableModule.EventData) {
39+
var button: buttonModule.Button = <buttonModule.Button>args.object;
40+
trace.write("tasks: " + button.bindingContext.get("tasks"), trace.categories.Test, trace.messageType.info);
41+
button.bindingContext.get("tasks").push("alabala");
42+
}
43+
3044
//export function createPage() {
3145
// var stackLayout = new stackLayoutModule.StackLayout();
3246
// var firstTextField = new textFieldModule.TextField();

apps/tests/app/binding_tests.xml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<Page xmlns="http://www.nativescript.org/tns.xsd" loaded="pageLoaded">
22
<StackLayout padding="7">
3-
<TextField text="{{ testProperty, testProperty | toUpper(paramProperty) }}" />
4-
<!--<TextField text="{{ testProperty | toUpper }}" />-->
5-
<TextField text="{{ testProperty }}" />
3+
<TextField text="{{ tasks.length, tasks.length === 0 ? 'zero items' : tasks.length + ' items', false }}" />
4+
<Button text="Click" tap="onTap" />
65
</StackLayout>
76
</Page>

apps/tests/observable-array-tests.ts

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import TKUnit = require("./TKUnit");
2+
import bindableModule = require("ui/core/bindable");
23
require("globals");
34

45
// <snippet module="data/observable-array" title="observable-array">
@@ -80,34 +81,6 @@ export var test_ObservableArray_concatShouldReturnNewArrayWithNewItemsAtTheEnd =
8081
TKUnit.assert(result.length === 6 && result[4] === 5, "ObservableArray concat() should add items at the end!");
8182
};
8283

83-
export var test_ObservableArray_concatShouldReturnNewArrayWithNewItemsAtTheEndAndRaiseChangeEventWithCorrectArgs = function () {
84-
var result: observableArrayModule.ChangedData<number>;
85-
// <snippet module="data/observable-array" title="observable-array">
86-
// ### Use concat() method to append array to ObservableArray and handle "change" event.
87-
// ``` JavaScript
88-
var array = new observableArrayModule.ObservableArray([1, 2, 3]);
89-
90-
array.on(observableArrayModule.knownEvents.change, (args: observableArrayModule.ChangedData<number>) => {
91-
//// Argument (args) is ChangedData<T>.
92-
//// args.eventName is "change".
93-
//// args.action is "add".
94-
//// args.index is equal to the array length.
95-
//// args.removed.length is 0.
96-
//// args.addedCount is equal to number of added items.
97-
98-
// <hide>
99-
result = args;
100-
// </hide>
101-
});
102-
103-
array.concat([4, 5, 6]);
104-
// ```
105-
// </snippet>
106-
107-
TKUnit.assert(result.eventName === "change" && result.action === observableArrayModule.ChangeType.Add &&
108-
result.removed.length === 0 && result.index === 3 && result.addedCount === 3, "ObservableArray concat() should raise 'change' event with correct args!");
109-
};
110-
11184
export var test_ObservableArray_joinShouldReturnStringWithAllItemsSeparatedWithComma = function () {
11285
// <snippet module="data/observable-array" title="observable-array">
11386
// ### Use join() method to convert ObservableArray to comma separated string.
@@ -135,10 +108,16 @@ export var test_ObservableArray_popShouldRemoveTheLastElement = function () {
135108
// ### Use pop() method to remove the last element.
136109
// ``` JavaScript
137110
var array = new observableArrayModule.ObservableArray([1, 2, 3]);
111+
// <hide>
112+
var bindable = new bindableModule.Bindable();
113+
bindable.set("testProperty", 0);
114+
bindable.bind({ sourceProperty: "length", targetProperty: "testProperty" }, array);
115+
// </hide>
138116
var result = array.pop();
139117
// ```
140118
// </snippet>
141119
TKUnit.assert(result === 3 && array.length === 2, "ObservableArray pop() should remove last element!");
120+
TKUnit.assert(bindable.get("testProperty") === array.length, "Expected: " + array.length + ", Actual: " + bindable.get("testProperty"));
142121
};
143122

144123
export var test_ObservableArray_popShouldRemoveTheLastElementAndRaiseChangeEventWithCorrectArgs = function () {
@@ -176,10 +155,16 @@ export var test_ObservableArray_pushShouldAppendNewElement = function () {
176155
// ### Use push() method to add single element to the array.
177156
// ``` JavaScript
178157
var array = new observableArrayModule.ObservableArray([1, 2, 3]);
158+
// <hide>
159+
var bindable = new bindableModule.Bindable();
160+
bindable.set("testProperty", 0);
161+
bindable.bind({ sourceProperty: "length", targetProperty: "testProperty" }, array);
162+
// </hide>
179163
var result = array.push(4);
180164
// ```
181165
// </snippet>
182166
TKUnit.assert(result === 4 && array.getItem(3) === 4, "ObservableArray push() should append new element!");
167+
TKUnit.assert(bindable.get("testProperty") === array.length, "Expected: " + array.length + ", Actual: " + bindable.get("testProperty"));
183168
};
184169

185170
export var test_ObservableArray_pushShouldAppendNewElementAndRaiseChangeEventWithCorrectArgs = function () {
@@ -215,10 +200,16 @@ export var test_ObservableArray_pushShouldAppendNewElements = function () {
215200
// ### Use push() method to add multiple elements to the array.
216201
// ``` JavaScript
217202
var array = new observableArrayModule.ObservableArray([1, 2, 3]);
203+
// <hide>
204+
var bindable = new bindableModule.Bindable();
205+
bindable.set("testProperty", 0);
206+
bindable.bind({ sourceProperty: "length", targetProperty: "testProperty" }, array);
207+
// </hide>
218208
var result = array.push(4, 5, 6);
219209
// ```
220210
// </snippet>
221211
TKUnit.assert(result === 6 && array.getItem(5) === 6, "ObservableArray push() should append new elements!");
212+
TKUnit.assert(bindable.get("testProperty") === array.length, "Expected: " + array.length + ", Actual: " + bindable.get("testProperty"));
222213
};
223214

224215
export var test_ObservableArray_pushShouldAppendNewElementsAndRaiseChangeEventWithCorrectArgs = function () {
@@ -254,10 +245,16 @@ export var test_ObservableArray_pushShouldAppendNewElementsFromSourceArray = fun
254245
// ### Use push() method to add multiple elements from source array to the ObservableArray.
255246
// ``` JavaScript
256247
var array = new observableArrayModule.ObservableArray([1, 2, 3]);
248+
// <hide>
249+
var bindable = new bindableModule.Bindable();
250+
bindable.set("testProperty", 0);
251+
bindable.bind({ sourceProperty: "length", targetProperty: "testProperty" }, array);
252+
// </hide>
257253
var result = array.push([4, 5, 6]);
258254
// ```
259255
// </snippet>
260256
TKUnit.assert(result === 6 && array.getItem(5) === 6, "ObservableArray push() should append new elements from source array!");
257+
TKUnit.assert(bindable.get("testProperty") === array.length, "Expected: " + array.length + ", Actual: " + bindable.get("testProperty"));
261258
};
262259

263260
export var test_ObservableArray_pushShouldAppendNewElementsFromSourceArrayAndRaiseChangeEventWithCorrectArgs = function () {
@@ -304,10 +301,16 @@ export var test_ObservableArray_shiftShouldRemoveTheFirstElement = function () {
304301
// ### Use shift() method to remove the first element of the array.
305302
// ``` JavaScript
306303
var array = new observableArrayModule.ObservableArray([1, 2, 3]);
304+
// <hide>
305+
var bindable = new bindableModule.Bindable();
306+
bindable.set("testProperty", 0);
307+
bindable.bind({ sourceProperty: "length", targetProperty: "testProperty" }, array);
308+
// </hide>
307309
var result = array.shift();
308310
// ```
309311
// </snippet>
310312
TKUnit.assert(result === 1 && array.length === 2, "ObservableArray shift() should remove first element!");
313+
TKUnit.assert(bindable.get("testProperty") === array.length, "Expected: " + array.length + ", Actual: " + bindable.get("testProperty"));
311314
};
312315

313316
export var test_ObservableArray_shiftShouldRemoveTheFirstElementAndRaiseChangeEventWithCorrectArgs = function () {
@@ -388,11 +391,17 @@ export var test_ObservableArray_spliceShouldRemoveSpecifiedNumberOfElementsStart
388391
// ### Use splice(start, deleteCount) method to delete elements in the array.
389392
// ``` JavaScript
390393
var array = new observableArrayModule.ObservableArray(["one", "two", "three"]);
394+
// <hide>
395+
var bindable = new bindableModule.Bindable();
396+
bindable.set("testProperty", 0);
397+
bindable.bind({ sourceProperty: "length", targetProperty: "testProperty" }, array);
398+
// </hide>
391399
var result = array.splice(1, 2);
392400
// ```
393401
// </snippet>
394402
TKUnit.assert(result.length === 2 && result[0] === "two" && array.length === 1 && array.getItem(0) === "one",
395403
"ObservableArray splice() should remove specified number of elements starting from specified index!");
404+
TKUnit.assert(bindable.get("testProperty") === array.length, "Expected: " + array.length + ", Actual: " + bindable.get("testProperty"));
396405
};
397406

398407
export var test_ObservableArray_spliceShouldRemoveSpecifiedNumberOfElementsStartingFromSpecifiedIndexAndRaiseChangeEventWithCorrectArgs = function () {
@@ -470,11 +479,17 @@ export var test_ObservableArray_unshiftShouldInsertNewElementsFromTheStart = fun
470479
// ### Use unshift(item1, item2... itemN) method to insert elements from the start of the array.
471480
// ``` JavaScript
472481
var array = new observableArrayModule.ObservableArray([1, 2, 3]);
482+
// <hide>
483+
var bindable = new bindableModule.Bindable();
484+
bindable.set("testProperty", 0);
485+
bindable.bind({ sourceProperty: "length", targetProperty: "testProperty" }, array);
486+
// </hide>
473487
var result = array.unshift(4, 5);
474488
// ```
475489
// </snippet>
476490

477491
TKUnit.assert(array.getItem(0) === 4 && result === 5 && array.length === 5, "ObservableArray unshift() should insert new elements from the start!");
492+
TKUnit.assert(bindable.get("testProperty") === array.length, "Expected: " + array.length + ", Actual: " + bindable.get("testProperty"));
478493
};
479494

480495
export var test_ObservableArray_unshiftShouldInsertNewElementsFromTheStartAndRaiseChangeEventWithCorrectArgs = function () {

apps/tests/ui/bindable-tests.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,43 @@ export var test_Bindable_BindingContext_String_DoesNotThrow = function () {
380380

381381
TKUnit.assert(obj.get("test") === 6, "Expected: 6; Actual: " + obj.get("test"));
382382
}
383+
384+
export var test_getBindableOptionsFromStringFullFormat = function () {
385+
var bindingExpression = "bindProperty, bindProperty * 2, false";
386+
var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
387+
388+
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
389+
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
390+
TKUnit.assert(bindOptions.expression === "bindProperty * 2", "Expected: bindProperty * 2, Actual:" + bindOptions.expression);
391+
TKUnit.assert(bindOptions.twoWay === false, "Expected: false, Actual: " + bindOptions.twoWay);
392+
}
393+
394+
export var test_getBindableOptionsFromStringShortFormatExpression = function () {
395+
var bindingExpression = "bindProperty * 2";
396+
var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
397+
398+
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
399+
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
400+
TKUnit.assert(bindOptions.expression === "bindProperty * 2", "Expected: bindProperty * 2, Actual: " + bindOptions.expression);
401+
TKUnit.assert(bindOptions.twoWay === true, "Expected: true, Actual: " + bindOptions.twoWay);
402+
}
403+
404+
export var test_getBindableOptionsFromStringShortFormatProperty = function () {
405+
var bindingExpression = "bindProperty";
406+
var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
407+
408+
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
409+
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
410+
TKUnit.assert(bindOptions.expression === null, "Expected: null, Actual: " + bindOptions.expression);
411+
TKUnit.assert(bindOptions.twoWay === true, "Expected: true, Actual: " + bindOptions.twoWay);
412+
}
413+
414+
export var test_getBindableOptionsFromStringTwoParamsFormat = function () {
415+
var bindingExpression = "bindProperty, bindProperty * 2";
416+
var bindOptions = bindable.Bindable._getBindingOptions("targetBindProperty", bindingExpression);
417+
418+
TKUnit.assert(bindOptions.sourceProperty === "bindProperty", "Expected: bindProperty, Actual: " + bindOptions.sourceProperty);
419+
TKUnit.assert(bindOptions.targetProperty === "targetBindProperty", "Expected: targetBindProperty, Actual: " + bindOptions.targetProperty);
420+
TKUnit.assert(bindOptions.expression === "bindProperty * 2", "Expected: bindProperty * 2, Actual:" + bindOptions.expression);
421+
TKUnit.assert(bindOptions.twoWay === true, "Expected: true, Actual: " + bindOptions.twoWay);
422+
}

data/observable-array/observable-array.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,7 @@ export class ObservableArray<T> extends observable.Observable implements observa
8888
*/
8989
concat(): T[] {
9090
this._addArgs.index = this._array.length;
91-
9291
var result = this._array.concat.apply(this._array, arguments);
93-
94-
this._addArgs.addedCount = result.length - this._array.length;
95-
96-
this.notify(this._addArgs);
97-
9892
return result;
9993
}
10094

@@ -117,6 +111,7 @@ export class ObservableArray<T> extends observable.Observable implements observa
117111
this._deleteArgs.removed = [result];
118112

119113
this.notify(this._deleteArgs);
114+
this._notifyLengthChange();
120115

121116
return result;
122117
}
@@ -143,10 +138,16 @@ export class ObservableArray<T> extends observable.Observable implements observa
143138
this._addArgs.addedCount = this._array.length - this._addArgs.index;
144139

145140
this.notify(this._addArgs);
141+
this._notifyLengthChange();
146142

147143
return this._array.length;
148144
}
149145

146+
_notifyLengthChange() {
147+
var lengthChangedData = this._createPropertyChangeData("length", this._array.length);
148+
this.notify(lengthChangedData);
149+
}
150+
150151
/**
151152
* Reverses the elements in an Array.
152153
*/
@@ -164,6 +165,7 @@ export class ObservableArray<T> extends observable.Observable implements observa
164165
this._deleteArgs.removed = [result];
165166

166167
this.notify(this._deleteArgs);
168+
this._notifyLengthChange();
167169

168170
return result;
169171
}
@@ -202,6 +204,9 @@ export class ObservableArray<T> extends observable.Observable implements observa
202204
removed: result,
203205
addedCount: this._array.length > length ? this._array.length - length : 0
204206
});
207+
if (this._array.length !== length) {
208+
this._notifyLengthChange();
209+
}
205210

206211
return result;
207212
}
@@ -218,6 +223,7 @@ export class ObservableArray<T> extends observable.Observable implements observa
218223
this._addArgs.addedCount = result - length;
219224

220225
this.notify(this._addArgs);
226+
this._notifyLengthChange();
221227

222228
return result;
223229
}

ui/builder/component-builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export function getComponentModule(elementName: string, namespace: string, attri
114114
gridLayoutModule.GridLayout.setColumnSpan(instance, !isNaN(+attrValue) && +attrValue);
115115
} else if (attr === ROW_SPAN) {
116116
gridLayoutModule.GridLayout.setRowSpan(instance, !isNaN(+attrValue) && +attrValue);
117-
} if (attr === LEFT) {
117+
} else if (attr === LEFT) {
118118
absoluteLayoutDef.AbsoluteLayout.setLeft(instance, !isNaN(+attrValue) && +attrValue);
119119
} else if (attr === TOP) {
120120
absoluteLayoutDef.AbsoluteLayout.setTop(instance, !isNaN(+attrValue) && +attrValue);

ui/core/bindable.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export class Bindable extends dependencyObservable.DependencyObservable implemen
120120
private static extractPropertyNameFromExpression(expression: string): string {
121121
var firstExpressionSymbolIndex = expression.search(expressionSymbolsRegex);
122122
if (firstExpressionSymbolIndex > -1) {
123-
return expression.substr(0, firstExpressionSymbolIndex);
123+
return expression.substr(0, firstExpressionSymbolIndex).trim();
124124
}
125125
else {
126126
return expression;
@@ -135,9 +135,16 @@ export class Bindable extends dependencyObservable.DependencyObservable implemen
135135
};
136136
if (types.isString(bindingExpression)) {
137137
var params = bindingExpression.split(",");
138-
result.sourceProperty = Bindable.extractPropertyNameFromExpression(params[0]);
139-
result.expression = params[1];
140-
result.twoWay = params[2] ? params[2].toLowerCase() === "true" : true;
138+
if (params.length === 1) {
139+
result.sourceProperty = Bindable.extractPropertyNameFromExpression(params[0].trim());
140+
result.expression = params[0].search(expressionSymbolsRegex) > -1 ? params[0].trim() : null;
141+
result.twoWay = true;
142+
}
143+
else {
144+
result.sourceProperty = Bindable.extractPropertyNameFromExpression(params[0].trim());
145+
result.expression = params[1].trim();
146+
result.twoWay = params[2] ? params[2].toLowerCase().trim() === "true" : true;
147+
}
141148
}
142149
return result;
143150
}
@@ -276,7 +283,6 @@ export class Binding {
276283
this.sourceOptions.property in sourceOptionsInstance) {
277284
value = sourceOptionsInstance[this.sourceOptions.property];
278285
}
279-
280286
return value;
281287
}
282288

0 commit comments

Comments
 (0)