From ba2a84de6b7bf3568312d5901b7cb66a9219844b Mon Sep 17 00:00:00 2001 From: Nedyalko Nikolov Date: Fri, 26 Feb 2016 14:16:52 +0200 Subject: [PATCH] Better tracing (logging) of run-time errors. --- apps/tests/ui-test.ts | 1 - apps/tests/ui/bindable-tests.ts | 24 ++++++++++++++++++++++++ trace/trace.ts | 11 ++++++++++- ui/core/bindable.ts | 30 +++++++++++++++--------------- utils/utils.android.ts | 3 ++- utils/utils.ios.ts | 14 ++++++++++++-- 6 files changed, 63 insertions(+), 20 deletions(-) diff --git a/apps/tests/ui-test.ts b/apps/tests/ui-test.ts index 08ed1bfde..04d8fe876 100644 --- a/apps/tests/ui-test.ts +++ b/apps/tests/ui-test.ts @@ -47,7 +47,6 @@ export class UITest implements trace.TraceWriter { }; trace.addWriter(this); - trace.enable(); navHelper.navigate(pageFactory); } diff --git a/apps/tests/ui/bindable-tests.ts b/apps/tests/ui/bindable-tests.ts index 66c6ca4a9..f41e1c91e 100644 --- a/apps/tests/ui/bindable-tests.ts +++ b/apps/tests/ui/bindable-tests.ts @@ -14,6 +14,7 @@ import labelModule = require("ui/label"); import textFieldModule = require("ui/text-field"); import fs = require("file-system"); import appModule = require("application"); +import trace = require("trace"); // // For information and examples how to use bindings please refer to special [**Data binding**](../../../../bindings.md) topic. @@ -693,6 +694,29 @@ export function test_NestedPropertiesBinding() { TKUnit.assertEqual(target1.get("targetProperty"), newExpectedValue); } +export function test_WrongNestedPropertiesBinding() { + var expectedValue = "Default Text"; + var viewModel = new observable.Observable(); + viewModel.set("activity", new Activity(expectedValue, "Default First Name", "Default Last Name")); + let errorMessage; + let traceWriter = { + write: function (message, category, type?) { + errorMessage = message; + } + } + trace.addWriter(traceWriter); + + var target1 = new bindable.Bindable(); + target1.bind({ + sourceProperty: "activity.", + targetProperty: "targetProperty", + twoWay: true + }, viewModel); + + TKUnit.assertNotEqual(errorMessage, undefined); + trace.removeWriter(traceWriter); +} + export function test_NestedPropertiesBindingTwoTargets() { var expectedText = "Default Text"; var expectedFirstName = "Default First Name"; diff --git a/trace/trace.ts b/trace/trace.ts index 9b6b4e3ab..629ca76bf 100644 --- a/trace/trace.ts +++ b/trace/trace.ts @@ -48,6 +48,14 @@ export function addCategories(categories: string) { } export function write(message: any, category: string, type?: number) { + // print error no matter what + var i; + if (type === messageType.error) { + for (i = 0; i < _writers.length; i++) { + _writers[i].write(message, category, type); + } + } + if (!_enabled) { return; } @@ -56,7 +64,7 @@ export function write(message: any, category: string, type?: number) { return; } - var i; + for (i = 0; i < _writers.length; i++) { _writers[i].write(message, category, type); } @@ -113,6 +121,7 @@ export module categories { export var Navigation = "Navigation"; export var Test = "Test"; export var Binding = "Binding"; + export var BindingError = "BindingError"; export var Error = "Error"; export var Animation = "Animation"; export var Transition = "Transition"; diff --git a/ui/core/bindable.ts b/ui/core/bindable.ts index eaefe8e7a..21f9c762f 100644 --- a/ui/core/bindable.ts +++ b/ui/core/bindable.ts @@ -252,22 +252,25 @@ export class Binding { var currentObjectChanged = false; for (i = 0; i < propsArrayLength; i++) { objProp = propsArray[i]; - if (propsArray[i] === bc.bindingValueKey) { + if (objProp === bc.bindingValueKey) { currentObjectChanged = true; } - if (propsArray[i] === bc.parentValueKey || propsArray[i].indexOf(bc.parentsValueKey) === 0) { - var parentView = this.getParentView(this.target.get(), propsArray[i]).view; + if (objProp === bc.parentValueKey || objProp.indexOf(bc.parentsValueKey) === 0) { + var parentView = this.getParentView(this.target.get(), objProp).view; if (parentView) { currentObject = parentView.bindingContext; - } - else { + } else { var targetInstance = this.target.get(); targetInstance.off(viewModule.View.loadedEvent, this.loadedHandlerVisualTreeBinding, this); targetInstance.on(viewModule.View.loadedEvent, this.loadedHandlerVisualTreeBinding, this); } currentObjectChanged = true; } - result.push({ instance: currentObject, property: objProp }); + if (currentObject) { + result.push({ instance: currentObject, property: objProp }); + } else { + break; + } // do not need to dive into last object property getter on binding stage will handle it if (!currentObjectChanged && (i < propsArrayLength - 1)) { currentObject = currentObject ? currentObject[propsArray[i]] : null; @@ -285,7 +288,7 @@ export class Binding { for (i = 0; i < objectsAndPropertiesLength; i++) { var prop = objectsAndProperties[i].property; var currentObject = objectsAndProperties[i].instance; - if (currentObject && !this.propertyChangeListeners[prop] && currentObject instanceof observable.Observable) { + if (!this.propertyChangeListeners[prop] && currentObject instanceof observable.Observable) { weakEvents.addWeakEventListener( currentObject, observable.Observable.propertyChangeEvent, @@ -504,13 +507,13 @@ export class Binding { var sourceOptionsInstance = this.sourceOptions.instance.get(); if (this.sourceOptions.property === bc.bindingValueKey) { value = sourceOptionsInstance; - } - else if (sourceOptionsInstance instanceof observable.Observable) { + } else if ((sourceOptionsInstance instanceof observable.Observable) && (this.sourceOptions.property && this.sourceOptions.property !== "")) { value = sourceOptionsInstance.get(this.sourceOptions.property); - } - else if (sourceOptionsInstance && this.sourceOptions.property && + } else if (sourceOptionsInstance && this.sourceOptions.property && this.sourceOptions.property !== "" && this.sourceOptions.property in sourceOptionsInstance) { value = sourceOptionsInstance[this.sourceOptions.property]; + } else { + trace.write("Property: '" + this.sourceOptions.property + "' is invalid or does not exist. SourceProperty: '" + this.options.sourceProperty + "'", trace.categories.Binding, trace.messageType.error); } } return value; @@ -564,8 +567,7 @@ export class Binding { result = result.parent; indexAsInt--; } - } - else if (types.isString(index)) { + } else if (types.isString(index)) { while (result && result.typeName !== index) { result = result.parent; } @@ -580,11 +582,9 @@ export class Binding { if (objectsAndProperties.length > 0) { var resolvedObj = objectsAndProperties[objectsAndProperties.length - 1].instance; var prop = objectsAndProperties[objectsAndProperties.length - 1].property; - if (resolvedObj) { return { instance: new WeakRef(resolvedObj), property: prop - } } } return null; diff --git a/utils/utils.android.ts b/utils/utils.android.ts index 0b1b46d73..9fc92a3db 100644 --- a/utils/utils.android.ts +++ b/utils/utils.android.ts @@ -266,8 +266,9 @@ export function openUrl(location: string): boolean { context.startActivity(intent); } catch (e) { + ensureTrace(); // We Don't do anything with an error. We just output it - console.error("Error in OpenURL", e); + trace.write("Error in OpenURL", trace.categories.Error, trace.messageType.error); return false; } return true; diff --git a/utils/utils.ios.ts b/utils/utils.ios.ts index 652bdb196..9f82a1aa6 100644 --- a/utils/utils.ios.ts +++ b/utils/utils.ios.ts @@ -4,9 +4,17 @@ import common = require("./utils-common"); import {Color} from "color"; import enums = require("ui/enums"); import * as fsModule from "file-system"; +import * as traceModule from "trace"; global.moduleMerge(common, exports); +var trace: typeof traceModule; +function ensureTrace() { + if (!trace) { + trace = require("trace"); + } +} + function isOrientationLandscape(orientation: number) { return orientation === UIDeviceOrientation.UIDeviceOrientationLandscapeLeft || orientation === UIDeviceOrientation.UIDeviceOrientationLandscapeRight; } @@ -234,7 +242,8 @@ export module ios { return controller.presentPreviewAnimated(true); } catch (e) { - console.error("Error in openFile", e); + ensureTrace(); + trace.write("Error in openFile", trace.categories.Error, trace.messageType.error); } return false; } @@ -252,8 +261,9 @@ export function openUrl(location: string): boolean { } } catch (e) { + ensureTrace(); // We Don't do anything with an error. We just output it - console.error("Error in OpenURL", e); + trace.write("Error in OpenURL", trace.categories.Error, trace.messageType.error); } return false; }