mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
ListView item template selector refactoring + sample code
This commit is contained in:
BIN
apps/app/ui-tests-app/list-view/first-image.png
Normal file
BIN
apps/app/ui-tests-app/list-view/first-image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 990 B |
8
apps/app/ui-tests-app/list-view/list-view.css
Normal file
8
apps/app/ui-tests-app/list-view/list-view.css
Normal file
@@ -0,0 +1,8 @@
|
||||
Label, Button {
|
||||
font-size: 8;
|
||||
}
|
||||
|
||||
ListView {
|
||||
border-width: 1;
|
||||
margin: 1;
|
||||
}
|
||||
60
apps/app/ui-tests-app/list-view/list-view.ts
Normal file
60
apps/app/ui-tests-app/list-view/list-view.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { EventData } from 'data/observable';
|
||||
import { ObservableArray } from "data/observable-array";
|
||||
import { View, KeyedTemplate } from "ui/core/view";
|
||||
import { Page } from 'ui/page';
|
||||
import { ViewModel, Item } from './main-view-model';
|
||||
import { ListView } from "ui/list-view";
|
||||
import { Label } from "ui/label";
|
||||
import { GridLayout } from "ui/layouts/grid-layout";
|
||||
import { Color } from "color";
|
||||
|
||||
export function selectItemTemplate(item: Item, index: number, items: ObservableArray<Item>): string {
|
||||
return item.id % 10 === 0 ? "red" : item.id % 2 === 0 ? "green" : "yellow";
|
||||
}
|
||||
|
||||
export function pageLoaded(args: EventData) {
|
||||
let page = <Page>args.object;
|
||||
page.bindingContext = new ViewModel();
|
||||
|
||||
let lv4 = page.getViewById<ListView>("lv4");
|
||||
lv4.itemTemplateSelector = (item: Item, index: number, items: ObservableArray<Item>) => {
|
||||
return index % 10 === 0 ? "red" : index % 2 === 0 ? "green" : "yellow";
|
||||
};
|
||||
|
||||
let createLabel = (backgroundColor: Color) => {
|
||||
let label = new Label();
|
||||
label.bind({
|
||||
sourceProperty: null,
|
||||
targetProperty: "text",
|
||||
expression: "$value"
|
||||
});
|
||||
label.style.backgroundColor = backgroundColor;
|
||||
return label;
|
||||
};
|
||||
|
||||
lv4.itemTemplates = new Array<KeyedTemplate>(
|
||||
{
|
||||
key: "red",
|
||||
createView: () => { return createLabel(new Color("red")); }
|
||||
},
|
||||
{
|
||||
key: "green",
|
||||
createView: () => { return createLabel(new Color("green")); }
|
||||
},
|
||||
{
|
||||
key: "yellow",
|
||||
createView: () => { return createLabel(new Color("yellow")); }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let scrollToBottom = true;
|
||||
export function onScroll(args: EventData){
|
||||
let page = (<View>args.object).page;
|
||||
let gridLayout = page.getViewById<GridLayout>("grid-layout");
|
||||
for (let i = 0, length = gridLayout.getChildrenCount(); i < length; i++){
|
||||
let listView = <ListView>gridLayout.getChildAt(i);
|
||||
listView.scrollToIndex(scrollToBottom ? listView.items.length - 1 : 0);
|
||||
}
|
||||
scrollToBottom = !scrollToBottom;
|
||||
}
|
||||
50
apps/app/ui-tests-app/list-view/list-view.xml
Normal file
50
apps/app/ui-tests-app/list-view/list-view.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<Page loaded="pageLoaded">
|
||||
<StackLayout>
|
||||
<Button text="SCROLL" tap="onScroll" height="30"/>
|
||||
<GridLayout id="grid-layout" columns="*,*" rows="*,*">
|
||||
<ListView id="lv1" col="0" row="0" items="{{ items }}" itemTemplateSelector="selectItemTemplate">
|
||||
<ListView.itemTemplates>
|
||||
<template key="red">
|
||||
<Label text="{{ $value }}" textWrap="true" style.backgroundColor="red"/>
|
||||
</template>
|
||||
<template key="green">
|
||||
<Label text="{{ $value }}" textWrap="true" style.backgroundColor="green"/>
|
||||
</template>
|
||||
<template key="yellow">
|
||||
<Label text="{{ $value }}" textWrap="true" style.backgroundColor="yellow"/>
|
||||
</template>
|
||||
</ListView.itemTemplates>
|
||||
</ListView>
|
||||
<ListView id="lv2" col="1" row="0" items="{{ items }}" itemTemplateSelector="id % 10 === 0 ? 'red' : id % 2 === 0 ? 'green' : 'yellow'">
|
||||
<ListView.itemTemplates>
|
||||
<template key="red">
|
||||
<Label text="{{ $value }}" textWrap="true" style.backgroundColor="red"/>
|
||||
</template>
|
||||
<template key="green">
|
||||
<Label text="{{ $value }}" textWrap="true" style.backgroundColor="green"/>
|
||||
</template>
|
||||
<template key="yellow">
|
||||
<Label text="{{ $value }}" textWrap="true" style.backgroundColor="yellow"/>
|
||||
</template>
|
||||
</ListView.itemTemplates>
|
||||
</ListView>
|
||||
<ListView id="lv3" col="0" row="1" items="{{ items }}" itemTemplateSelector="$index % 10 === 0 ? 'wrong' : $index % 2 === 0 ? 'green' : 'yellow'">
|
||||
<ListView.itemTemplate>
|
||||
<Label text="{{ $value + ' itemTemplate' }}" textWrap="true"/>
|
||||
</ListView.itemTemplate>
|
||||
<ListView.itemTemplates>
|
||||
<template key="red">
|
||||
<Label text="{{ $value }}" textWrap="true" style.backgroundColor="red"/>
|
||||
</template>
|
||||
<template key="green">
|
||||
<Label text="{{ $value }}" textWrap="true" style.backgroundColor="green"/>
|
||||
</template>
|
||||
<template key="yellow">
|
||||
<Label text="{{ $value }}" textWrap="true" style.backgroundColor="yellow"/>
|
||||
</template>
|
||||
</ListView.itemTemplates>
|
||||
</ListView>
|
||||
<ListView id="lv4" col="1" row="1" items="{{ items }}"></ListView>
|
||||
</GridLayout>
|
||||
</StackLayout>
|
||||
</Page>
|
||||
51
apps/app/ui-tests-app/list-view/main-view-model.ts
Normal file
51
apps/app/ui-tests-app/list-view/main-view-model.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Observable } from 'data/observable';
|
||||
import { ObservableArray } from 'data/observable-array';
|
||||
|
||||
export class Item extends Observable {
|
||||
private _name: string;
|
||||
private _id: number;
|
||||
|
||||
constructor(name: string, id: number) {
|
||||
super();
|
||||
this._name = name;
|
||||
this._id = id;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
set name(value: string) {
|
||||
if (this._name !== value) {
|
||||
this._name = value;
|
||||
this.notifyPropertyChange('name', value)
|
||||
}
|
||||
}
|
||||
|
||||
get id(): number {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
set id(value: number) {
|
||||
if (this._id !== value) {
|
||||
this._id = value;
|
||||
this.notifyPropertyChange('id', value)
|
||||
}
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return `${this.name} ${this.id}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewModel extends Observable {
|
||||
private _items: ObservableArray<Item>;
|
||||
|
||||
get items(): ObservableArray<Item> {
|
||||
this._items = new ObservableArray<Item>();
|
||||
for (let i = 0; i < 100; i++){
|
||||
this._items.push(new Item(`Item`, i));
|
||||
}
|
||||
return this._items;
|
||||
}
|
||||
}
|
||||
BIN
apps/app/ui-tests-app/list-view/no-image.png
Normal file
BIN
apps/app/ui-tests-app/list-view/no-image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
@@ -36,6 +36,7 @@ export function pageLoaded(args: EventData) {
|
||||
examples.set("transitions", "transitions/page0");
|
||||
examples.set("segStyle", "segmented-bar/all");
|
||||
examples.set("flexBox", "flexbox/flexbox");
|
||||
examples.set("list-view", "list-view/list-view");
|
||||
|
||||
//examples.set("listview_binding", "pages/listview_binding");
|
||||
//examples.set("textfield", "text-field/text-field");
|
||||
|
||||
@@ -793,10 +793,10 @@ export class ListViewTest extends testModule.UITest<listViewModule.ListView> {
|
||||
let itemTemplateSelectorFunction = <any>listView.itemTemplateSelector;
|
||||
TKUnit.wait(0.1);
|
||||
|
||||
let templateKey0 = itemTemplateSelectorFunction(0, items[0]);
|
||||
let templateKey0 = itemTemplateSelectorFunction(items[0], 0, items);
|
||||
TKUnit.assertEqual(templateKey0, "red", "itemTemplateSelector result for first item");
|
||||
|
||||
let templateKey1 = itemTemplateSelectorFunction(1, items[1]);
|
||||
let templateKey1 = itemTemplateSelectorFunction(items[1], 1, items);
|
||||
TKUnit.assertEqual(templateKey1, "green", "itemTemplateSelector result for second item");
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,8 @@ export function setPropertyValue(instance: View, instanceModule: Object, exports
|
||||
expression: bindOptions[bindingConstants.expression],
|
||||
twoWay: bindOptions[bindingConstants.twoWay]
|
||||
}, bindOptions[bindingConstants.source]);
|
||||
} else if (isEventOrGesture(propertyName, instance)) {
|
||||
}
|
||||
else if (isEventOrGesture(propertyName, instance)) {
|
||||
// Get the event handler from page module exports.
|
||||
var handler = exports && exports[propertyValue];
|
||||
|
||||
@@ -181,8 +182,7 @@ export function setPropertyValue(instance: View, instanceModule: Object, exports
|
||||
instance.on(propertyName, handler);
|
||||
}
|
||||
}
|
||||
//TODO_ITS: This code may break some people if they have an export named like one of their attribue values.
|
||||
else if (isFunction(exports && exports[propertyValue])) {
|
||||
else if (isKnownFunction(propertyName, instance) && isFunction(exports && exports[propertyValue])) {
|
||||
instance[propertyName] = exports[propertyValue];
|
||||
}
|
||||
else {
|
||||
@@ -214,4 +214,12 @@ function isBinding(value: any): boolean {
|
||||
}
|
||||
|
||||
return isBinding;
|
||||
}
|
||||
|
||||
// For example, ListView.itemTemplateSelector
|
||||
let KNOWN_FUNCTIONS = "knownFunctions";
|
||||
function isKnownFunction(name: string, instance: View): boolean {
|
||||
return instance.constructor
|
||||
&& KNOWN_FUNCTIONS in instance.constructor
|
||||
&& instance.constructor[KNOWN_FUNCTIONS].indexOf(name) !== -1;
|
||||
}
|
||||
@@ -79,8 +79,9 @@ export class ListView extends view.View implements definition.ListView {
|
||||
public static itemLoadingEvent = "itemLoading";
|
||||
public static itemTapEvent = "itemTap";
|
||||
public static loadMoreItemsEvent = "loadMoreItems";
|
||||
public static knownFunctions = ["itemTemplateSelector"]; //See component-builder.ts isKnownFunction
|
||||
|
||||
private _itemTemplateSelector: (index: number, item: any) => string;
|
||||
private _itemTemplateSelector: (item: any, index: number, items: any) => string;
|
||||
private _itemTemplateSelectorBindable = new Bindable();
|
||||
public _defaultTemplate: view.KeyedTemplate = {
|
||||
key: "default",
|
||||
@@ -174,25 +175,25 @@ export class ListView extends view.View implements definition.ListView {
|
||||
this._setValue(ListView.itemTemplatesProperty, newValue);
|
||||
}
|
||||
|
||||
get itemTemplateSelector(): string | ((index: number, item: any) => string) {
|
||||
get itemTemplateSelector(): string | ((item: any, index: number, items: any) => string) {
|
||||
return this._itemTemplateSelector;
|
||||
}
|
||||
|
||||
set itemTemplateSelector(value: string | ((index: number, item: any) => string)) {
|
||||
set itemTemplateSelector(value: string | ((item: any, index: number, items: any) => string)) {
|
||||
if (isString(value)){
|
||||
this._itemTemplateSelectorBindable.bind({
|
||||
sourceProperty: null,
|
||||
targetProperty: "templateKey",
|
||||
expression: <string>value
|
||||
});
|
||||
this._itemTemplateSelector = (index: number, item: any) => {
|
||||
this._itemTemplateSelector = (item: any, index: number, items: any) => {
|
||||
item["$index"] = index;
|
||||
this._itemTemplateSelectorBindable.bindingContext = item;
|
||||
return this._itemTemplateSelectorBindable.get("templateKey");
|
||||
};
|
||||
}
|
||||
else if (isFunction(value)) {
|
||||
this._itemTemplateSelector = <((index: number, item: any) => string)>value;
|
||||
this._itemTemplateSelector = <((item: any, index: number, items: any) => string)>value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +231,7 @@ export class ListView extends view.View implements definition.ListView {
|
||||
let templateKey = "default";
|
||||
if (this.itemTemplateSelector){
|
||||
let dataItem = this._getDataItem(index);
|
||||
templateKey = this._itemTemplateSelector(index, dataItem);
|
||||
templateKey = this._itemTemplateSelector(dataItem, index, this.items);
|
||||
}
|
||||
|
||||
for (let i = 0, length = this._itemTemplatesInternal.length; i < length; i++) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import definition = require("ui/list-view");
|
||||
import {ProxyViewContainer} from "ui/proxy-view-container";
|
||||
import * as layoutBase from "ui/layouts/layout-base";
|
||||
import * as colorModule from "color";
|
||||
import * as traceModule from "trace";
|
||||
|
||||
let color: typeof colorModule;
|
||||
function ensureColor() {
|
||||
|
||||
2
tns-core-modules/ui/list-view/list-view.d.ts
vendored
2
tns-core-modules/ui/list-view/list-view.d.ts
vendored
@@ -91,7 +91,7 @@ declare module "ui/list-view" {
|
||||
* A function that returns the appropriate ket template based on the data item.
|
||||
*/
|
||||
|
||||
itemTemplateSelector: string | ((index: number, item: any) => string);
|
||||
itemTemplateSelector: string | ((item: any, index: number, items: any) => string);
|
||||
/**
|
||||
* Gets or set the items separator line color of the ListView.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user