Merge pull request #854 from NativeScript/nnikolov/BindingContextSetBeforeAddingToVisualTree

BindingContext will be set before adding items to visual tree (ListView, Repeater).
This commit is contained in:
Nedyalko Nikolov
2015-09-30 13:36:25 +03:00
12 changed files with 64 additions and 9 deletions

View File

@ -7,7 +7,7 @@ import uiTestModule = require("./ui-test");
frameModule.Frame.defaultAnimatedNavigation = false; frameModule.Frame.defaultAnimatedNavigation = false;
function isRunningOnEmulator(): boolean { export function isRunningOnEmulator(): boolean {
// This checks are not good enough to be added to modules but keeps unittests green. // This checks are not good enough to be added to modules but keeps unittests green.
if (platform.device.os === platform.platformNames.android) { if (platform.device.os === platform.platformNames.android) {

View File

@ -6,6 +6,7 @@ import observable = require("data/observable");
import types = require("utils/types"); import types = require("utils/types");
import platform = require("platform"); import platform = require("platform");
import utils = require("utils/utils"); import utils = require("utils/utils");
import testRunner = require("../../testRunner");
// <snippet module="ui/list-view" title="list-view"> // <snippet module="ui/list-view" title="list-view">
// # ListView // # ListView
@ -633,6 +634,9 @@ export function test_ConverterIsCalledJustOnce_onAddingItemsToListView() {
} }
export function test_no_memory_leak_when_items_is_regular_array() { export function test_no_memory_leak_when_items_is_regular_array() {
if (testRunner.isRunningOnEmulator()) {
return;
}
var createFunc = function (): listViewModule.ListView { var createFunc = function (): listViewModule.ListView {
var listView = new listViewModule.ListView(); var listView = new listViewModule.ListView();
listView.items = FEW_ITEMS; listView.items = FEW_ITEMS;
@ -645,6 +649,9 @@ export function test_no_memory_leak_when_items_is_regular_array() {
} }
export function test_no_memory_leak_when_items_is_observable_array() { export function test_no_memory_leak_when_items_is_observable_array() {
if (testRunner.isRunningOnEmulator()) {
return;
}
// Keep the reference to the observable array to test the weakEventListener // Keep the reference to the observable array to test the weakEventListener
var colors = new observableArray.ObservableArray(["red", "green", "blue"]); var colors = new observableArray.ObservableArray(["red", "green", "blue"]);

View File

@ -378,3 +378,30 @@ export var test_Observable_WhenCreatedWithJSON_PropertyChangedWithBracketsNotati
TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly."); TKUnit.assert(receivedCount === 1, "PropertyChanged event not raised properly.");
} }
export function test_AddingTwoEventHandlersAndRemovingWithinHandlerShouldRaiseAllEvents() {
var observableInstance = new observable.Observable();
var firstHandlerCalled = false;
var secondHandlerCalled= false;
var firstHandler = function (args) {
observableInstance.off(observable.Observable.propertyChangeEvent, firstHandler, firstObserver);
firstHandlerCalled = true;
}
var secondHandler = function (args) {
observableInstance.off(observable.Observable.propertyChangeEvent, secondHandler, secondObserver);
secondHandlerCalled = true;
}
var firstObserver = new observable.Observable();
var secondObserver = new observable.Observable();
observableInstance.on(observable.Observable.propertyChangeEvent, firstHandler, firstObserver);
observableInstance.on(observable.Observable.propertyChangeEvent, secondHandler, secondObserver);
observableInstance.set("someProperty", "some value");
TKUnit.assertEqual(firstHandlerCalled, true);
TKUnit.assertEqual(secondHandlerCalled, true);
}

View File

@ -2,7 +2,7 @@
<Repeater id="repeater" items="{{ items }}"> <Repeater id="repeater" items="{{ items }}">
<Repeater.itemTemplate> <Repeater.itemTemplate>
<GridLayout width="94" height="94" > <GridLayout width="94" height="94" >
<Label text="{{ $parents['Page'].parentViewProperty }}" tap="{{ tapItem }}" /> <Label text="{{ sourceProperty = $parents['Page'].parentViewProperty }}" tap="{{ tapItem }}" />
</GridLayout> </GridLayout>
</Repeater.itemTemplate> </Repeater.itemTemplate>
</Repeater> </Repeater>

View File

@ -1,4 +1,5 @@
import TKUnit = require("../../TKUnit"); import TKUnit = require("../../TKUnit");
import testRunner = require("../../testRunner");
import helper = require("../helper"); import helper = require("../helper");
import viewModule = require("ui/core/view"); import viewModule = require("ui/core/view");
import pagesModule = require("ui/page"); import pagesModule = require("ui/page");
@ -427,6 +428,9 @@ export var testNativeTextAlignmentFromLocal = function () {
} }
export var testMemoryLeak = function () { export var testMemoryLeak = function () {
if (testRunner.isRunningOnEmulator()) {
return;
}
helper.buildUIWithWeakRefAndInteract(_createTextFieldFunc, function (textField) { helper.buildUIWithWeakRefAndInteract(_createTextFieldFunc, function (textField) {
textFieldTestsNative.typeTextNatively(textField, "Hello, world!"); textFieldTestsNative.typeTextNatively(textField, "Hello, world!");
}); });

View File

@ -1,4 +1,5 @@
import TKUnit = require("../../TKUnit"); import TKUnit = require("../../TKUnit");
import testRunner = require("../../testRunner");
import helper = require("../helper"); import helper = require("../helper");
import viewModule = require("ui/core/view"); import viewModule = require("ui/core/view");
import pagesModule = require("ui/page"); import pagesModule = require("ui/page");
@ -468,6 +469,9 @@ export var testNativeTextAlignmentFromLocal = function () {
} }
export var testMemoryLeak = function () { export var testMemoryLeak = function () {
if (testRunner.isRunningOnEmulator()) {
return;
}
helper.buildUIWithWeakRefAndInteract(_createTextViewFunc, function (textView) { helper.buildUIWithWeakRefAndInteract(_createTextViewFunc, function (textView) {
textViewTestsNative.typeTextNatively(textView, "Hello, world!"); textViewTestsNative.typeTextNatively(textView, "Hello, world!");
}); });

View File

@ -603,7 +603,7 @@ export function test_parse_ShouldParseNestedListViewInListViewTemplate() {
} }
export function test_parse_ShouldEvaluateEventBindingExpressionInListViewTemplate() { export function test_parse_ShouldEvaluateEventBindingExpressionInListViewTemplate() {
var p = <Page>builder.parse('<Page xmlns="http://www.nativescript.org/tns.xsd"><ListView items="{{ items }}" itemLoading="{{ itemLoading }}"><ListView.itemTemplate><SegmentedBar items="{{ $parents[\'ListView\'].items }}" selectedIndexChanged="{{ $parents[\'ListView\'].changed }}" /></ListView.itemTemplate></ListView></Page>'); var p = <Page>builder.parse('<Page xmlns="http://www.nativescript.org/tns.xsd"><ListView items="{{ items }}" itemLoading="{{ itemLoading }}"><ListView.itemTemplate><SegmentedBar items="{{ sourceProperty = $parents[\'ListView\'].items }}" selectedIndexChanged="{{ sourceProperty = $parents[\'ListView\'].changed }}" /></ListView.itemTemplate></ListView></Page>');
function testAction(views: Array<viewModule.View>) { function testAction(views: Array<viewModule.View>) {
var ctrl: segmentedBar.SegmentedBar; var ctrl: segmentedBar.SegmentedBar;

View File

@ -136,7 +136,7 @@ export class Observable implements definition.Observable {
var i; var i;
var entry: ListenerEntry; var entry: ListenerEntry;
var observersLength = observers.length; var observersLength = observers.length;
for (i = 0; i < observersLength; i++) { for (i = observersLength - 1; i >= 0 ; i--) {
entry = observers[i]; entry = observers[i];
if (entry.thisArg) { if (entry.thisArg) {
entry.callback.apply(entry.thisArg, [data]); entry.callback.apply(entry.thisArg, [data]);

View File

@ -140,6 +140,15 @@ export class Binding {
source: WeakRef<Object>; source: WeakRef<Object>;
target: WeakRef<Bindable>; target: WeakRef<Bindable>;
public loadedHandlerVisualTreeBinding(args) {
var targetInstance = args.object;
targetInstance.off(viewModule.View.loadedEvent, this.loadedHandlerVisualTreeBinding, this);
this.unbind();
if (!types.isNullOrUndefined(targetInstance.bindingContext)) {
this.bind(targetInstance.bindingContext);
}
};
private propertyChangeListeners = {}; private propertyChangeListeners = {};
private sourceOptions: { instance: WeakRef<any>; property: any }; private sourceOptions: { instance: WeakRef<any>; property: any };
@ -213,6 +222,11 @@ export class Binding {
if (parentView) { if (parentView) {
currentObject = parentView.bindingContext; currentObject = parentView.bindingContext;
} }
else {
var targetInstance = this.target.get();
targetInstance.off(viewModule.View.loadedEvent, this.loadedHandlerVisualTreeBinding, this);
targetInstance.on(viewModule.View.loadedEvent, this.loadedHandlerVisualTreeBinding, this);
}
currentObjectChanged = true; currentObjectChanged = true;
} }
result.push({ instance: currentObject, property: objProp }); result.push({ instance: currentObject, property: objProp });
@ -507,7 +521,7 @@ export class Binding {
indexAsInt--; indexAsInt--;
} }
} }
else { else if (types.isString(index)) {
while (result && result.typeName !== index) { while (result && result.typeName !== index) {
result = result.parent; result = result.parent;
} }

View File

@ -205,6 +205,7 @@ class ListViewAdapter extends android.widget.BaseAdapter {
} }
if (args.view) { if (args.view) {
this._listView._prepareItem(args.view, index);
if (!args.view.parent) { if (!args.view.parent) {
if (args.view instanceof layoutBaseModule.LayoutBase) { if (args.view instanceof layoutBaseModule.LayoutBase) {
this._listView._addView(args.view); this._listView._addView(args.view);
@ -221,8 +222,6 @@ class ListViewAdapter extends android.widget.BaseAdapter {
this._listView._realizedItems[convertView.hashCode()] = args.view; this._listView._realizedItems[convertView.hashCode()] = args.view;
// cache the realized index (used to raise the ItemLoading event upon scroll stop) // cache the realized index (used to raise the ItemLoading event upon scroll stop)
args.view[REALIZED_INDEX] = index; args.view[REALIZED_INDEX] = index;
this._listView._prepareItem(args.view, index);
} }
return convertView; return convertView;

View File

@ -239,12 +239,12 @@ export class ListView extends common.ListView {
var args = notifyForItemAtIndex(this, cell, ITEMLOADING, indexPath); var args = notifyForItemAtIndex(this, cell, ITEMLOADING, indexPath);
var view = cell.view = args.view || this._getDefaultItemContent(indexPath.row); var view = cell.view = args.view || this._getDefaultItemContent(indexPath.row);
this._prepareItem(view, indexPath.row);
if (view && !view.parent && view.ios) { if (view && !view.parent && view.ios) {
cell.contentView.addSubview(view.ios); cell.contentView.addSubview(view.ios);
this._addView(view); this._addView(view);
} }
this._prepareItem(view, indexPath.row);
cellHeight = this._layoutCell(view, indexPath); cellHeight = this._layoutCell(view, indexPath);
} }
finally { finally {

View File

@ -149,8 +149,8 @@ export class Repeater extends viewModule.CustomLayoutView implements definition.
for (i = 0; i < this.items.length; i++) { for (i = 0; i < this.items.length; i++) {
var viewToAdd = !types.isNullOrUndefined(this.itemTemplate) ? builder.parse(this.itemTemplate, this) : this._getDefaultItemContent(i); var viewToAdd = !types.isNullOrUndefined(this.itemTemplate) ? builder.parse(this.itemTemplate, this) : this._getDefaultItemContent(i);
if (!types.isNullOrUndefined(viewToAdd)) { if (!types.isNullOrUndefined(viewToAdd)) {
this.itemsLayout.addChild(viewToAdd);
viewToAdd.bindingContext = this._getDataItem(i); viewToAdd.bindingContext = this._getDataItem(i);
this.itemsLayout.addChild(viewToAdd);
} }
} }
} }