Added application resources (used for binding converters), also some bug

fixes (binding related).
This commit is contained in:
Nedyalko Nikolov
2015-03-30 13:44:37 +03:00
parent dfcc820f2f
commit d952faa3c2
8 changed files with 151 additions and 37 deletions

View File

@ -6,6 +6,8 @@ import styleScope = require("ui/styling/style-scope");
export var cssFile: string = "app.css"
export var resources: any = {};
export var onUncaughtError: (error: definition.NativeScriptError) => void = undefined;
export var onLaunch: (context: any) => any = undefined;

View File

@ -23,6 +23,11 @@ declare module "application" {
*/
export var mainModule: string;
/**
* An application level static resources.
*/
export var resources: any;
/**
* The application level css file name (starting from the application root). Used to set css across all pages.
* Css will be applied for every page and page css will be applied after.

View File

@ -429,6 +429,38 @@ export function test_loadMoreItems_not_raised_when_showing_many_items() {
helper.buildUIAndRunTest(listView, testAction);
}
export function test_usingAppLevelConvertersInListViewItems() {
var listView = new listViewModule.ListView();
var dateConverter = function (value, format) {
var result = format;
var day = value.getDate();
result = result.replace("DD", month < 10 ? "0" + day : day);
var month = value.getMonth() + 1;
result = result.replace("MM", month < 10 ? "0" + month : month);
result = result.replace("YYYY", value.getFullYear());
return result;
};
app.resources["dateConverter"] = dateConverter;
var data = new observableArray.ObservableArray();
data.push({date: new Date()});
function testAction(views: Array<viewModule.View>) {
listView.itemTemplate = "<Label id=\"testLabel\" text=\"{{ date, date | dateConverter('DD.MM.YYYY') }}\" />";
listView.items = data;
TKUnit.wait(ASYNC);
var nativeElementText = getTextFromNativeElementAt(listView, 0);
TKUnit.assertEqual(nativeElementText, dateConverter(new Date(), "DD.MM.YYYY"), "native element");
};
helper.buildUIAndRunTest(listView, testAction);
}
function loadViewWithItemNumber(args: listViewModule.ItemEventData) {
if (!args.view) {
args.view = new labelModule.Label();
@ -436,6 +468,19 @@ function loadViewWithItemNumber(args: listViewModule.ItemEventData) {
(<labelModule.Label>args.view).text = "item " + args.index;
}
function getTextFromNativeElementAt(listView: listViewModule.ListView, index: number): any {
if (listView.android) {
var nativeElement = listView.android.getChildAt(index);
if (nativeElement instanceof android.view.ViewGroup) {
return (<android.widget.TextView>(<any>nativeElement.getChildAt(0))).getText();
}
return (<android.widget.TextView>nativeElement).getText();
}
else if (listView.ios) {
return listView.ios.visibleCells()[index].contentView.subviews[0].text;
}
}
function getNativeViewCount(listView: listViewModule.ListView): number {
if (listView.android) {
return listView.android.getChildCount();

View File

@ -8,6 +8,7 @@ import frameModule = require("ui/frame");
import pageModule = require("ui/page");
import listViewModule = require("ui/list-view");
import buttonModule = require("ui/button");
import observable = require("data/observable");
// <snippet module="ui/tab-view" title="TabView">
// # TabView
@ -38,6 +39,8 @@ import tabViewModule = require("ui/tab-view");
// ```
// </snippet>
var ASYNC = 0.3;
function _createTabView(): tabViewModule.TabView {
// <snippet module="ui/tab-view" title="TabView">
// ## Creating a TabView
@ -367,6 +370,44 @@ export var testWhenNavigatingBackToANonCachedPageContainingATabViewWithAListView
TKUnit.assert(listView !== undefined, "ListView should be created when navigating back to the main page.");
}
export function testBindingIsRefreshedWhenTabViewItemIsUnselectedAndThenSelectedAgain() {
helper.buildUIAndRunTest(_createTabView(), function (views: Array<viewModule.View>) {
var viewModel = new observable.Observable();
viewModel.set("counter", 0);
frameModule.topmost().currentPage.bindingContext = viewModel;
var tabView = <tabViewModule.TabView>views[0];
var items = _createItems(10);
var StackLayout0 = new stackLayoutModule.StackLayout();
var label0 = new labelModule.Label();
label0.text = "Tab 0";
label0.id = "testLabel";
label0.bind({ sourceProperty: "counter", targetProperty: "text", twoWay: true });
StackLayout0.addChild(label0);
var tabEntry0 = {
title: "Tab 0",
view: StackLayout0
};
items.push(tabEntry0);
tabView.items = items;
tabView.selectedIndex = 10;
TKUnit.wait(ASYNC);
tabView.selectedIndex = 0;
TKUnit.wait(ASYNC);
tabView.selectedIndex = 10;
TKUnit.wait(ASYNC);
var expectedValue = 5;
viewModel.set("counter", expectedValue);
var testLabel = <labelModule.Label>(tabView.items[10].view.getViewById("testLabel"))
TKUnit.assertEqual(testLabel.text, expectedValue, "binding is not working!");
});
}
function _clickTheFirstButtonInTheListViewNatively(tabView: tabViewModule.TabView) {
if (tabView.android) {
var androidListView = <android.widget.ListView>tabView.android.getChildAt(0);

View File

@ -14,4 +14,3 @@ declare module "js-libs/polymer-expressions" {
getValue(model, isBackConvert, changedModel);
}
}

View File

@ -402,9 +402,9 @@ var Path = require("js-libs/polymer-expressions/path-parser").Path;
Expression.prototype = {
getValue: function (model, isBackConvert, changedModel, observer) {
var value = getFn(this.expression)(model, observer, changedModel);
var value = getFn(this.expression)(model.context, observer, changedModel);
for (var i = 0; i < this.filters.length; i++) {
value = this.filters[i].transform(model, observer, model, isBackConvert, [value]);
value = this.filters[i].transform(model.context, observer, model.resources, isBackConvert, [value]);
}
return value;

View File

@ -2,6 +2,7 @@
import definition = require("ui/core/bindable");
import dependencyObservable = require("ui/core/dependency-observable");
import weakEventListener = require("ui/core/weak-event-listener");
import appModule = require("application");
import types = require("utils/types");
import trace = require("trace");
import polymerExpressions = require("js-libs/polymer-expressions");
@ -12,6 +13,9 @@ var bindingContextProperty = new dependencyObservable.Property(
new dependencyObservable.PropertyMetadata(undefined, dependencyObservable.PropertyMetadataSettings.Inheritable) // TODO: Metadata options?
);
var contextKey = "context";
var resourcesKey = "resources";
export class Bindable extends dependencyObservable.DependencyObservable implements definition.Bindable {
public static bindingContextProperty = bindingContextProperty;
@ -99,11 +103,6 @@ export class Bindable extends dependencyObservable.DependencyObservable implemen
continue;
}
if (binding.source && binding.source.get() !== oldValue) {
// Binding has its source set directly, not through binding context, do not bind/unbind in this case
continue;
}
trace.write(
"Binding target: " + binding.target.get() +
" targetProperty: " + binding.options.targetProperty +
@ -151,7 +150,6 @@ export class Binding {
obj = new String(obj);
}
/* tslint:enable */
this.source = new WeakRef(obj);
this.updateTarget(this.getSourceProperty());
@ -159,6 +157,7 @@ export class Binding {
this.sourceOptions = this.resolveOptions(this.source, this.options.sourceProperty);
}
if (this.sourceOptions) {
var sourceOptionsInstance = this.sourceOptions.instance.get();
if (sourceOptionsInstance instanceof observable.Observable) {
this.weakEventListenerOptions = {
@ -172,6 +171,7 @@ export class Binding {
this.weakEL.addWeakEventListener(this.weakEventListenerOptions);
}
}
}
public unbind() {
if (!this.source) {
@ -180,13 +180,22 @@ export class Binding {
this.weakEL.removeWeakEventListener(this.weakEventListenerOptions);
this.weakEventListenerOptions = undefined;
if (this.source) {
this.source.clear();
}
if (this.sourceOptions) {
this.sourceOptions.instance.clear();
this.sourceOptions = undefined;
}
if (this.targetOptions) {
this.targetOptions = undefined;
}
}
public updateTwoWay(value: any) {
if (this.updating) {
return;
}
if (this.options.twoWay) {
if (this._isExpression(this.options.expression)) {
var changedModel = {};
@ -220,7 +229,10 @@ export class Binding {
var exp = polymerExpressions.PolymerExpressions.getExpression(expression);
if (exp) {
var context = this.source && this.source.get && this.source.get() || global;
return exp.getValue(context, isBackConvert, changedModel);
var model = {};
model[contextKey] = context;
model[resourcesKey] = appModule.resources;
return exp.getValue(model, isBackConvert, changedModel);
}
return new Error(expression + " is not a valid expression.");
}
@ -261,6 +273,7 @@ export class Binding {
var value;
if (this.sourceOptions) {
var sourceOptionsInstance = this.sourceOptions.instance.get();
if (sourceOptionsInstance instanceof observable.Observable) {
value = sourceOptionsInstance.get(this.sourceOptions.property);
@ -268,6 +281,7 @@ export class Binding {
this.sourceOptions.property in sourceOptionsInstance) {
value = sourceOptionsInstance[this.sourceOptions.property];
}
}
return value;
}
@ -308,11 +322,12 @@ export class Binding {
currentObject = currentObject[properties[i]];
}
if (currentObject !== undefined && currentObject !== null) {
options = {
instance: new WeakRef(currentObject),
property: properties[properties.length - 1]
}
}
} else {
options = {
instance: obj,
@ -324,8 +339,15 @@ export class Binding {
}
private updateOptions(options: { instance: WeakRef<any>; property: any }, value: any) {
var optionsInstance;
if (options && options.instance) {
optionsInstance = options.instance.get();
}
if (!optionsInstance) {
return;
}
this.updating = true;
var optionsInstance = options.instance.get();
try {
if (optionsInstance instanceof observable.Observable) {

View File

@ -155,5 +155,5 @@ function getExports(instance: view.View): any {
parent = parent.parent;
}
return (<any>parent).exports;
return parent ? (<any>parent).exports : undefined;
}