mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-18 22:01:42 +08:00
Merge pull request #214 from NativeScript/repeater
Repeater component added
This commit is contained in:
@ -76,6 +76,9 @@
|
|||||||
<TypeScriptCompile Include="apps\gallery-app\views\list-view.ts">
|
<TypeScriptCompile Include="apps\gallery-app\views\list-view.ts">
|
||||||
<DependentUpon>list-view.xml</DependentUpon>
|
<DependentUpon>list-view.xml</DependentUpon>
|
||||||
</TypeScriptCompile>
|
</TypeScriptCompile>
|
||||||
|
<TypeScriptCompile Include="apps\gallery-app\views\repeater.ts">
|
||||||
|
<DependentUpon>repeater.xml</DependentUpon>
|
||||||
|
</TypeScriptCompile>
|
||||||
<TypeScriptCompile Include="apps\placeholder-demo\app.ts" />
|
<TypeScriptCompile Include="apps\placeholder-demo\app.ts" />
|
||||||
<TypeScriptCompile Include="apps\placeholder-demo\main-page.android.ts">
|
<TypeScriptCompile Include="apps\placeholder-demo\main-page.android.ts">
|
||||||
<DependentUpon>main-page.xml</DependentUpon>
|
<DependentUpon>main-page.xml</DependentUpon>
|
||||||
@ -150,6 +153,7 @@
|
|||||||
</TypeScriptCompile>
|
</TypeScriptCompile>
|
||||||
<TypeScriptCompile Include="apps\tests\ui\bindingContext_testPage1.ts" />
|
<TypeScriptCompile Include="apps\tests\ui\bindingContext_testPage1.ts" />
|
||||||
<TypeScriptCompile Include="apps\tests\ui\bindingContext_testPage2.ts" />
|
<TypeScriptCompile Include="apps\tests\ui\bindingContext_testPage2.ts" />
|
||||||
|
<TypeScriptCompile Include="apps\tests\ui\repeater\repeater-tests.ts" />
|
||||||
<TypeScriptCompile Include="apps\tests\ui\time-picker\time-picker-tests-native.android.ts">
|
<TypeScriptCompile Include="apps\tests\ui\time-picker\time-picker-tests-native.android.ts">
|
||||||
<DependentUpon>time-picker-tests-native.d.ts</DependentUpon>
|
<DependentUpon>time-picker-tests-native.d.ts</DependentUpon>
|
||||||
</TypeScriptCompile>
|
</TypeScriptCompile>
|
||||||
@ -347,6 +351,10 @@
|
|||||||
<TypeScriptCompile Include="trace\trace.ts">
|
<TypeScriptCompile Include="trace\trace.ts">
|
||||||
<DependentUpon>trace.d.ts</DependentUpon>
|
<DependentUpon>trace.d.ts</DependentUpon>
|
||||||
</TypeScriptCompile>
|
</TypeScriptCompile>
|
||||||
|
<TypeScriptCompile Include="ui\repeater\repeater.ts">
|
||||||
|
<DependentUpon>repeater.d.ts</DependentUpon>
|
||||||
|
</TypeScriptCompile>
|
||||||
|
<TypeScriptCompile Include="ui\repeater\repeater.d.ts" />
|
||||||
<TypeScriptCompile Include="ui\builder\binding-builder.ts" />
|
<TypeScriptCompile Include="ui\builder\binding-builder.ts" />
|
||||||
<TypeScriptCompile Include="ui\list-picker\list-picker-common.ts">
|
<TypeScriptCompile Include="ui\list-picker\list-picker-common.ts">
|
||||||
<DependentUpon>list-picker.d.ts</DependentUpon>
|
<DependentUpon>list-picker.d.ts</DependentUpon>
|
||||||
@ -592,6 +600,7 @@
|
|||||||
<Content Include="apps\gallery-app\content\border.xml" />
|
<Content Include="apps\gallery-app\content\border.xml" />
|
||||||
<Content Include="apps\gallery-app\app.css" />
|
<Content Include="apps\gallery-app\app.css" />
|
||||||
<Content Include="apps\gallery-app\views\list-picker.xml" />
|
<Content Include="apps\gallery-app\views\list-picker.xml" />
|
||||||
|
<Content Include="apps\gallery-app\views\repeater.xml" />
|
||||||
<Content Include="apps\gallery-app\views\segmented-bar.xml" />
|
<Content Include="apps\gallery-app\views\segmented-bar.xml" />
|
||||||
<Content Include="apps\gallery-app\views\time-picker.xml" />
|
<Content Include="apps\gallery-app\views\time-picker.xml" />
|
||||||
<Content Include="apps\gallery-app\views\date-picker.xml" />
|
<Content Include="apps\gallery-app\views\date-picker.xml" />
|
||||||
@ -1528,6 +1537,9 @@
|
|||||||
<Content Include="ui\placeholder\package.json">
|
<Content Include="ui\placeholder\package.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="ui\repeater\package.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="build\tslint.json" />
|
<None Include="build\tslint.json" />
|
||||||
@ -1590,7 +1602,7 @@
|
|||||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||||
</WebProjectProperties>
|
</WebProjectProperties>
|
||||||
</FlavorProperties>
|
</FlavorProperties>
|
||||||
<UserProperties ui_2layouts_2wrap-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2grid-layout_2package_1json__JSONSchema="" ui_2layouts_2dock-layout_2package_1json__JSONSchema="" ui_2layouts_2absolute-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2linear-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2web-view_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2content-view_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2gallery-app_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2absolute-layout-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2editable-text-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2scroll-view_2package_1json__JSONSchema="http://json.schemastore.org/package" />
|
<UserProperties ui_2scroll-view_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2editable-text-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2absolute-layout-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2gallery-app_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2content-view_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2web-view_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2linear-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2absolute-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2dock-layout_2package_1json__JSONSchema="" ui_2layouts_2grid-layout_2package_1json__JSONSchema="" ui_2layouts_2wrap-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" />
|
||||||
</VisualStudio>
|
</VisualStudio>
|
||||||
</ProjectExtensions>
|
</ProjectExtensions>
|
||||||
</Project>
|
</Project>
|
@ -32,6 +32,7 @@
|
|||||||
<Button tag="views/image" text="Image" tap="itemTap" />
|
<Button tag="views/image" text="Image" tap="itemTap" />
|
||||||
<Button tag="views/search-bar" text="SearchBar" tap="itemTap" />
|
<Button tag="views/search-bar" text="SearchBar" tap="itemTap" />
|
||||||
<Button tag="views/list-view" text="ListView" tap="itemTap" />
|
<Button tag="views/list-view" text="ListView" tap="itemTap" />
|
||||||
|
<Button tag="views/repeater" text="Repeater" tap="itemTap" />
|
||||||
<Button tag="views/dialogs" text="Dialogs" tap="itemTap" />
|
<Button tag="views/dialogs" text="Dialogs" tap="itemTap" />
|
||||||
<Button tag="views/date-picker" text="DatePicker" tap="itemTap" />
|
<Button tag="views/date-picker" text="DatePicker" tap="itemTap" />
|
||||||
<Button tag="views/time-picker" text="TimePicker" tap="itemTap" />
|
<Button tag="views/time-picker" text="TimePicker" tap="itemTap" />
|
||||||
|
10
apps/gallery-app/views/repeater.ts
Normal file
10
apps/gallery-app/views/repeater.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export function pageLoaded(args) {
|
||||||
|
var page = args.object;
|
||||||
|
|
||||||
|
var itemsArr = [];
|
||||||
|
for (var i = 1; i <= 64; i++) {
|
||||||
|
itemsArr.push({ title: "List item " + i });
|
||||||
|
}
|
||||||
|
|
||||||
|
page.bindingContext = { items: itemsArr };
|
||||||
|
}
|
13
apps/gallery-app/views/repeater.xml
Normal file
13
apps/gallery-app/views/repeater.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<Page loaded="pageLoaded">
|
||||||
|
<ScrollView>
|
||||||
|
<Repeater items="{{ items }}">
|
||||||
|
<Repeater.itemsLayout>
|
||||||
|
<WrapLayout />
|
||||||
|
</Repeater.itemsLayout>
|
||||||
|
<Repeater.itemTemplate>
|
||||||
|
<Label text="{{ title }}" margin="10" />
|
||||||
|
</Repeater.itemTemplate>
|
||||||
|
</Repeater>
|
||||||
|
</ScrollView>
|
||||||
|
</Page>
|
@ -69,6 +69,7 @@ allTests["DATE-PICKER"] = require("./ui/date-picker/date-picker-tests");
|
|||||||
allTests["TIME-PICKER"] = require("./ui/time-picker/time-picker-tests");
|
allTests["TIME-PICKER"] = require("./ui/time-picker/time-picker-tests");
|
||||||
allTests["WEB-VIEW"] = require("./ui/web-view/web-view-tests");
|
allTests["WEB-VIEW"] = require("./ui/web-view/web-view-tests");
|
||||||
allTests["WEAK-EVENTS"] = require("./weak-event-listener-tests");
|
allTests["WEAK-EVENTS"] = require("./weak-event-listener-tests");
|
||||||
|
allTests["REPEATER"] = require("./ui/repeater/repeater-tests");
|
||||||
|
|
||||||
if (!isRunningOnEmulator()) {
|
if (!isRunningOnEmulator()) {
|
||||||
allTests["LOCATION"] = require("./location-tests");
|
allTests["LOCATION"] = require("./location-tests");
|
||||||
|
420
apps/tests/ui/repeater/repeater-tests.ts
Normal file
420
apps/tests/ui/repeater/repeater-tests.ts
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
import TKUnit = require("../../TKUnit");
|
||||||
|
import app = require("application");
|
||||||
|
import helper = require("../helper");
|
||||||
|
import viewModule = require("ui/core/view");
|
||||||
|
import stackLayoutModule = require("ui/layouts/stack-layout");
|
||||||
|
|
||||||
|
// <snippet module="ui/repeater" title="repeater">
|
||||||
|
// # Repeater
|
||||||
|
// Using a Repeater requires the repeater module.
|
||||||
|
// ``` JavaScript
|
||||||
|
import repeaterModule = require("ui/repeater");
|
||||||
|
// ```
|
||||||
|
// Other modules which will be used in the code samples in this article:
|
||||||
|
// ``` JavaScript
|
||||||
|
import observableArray = require("data/observable-array");
|
||||||
|
import labelModule = require("ui/label");
|
||||||
|
// ```
|
||||||
|
|
||||||
|
// ### Binding the Repeater items property to collection in the view-model.
|
||||||
|
//```XML
|
||||||
|
// <Page>
|
||||||
|
// {%raw%}<Repeater items="{{ myItems }}" />{%endraw%}
|
||||||
|
// </Page>
|
||||||
|
//```
|
||||||
|
|
||||||
|
// ### Define the Repeater itemTemplate property.
|
||||||
|
//```XML
|
||||||
|
// <Page>
|
||||||
|
// {%raw%}<Repeater items="{{ myItems }}">
|
||||||
|
// <Repeater.itemTemplate>
|
||||||
|
// <Label text="{{ title || 'Downloading...' }}" textWrap="true" cssClass="title" />
|
||||||
|
// </Repeater.itemTemplate>
|
||||||
|
// </Repeater>{%endraw%}
|
||||||
|
// </Page>
|
||||||
|
//```
|
||||||
|
|
||||||
|
// ### Define the Repeater itemsLayout property. Default is <StackLayout orientation="vertical" />.
|
||||||
|
//```XML
|
||||||
|
// <Page>
|
||||||
|
// {%raw%}<Repeater items="{{ myItems }}">
|
||||||
|
// <Repeater.itemsLayout>
|
||||||
|
// <StackLayout orientation="horizontal" />
|
||||||
|
// </Repeater.itemsLayout>
|
||||||
|
// </Repeater>{%endraw%}
|
||||||
|
// </Page>
|
||||||
|
//```
|
||||||
|
|
||||||
|
// ### Repeater with WrapLayout inside ScrollView.
|
||||||
|
//```XML
|
||||||
|
// <Page>
|
||||||
|
// {%raw%}<ScrollView>
|
||||||
|
// <Repeater items="{{ myItems }}">
|
||||||
|
// <Repeater.itemsLayout>
|
||||||
|
// <WrapLayout />
|
||||||
|
// </Repeater.itemsLayout>
|
||||||
|
// <Repeater.itemTemplate>
|
||||||
|
// <Label text="{{ $value }}" margin="10" />
|
||||||
|
// </Repeater.itemTemplate>
|
||||||
|
// </Repeater>
|
||||||
|
// </ScrollView>{%endraw%}
|
||||||
|
// </Page>
|
||||||
|
//```
|
||||||
|
|
||||||
|
// </snippet>
|
||||||
|
|
||||||
|
var ASYNC = 0.2;
|
||||||
|
var FEW_ITEMS = [0, 1, 2];
|
||||||
|
var MANY_ITEMS = [];
|
||||||
|
for (var i = 0; i < 100; i++) {
|
||||||
|
MANY_ITEMS[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_set_items_to_array_loads_all_items() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
// <snippet module="ui/repeater" title="repeater">
|
||||||
|
// ### Using Repeater with Array
|
||||||
|
// ``` JavaScript
|
||||||
|
var colors = ["red", "green", "blue"];
|
||||||
|
repeater.items = colors;
|
||||||
|
// ```
|
||||||
|
// </snippet>
|
||||||
|
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
|
||||||
|
TKUnit.assert(getChildAtText(repeater, 0) === "red", "Item not created for index 0");
|
||||||
|
TKUnit.assert(getChildAtText(repeater, 1) === "green", "Item not created for index 1");
|
||||||
|
TKUnit.assert(getChildAtText(repeater, 2) === "blue", "Item not created for index 2");
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_set_items_to_array_creates_views() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
repeater.items = FEW_ITEMS;
|
||||||
|
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), FEW_ITEMS.length, "views count.");
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_refresh_after_adding_items_to_array_loads_new_items() {
|
||||||
|
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
var colors = ["red", "green", "blue"];
|
||||||
|
repeater.items = colors;
|
||||||
|
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), colors.length, "views count.");
|
||||||
|
// <snippet module="ui/repeater" title="repeater">
|
||||||
|
// > Note, that changing the array after the repeater is shown will not update the UI.
|
||||||
|
// You can force-update the UI using the refresh() method.
|
||||||
|
// ``` JavaScript
|
||||||
|
colors.push("yellow");
|
||||||
|
//// Manually trigger the update so that the new color is shown.
|
||||||
|
repeater.refresh();
|
||||||
|
// ```
|
||||||
|
// </snippet>
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), colors.length, "views count.");
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_refresh_reloads_all_items() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
var testStarted = false;
|
||||||
|
|
||||||
|
var itemsToBind = <Array<any>>FEW_ITEMS;
|
||||||
|
|
||||||
|
repeater.items = itemsToBind;
|
||||||
|
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
testStarted = true;
|
||||||
|
|
||||||
|
itemsToBind[0] = "red";
|
||||||
|
itemsToBind[1] = "green";
|
||||||
|
itemsToBind[2] = "blue";
|
||||||
|
|
||||||
|
repeater.refresh();
|
||||||
|
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
|
||||||
|
TKUnit.assert(getChildAtText(repeater, 0) === "red", "Item not created for index 0");
|
||||||
|
TKUnit.assert(getChildAtText(repeater, 1) === "green", "Item not created for index 1");
|
||||||
|
TKUnit.assert(getChildAtText(repeater, 2) === "blue", "Item not created for index 2");
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_set_itmes_to_null_clears_items() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
repeater.items = FEW_ITEMS;
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), FEW_ITEMS.length, "views count.");
|
||||||
|
|
||||||
|
repeater.items = null;
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), 0, "views count.");
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_set_itmeLayout_accepted() {
|
||||||
|
// <snippet module="ui/repeater" title="repeater">
|
||||||
|
// ### Using Repeater with different layout.
|
||||||
|
// ``` JavaScript
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
var stackLayout = new stackLayoutModule.StackLayout();
|
||||||
|
stackLayout.orientation = "horizontal";
|
||||||
|
repeater.itemsLayout = stackLayout;
|
||||||
|
// ```
|
||||||
|
// </snippet>
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
|
||||||
|
repeater.items = FEW_ITEMS;
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assert((<stackLayoutModule.StackLayout>repeater.itemsLayout).orientation === "horizontal", "views count.");
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), FEW_ITEMS.length, "views count.");
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_set_itmes_to_undefiend_clears_items() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
repeater.items = FEW_ITEMS;
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), FEW_ITEMS.length, "views count.");
|
||||||
|
|
||||||
|
repeater.items = undefined;
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), 0, "views count.");
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_set_itmes_to_different_source_loads_new_items() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
repeater.items = [1, 2, 3];
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), 3, "views count.");
|
||||||
|
|
||||||
|
repeater.items = ["a", "b", "c", "d"];
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), 4, "views count.");
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_set_items_to_observable_array_loads_all_items() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
// <snippet module="ui/repeater" title="repeater">
|
||||||
|
// ### Using Repeater with ObservableArray
|
||||||
|
// ``` JavaScript
|
||||||
|
var colors = new observableArray.ObservableArray(["red", "green", "blue"]);
|
||||||
|
repeater.items = colors;
|
||||||
|
// ```
|
||||||
|
// </snippet>
|
||||||
|
|
||||||
|
TKUnit.assert(getChildAtText(repeater, 0) === "red", "Item not created for index 0");
|
||||||
|
TKUnit.assert(getChildAtText(repeater, 1) === "green", "Item not created for index 1");
|
||||||
|
TKUnit.assert(getChildAtText(repeater, 2) === "blue", "Item not created for index 2");
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_add_to_observable_array_refreshes_the_Repeater() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
var colors = new observableArray.ObservableArray(["red", "green", "blue"]);
|
||||||
|
repeater.items = colors;
|
||||||
|
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), 3, "getChildrenCount");
|
||||||
|
|
||||||
|
// <snippet module="ui/repeater" title="repeater">
|
||||||
|
// > When using ObservableArray the repeater will be automatically updated when items are added or removed form the array.
|
||||||
|
// ``` JavaScript
|
||||||
|
colors.push("yellow");
|
||||||
|
//// The Repeater will be updated automatically.
|
||||||
|
// ```
|
||||||
|
// </snippet>
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), 4, "getChildrenCount");
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_remove_from_observable_array_refreshes_the_Repeater() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
var data = new observableArray.ObservableArray([1, 2, 3]);
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
repeater.items = data;
|
||||||
|
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), 3, "getChildrenCount");
|
||||||
|
|
||||||
|
data.pop();
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), 2, "getChildrenCount");
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_splice_observable_array_refreshes_the_Repeater() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
var data = new observableArray.ObservableArray(["a", "b", "c"]);
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
repeater.items = data;
|
||||||
|
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), 3, "getChildrenCount");
|
||||||
|
|
||||||
|
// Remove the first 2 elements and add
|
||||||
|
data.splice(0, 2, "d", "e", "f");
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
TKUnit.assertEqual(getChildrenCount(repeater), 4, "getChildrenCount");
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_usingAppLevelConvertersInRepeaterItems() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
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>) {
|
||||||
|
repeater.itemTemplate = "<Label id=\"testLabel\" text=\"{{ date, date | dateConverter('DD.MM.YYYY') }}\" />";
|
||||||
|
repeater.items = data;
|
||||||
|
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
|
||||||
|
TKUnit.assertEqual(getChildAtText(repeater, 0), dateConverter(new Date(), "DD.MM.YYYY"), "element");
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_BindingRepeaterToASimpleArray() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
repeater.itemTemplate = "<Label id=\"testLabel\" text=\"{{ $value }}\" />";
|
||||||
|
repeater.items = [1, 2, 3];
|
||||||
|
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
|
||||||
|
TKUnit.assertEqual(getChildAtText(repeater, 0), "1", "first element text");
|
||||||
|
TKUnit.assertEqual(getChildAtText(repeater, 1), "2", "second element text");
|
||||||
|
TKUnit.assertEqual(getChildAtText(repeater, 2), "3", "third element text");
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_BindingRepeaterToASimpleArrayWithExpression() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
repeater.itemTemplate = "<Label id=\"testLabel\" text=\"{{ $value, $value + ' some static text' }}\" />";
|
||||||
|
repeater.items = [1, 2, 3];
|
||||||
|
|
||||||
|
TKUnit.wait(ASYNC);
|
||||||
|
|
||||||
|
TKUnit.assertEqual(getChildAtText(repeater, 0), "1 some static text", "first element text");
|
||||||
|
TKUnit.assertEqual(getChildAtText(repeater, 1), "2 some static text", "second element text");
|
||||||
|
TKUnit.assertEqual(getChildAtText(repeater, 2), "3 some static text", "third element text");
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
export function test_no_memory_leak_when_items_is_regular_array() {
|
||||||
|
var createFunc = function (): repeaterModule.Repeater {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
repeater.items = FEW_ITEMS;
|
||||||
|
return repeater;
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIWithWeakRefAndInteract(createFunc,(list) => {
|
||||||
|
TKUnit.assert(list.isLoaded, "Repeater should be loaded here");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function test_no_memory_leak_when_items_is_observable_array() {
|
||||||
|
// Keep the reference to the observable array to test the weakEventListener
|
||||||
|
var colors = new observableArray.ObservableArray(["red", "green", "blue"]);
|
||||||
|
|
||||||
|
var createFunc = function (): repeaterModule.Repeater {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
repeater.items = colors;
|
||||||
|
return repeater;
|
||||||
|
};
|
||||||
|
|
||||||
|
helper.buildUIWithWeakRefAndInteract(createFunc,(list) => {
|
||||||
|
TKUnit.assert(list.isLoaded, "Repeater should be loaded here");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
function getChildrenCount(repeater: repeaterModule.Repeater): number {
|
||||||
|
return repeater.itemsLayout.getChildrenCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChildAt(repeater: repeaterModule.Repeater, index: number): viewModule.View {
|
||||||
|
return repeater.itemsLayout.getChildAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChildAtText(repeater: repeaterModule.Repeater, index: number): string {
|
||||||
|
return (<labelModule.Label>getChildAt(repeater, index)).text + "";
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,11 @@ var KNOWNCOLLECTIONS = "knownCollections";
|
|||||||
|
|
||||||
export function parse(value: string, exports: any): view.View {
|
export function parse(value: string, exports: any): view.View {
|
||||||
var viewToReturn: view.View;
|
var viewToReturn: view.View;
|
||||||
|
|
||||||
|
if (exports instanceof view.View) {
|
||||||
|
exports = getExports(exports);
|
||||||
|
}
|
||||||
|
|
||||||
var componentModule = parseInternal(value, exports);
|
var componentModule = parseInternal(value, exports);
|
||||||
|
|
||||||
if (componentModule) {
|
if (componentModule) {
|
||||||
@ -234,3 +239,13 @@ interface ComplexProperty {
|
|||||||
name: string;
|
name: string;
|
||||||
items?: Array<any>;
|
items?: Array<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getExports(instance: view.View): any {
|
||||||
|
var parent = instance.parent;
|
||||||
|
|
||||||
|
while (parent && (<any>parent).exports === undefined) {
|
||||||
|
parent = parent.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent ? (<any>parent).exports : undefined;
|
||||||
|
}
|
@ -105,7 +105,7 @@ export class ListView extends view.View implements definition.ListView {
|
|||||||
var v;
|
var v;
|
||||||
|
|
||||||
if (this.itemTemplate && this.items) {
|
if (this.itemTemplate && this.items) {
|
||||||
v = builder.parse(this.itemTemplate, getExports(this));
|
v = builder.parse(this.itemTemplate, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
@ -146,13 +146,3 @@ export class ListView extends view.View implements definition.ListView {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExports(instance: view.View): any {
|
|
||||||
var parent = instance.parent;
|
|
||||||
|
|
||||||
while (parent && (<any>parent).exports === undefined) {
|
|
||||||
parent = parent.parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent ? (<any>parent).exports : undefined;
|
|
||||||
}
|
|
2
ui/repeater/package.json
Normal file
2
ui/repeater/package.json
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{ "name" : "repeater",
|
||||||
|
"main" : "repeater.js" }
|
49
ui/repeater/repeater.d.ts
vendored
Normal file
49
ui/repeater/repeater.d.ts
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* Contains the Repeater class, which represents a UI Repeater component.
|
||||||
|
*/
|
||||||
|
declare module "ui/repeater" {
|
||||||
|
import view = require("ui/core/view");
|
||||||
|
import dependencyObservable = require("ui/core/dependency-observable");
|
||||||
|
import layoutModule = require("ui/layouts/layout");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a UI Repeater component.
|
||||||
|
*/
|
||||||
|
export class Repeater extends view.View {
|
||||||
|
/**
|
||||||
|
* Represents the observable property backing the items property of each Repeater instance.
|
||||||
|
*/
|
||||||
|
public static itemsProperty: dependencyObservable.Property;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the item template property of each Repeater instance.
|
||||||
|
*/
|
||||||
|
public static itemTemplateProperty: dependencyObservable.Property;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the items layout property of each Repeater instance.
|
||||||
|
*/
|
||||||
|
public static itemsLayoutProperty: dependencyObservable.Property;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets or set the items collection of the Repeater.
|
||||||
|
* The items property can be set to an array or an object defining length and getItem(index) method.
|
||||||
|
*/
|
||||||
|
items: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets or set the item template of the Repeater.
|
||||||
|
*/
|
||||||
|
itemTemplate: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets or set the items layout of the Repeater. Default value is StackLayout with orientation="vertical".
|
||||||
|
*/
|
||||||
|
itemsLayout: layoutModule.Layout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces the Repeater to reload all its items.
|
||||||
|
*/
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
227
ui/repeater/repeater.ts
Normal file
227
ui/repeater/repeater.ts
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
import definition = require("ui/repeater");
|
||||||
|
import proxy = require("ui/core/proxy");
|
||||||
|
import dependencyObservable = require("ui/core/dependency-observable");
|
||||||
|
import viewModule = require("ui/core/view");
|
||||||
|
import observable = require("data/observable");
|
||||||
|
import observableArray = require("data/observable-array");
|
||||||
|
import weakEvents = require("ui/core/weak-event-listener");
|
||||||
|
import types = require("utils/types");
|
||||||
|
import layoutModule = require("ui/layouts/layout");
|
||||||
|
import stackLayoutModule = require("ui/layouts/stack-layout");
|
||||||
|
import builder = require("ui/builder");
|
||||||
|
import utils = require("utils/utils");
|
||||||
|
import platform = require("platform");
|
||||||
|
import labelModule = require("ui/label");
|
||||||
|
|
||||||
|
var ITEMS = "items";
|
||||||
|
var ITEMTEMPLATE = "itemTemplate";
|
||||||
|
var LAYOUT = "layout";
|
||||||
|
var REPEATER = "Repeater";
|
||||||
|
|
||||||
|
export module knownTemplates {
|
||||||
|
export var itemTemplate = "itemTemplate";
|
||||||
|
}
|
||||||
|
|
||||||
|
function onItemsPropertyChanged(data: dependencyObservable.PropertyChangeData) {
|
||||||
|
var repeater = <Repeater>data.object;
|
||||||
|
repeater._onItemsPropertyChanged(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onItemTemplatePropertyChanged(data: dependencyObservable.PropertyChangeData) {
|
||||||
|
var repeater = <definition.Repeater>data.object;
|
||||||
|
repeater.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onItemsLayoutPropertyPropertyChanged(data: dependencyObservable.PropertyChangeData) {
|
||||||
|
var repeater = <definition.Repeater>data.object;
|
||||||
|
repeater.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Repeater extends viewModule.CustomLayoutView implements definition.Repeater {
|
||||||
|
private isDirty: boolean = true;
|
||||||
|
private _ios: UIView;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
if (platform.device.os === platform.platformNames.ios) {
|
||||||
|
this._ios = UIView.new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static itemsProperty = new dependencyObservable.Property(
|
||||||
|
ITEMS,
|
||||||
|
REPEATER,
|
||||||
|
new proxy.PropertyMetadata(
|
||||||
|
undefined,
|
||||||
|
dependencyObservable.PropertyMetadataSettings.AffectsLayout,
|
||||||
|
onItemsPropertyChanged
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
public static itemTemplateProperty = new dependencyObservable.Property(
|
||||||
|
ITEMTEMPLATE,
|
||||||
|
REPEATER,
|
||||||
|
new proxy.PropertyMetadata(
|
||||||
|
undefined,
|
||||||
|
dependencyObservable.PropertyMetadataSettings.AffectsLayout,
|
||||||
|
onItemTemplatePropertyChanged
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
public static itemsLayoutProperty = new dependencyObservable.Property(
|
||||||
|
LAYOUT,
|
||||||
|
REPEATER,
|
||||||
|
new proxy.PropertyMetadata(
|
||||||
|
undefined,
|
||||||
|
dependencyObservable.PropertyMetadataSettings.AffectsLayout,
|
||||||
|
onItemsLayoutPropertyPropertyChanged
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
get items(): any {
|
||||||
|
return this._getValue(Repeater.itemsProperty);
|
||||||
|
}
|
||||||
|
set items(value: any) {
|
||||||
|
this._setValue(Repeater.itemsProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get itemTemplate(): string {
|
||||||
|
return this._getValue(Repeater.itemTemplateProperty);
|
||||||
|
}
|
||||||
|
set itemTemplate(value: string) {
|
||||||
|
this._setValue(Repeater.itemTemplateProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get itemsLayout(): layoutModule.Layout {
|
||||||
|
return this._getValue(Repeater.itemsLayoutProperty);
|
||||||
|
}
|
||||||
|
set itemsLayout(value: layoutModule.Layout) {
|
||||||
|
this._setValue(Repeater.itemsLayoutProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public refresh() {
|
||||||
|
this.isDirty = true;
|
||||||
|
|
||||||
|
this._createChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onLoaded() {
|
||||||
|
super.onLoaded();
|
||||||
|
|
||||||
|
this._createChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onUnloaded() {
|
||||||
|
super.onUnloaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
public _onItemsPropertyChanged(data: dependencyObservable.PropertyChangeData) {
|
||||||
|
if (data.oldValue instanceof observable.Observable) {
|
||||||
|
weakEvents.removeWeakEventListener(data.oldValue, observableArray.ObservableArray.changeEvent, this._onItemsChanged, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.newValue instanceof observable.Observable) {
|
||||||
|
weakEvents.addWeakEventListener(data.newValue, observableArray.ObservableArray.changeEvent, this._onItemsChanged, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types.isUndefined(this.itemsLayout)) {
|
||||||
|
this.itemsLayout = new stackLayoutModule.StackLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.itemsLayout.parent !== this) {
|
||||||
|
this._addView(this.itemsLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onItemsChanged(args: observable.EventData) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createChildren() {
|
||||||
|
if (this.isDirty && this.isLoaded) {
|
||||||
|
clearItemsLayout(this.itemsLayout);
|
||||||
|
|
||||||
|
if (!types.isNullOrUndefined(this.items) && types.isNumber(this.items.length)) {
|
||||||
|
var i: number;
|
||||||
|
for (i = 0; i < this.items.length; i++) {
|
||||||
|
var viewToAdd = !types.isNullOrUndefined(this.itemTemplate) ? builder.parse(this.itemTemplate, this) : this._getDefaultItemContent(i);
|
||||||
|
if (!types.isNullOrUndefined(viewToAdd)) {
|
||||||
|
this.itemsLayout.addChild(viewToAdd);
|
||||||
|
viewToAdd.bindingContext = this._getDataItem(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isDirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public _getDefaultItemContent(index: number): viewModule.View {
|
||||||
|
var lbl = new labelModule.Label();
|
||||||
|
lbl.bind({
|
||||||
|
targetProperty: "text",
|
||||||
|
sourceProperty: "$value"
|
||||||
|
});
|
||||||
|
return lbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getDataItem(index: number): any {
|
||||||
|
return this.items.getItem ? this.items.getItem(index) : this.items[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
get ios(): UIView {
|
||||||
|
return this._ios;
|
||||||
|
}
|
||||||
|
|
||||||
|
get _childrenCount(): number {
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
if (this.itemsLayout) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public _eachChildView(callback: (child: viewModule.View) => boolean) {
|
||||||
|
if (this.itemsLayout) {
|
||||||
|
callback(this.itemsLayout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public onLayout(left: number, top: number, right: number, bottom: number): void {
|
||||||
|
viewModule.View.layoutChild(this, this.itemsLayout, 0, 0, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
|
||||||
|
var result = viewModule.View.measureChild(this, this.itemsLayout, widthMeasureSpec, heightMeasureSpec);
|
||||||
|
|
||||||
|
var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
|
||||||
|
var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
|
||||||
|
|
||||||
|
var height = utils.layout.getMeasureSpecSize(heightMeasureSpec);
|
||||||
|
var heightMode = utils.layout.getMeasureSpecMode(heightMeasureSpec);
|
||||||
|
|
||||||
|
var widthAndState = viewModule.View.resolveSizeAndState(result.measuredWidth, width, widthMode, 0);
|
||||||
|
var heightAndState = viewModule.View.resolveSizeAndState(result.measuredHeight, height, heightMode, 0);
|
||||||
|
|
||||||
|
this.setMeasuredDimension(widthAndState, heightAndState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearItemsLayout(itemsLayout: layoutModule.Layout) {
|
||||||
|
if (!types.isNullOrUndefined(itemsLayout)) {
|
||||||
|
var i: number = itemsLayout.getChildrenCount();
|
||||||
|
if (i > 0) {
|
||||||
|
while (i >= 0) {
|
||||||
|
var child = itemsLayout.getChildAt(i);
|
||||||
|
if (!types.isNullOrUndefined(child)) {
|
||||||
|
itemsLayout.removeChild(child);
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user