From 4e4a69c28258a277630ed828d28570c8c3f5d1c2 Mon Sep 17 00:00:00 2001 From: Nedyalko Nikolov Date: Tue, 3 May 2016 14:23:30 +0300 Subject: [PATCH] Additional fix for changing binding context issue. --- apps/tests/ui/bindable-tests.ts | 54 +++++++++++++++++++++++++++++++++ ui/core/bindable.ts | 10 ++++-- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/apps/tests/ui/bindable-tests.ts b/apps/tests/ui/bindable-tests.ts index 37a6b9b1c..8d9b4aef6 100644 --- a/apps/tests/ui/bindable-tests.ts +++ b/apps/tests/ui/bindable-tests.ts @@ -492,6 +492,7 @@ export var test_bindingToNestedPropertyWithValueSyntax = function () { }, bindingSource); TKUnit.assertEqual(testElement.get("targetPropertyName"), "testValue"); + TKUnit.assertTrue(bindingSource['$value'] === undefined, "We should not add $value to bindingSource."); } export var test_TwoElementsBindingToSameBindingContext = function () { @@ -546,6 +547,58 @@ export var test_BindingToSource_FailsAfterBindingContextChange = function () { helper.buildUIAndRunTest(createLabel(), testFunc); } +export var test_BindingToParentView_ShouldNotLeaveGarbageInViewModel = function () { + var createStack = function () { + var stack = new stackLayoutModule.StackLayout(); + var label = new labelModule.Label(); + stack.addChild(label); + return stack; + } + var stackViewModel = new observable.Observable(); + var expectedValue = "testValue"; + stackViewModel.set("testProperty", expectedValue); + + var testFunc = function (views: Array) { + let testStack = (views[0]); + testStack.bindingContext = stackViewModel; + + let testLabel = (testStack.getChildAt(0)); + testLabel.bind({ sourceProperty: "$parent.testProperty", targetProperty: "text", expression: "$parent.testProperty"}); + + TKUnit.assertEqual(testLabel.text, expectedValue); + TKUnit.assertTrue(stackViewModel['$parent'] === undefined, "stackViewModel['$parent'] should be removed from parent binding context."); + TKUnit.assertTrue(testLabel.bindingContext['$parent'] === undefined, "testLabel.bindingContext['$parent'] should be removed from parent binding context."); + } + + helper.buildUIAndRunTest(createStack(), testFunc); +} + +export var test_BindingToParentsView_ShouldNotLeaveGarbageInViewModel = function () { + var createStack = function () { + var stack = new stackLayoutModule.StackLayout(); + var label = new labelModule.Label(); + stack.addChild(label); + return stack; + } + var stackViewModel = new observable.Observable(); + var expectedValue = "testValue"; + stackViewModel.set("testProperty", expectedValue); + + var testFunc = function (views: Array) { + let testStack = (views[0]); + testStack.bindingContext = stackViewModel; + + let testLabel = (testStack.getChildAt(0)); + testLabel.bind({ sourceProperty: "$parents['StackLayout'].testProperty", targetProperty: "text", expression: "$parents['StackLayout'].testProperty"}); + + TKUnit.assertEqual(testLabel.text, expectedValue); + TKUnit.assertTrue(stackViewModel['$parent'] === undefined, "stackViewModel['$parent'] should be removed from parent binding context."); + TKUnit.assertTrue(testLabel.bindingContext['$parents'] === undefined, "testLabel.bindingContext['$parents'] should be removed from parent binding context."); + } + + helper.buildUIAndRunTest(createStack(), testFunc); +} + export function test_BindingToDictionaryAtAppLevel() { var createLabel = function () { var label = new labelModule.Label(); @@ -998,6 +1051,7 @@ export function test_$ValueSupportWithinExpression() { model.set("anyColor", "red"); TKUnit.assertEqual(bindableObj.get("test"), "red", "When anyColor is red test property should be red too."); + TKUnit.assertTrue(model['$value'] === undefined, "We should not add $value to binding context."); } class DummyNestedClass extends observable.Observable { diff --git a/ui/core/bindable.ts b/ui/core/bindable.ts index 03aa093e4..3c036333e 100644 --- a/ui/core/bindable.ts +++ b/ui/core/bindable.ts @@ -330,7 +330,7 @@ export class Binding { } let updateExpression = this.prepareExpressionForUpdate(); - this.prepareContextForExpression(changedModel, updateExpression); + this.prepareContextForExpression(changedModel, updateExpression, undefined); let expressionValue = this._getExpressionValue(updateExpression, true, changedModel); if (expressionValue instanceof Error) { @@ -357,7 +357,7 @@ export class Binding { } } - this.prepareContextForExpression(context, expression); + this.prepareContextForExpression(context, expression, addedProps); model[contextKey] = context; let result = exp.getValue(model, isBackConvert, changedModel ? changedModel : model); // clear added props @@ -443,17 +443,20 @@ export class Binding { } } - private prepareContextForExpression(model: Object, expression: string) { + private prepareContextForExpression(model: Object, expression: string, newProps: Array) { let parentViewAndIndex: { view: viewModule.View, index: number }; let parentView; + let addedProps = newProps || []; if (expression.indexOf(bc.bindingValueKey) > -1) { model[bc.bindingValueKey] = model; + addedProps.push(bc.bindingValueKey); } if (expression.indexOf(bc.parentValueKey) > -1) { parentView = this.getParentView(this.target.get(), bc.parentValueKey).view; if (parentView) { model[bc.parentValueKey] = parentView.bindingContext; + addedProps.push(bc.parentValueKey); } } @@ -464,6 +467,7 @@ export class Binding { if (parentViewAndIndex.view) { model[bc.parentsValueKey] = model[bc.parentsValueKey] || {}; model[bc.parentsValueKey][parentViewAndIndex.index] = parentViewAndIndex.view.bindingContext; + addedProps.push(bc.parentsValueKey); } } }