mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-17 04:41:36 +08:00
Merge pull request #1136 from NativeScript/cankov/template-factory-function
Add Template factory function and use it in the ui/builder, Repeater and ListView components
This commit is contained in:
@ -8,6 +8,7 @@ import observable = require("data/observable");
|
|||||||
import types = require("utils/types");
|
import types = require("utils/types");
|
||||||
import platform = require("platform");
|
import platform = require("platform");
|
||||||
import utils = require("utils/utils");
|
import utils = require("utils/utils");
|
||||||
|
import { Label } from "ui/label";
|
||||||
|
|
||||||
// <snippet module="ui/list-view" title="list-view">
|
// <snippet module="ui/list-view" title="list-view">
|
||||||
// # ListView
|
// # ListView
|
||||||
@ -481,6 +482,28 @@ export class ListViewTest extends testModule.UITest<listViewModule.ListView> {
|
|||||||
TKUnit.assertEqual(secondNativeElementText, "2", "second element text");
|
TKUnit.assertEqual(secondNativeElementText, "2", "second element text");
|
||||||
TKUnit.assertEqual(thirdNativeElementText, "3", "third element text");
|
TKUnit.assertEqual(thirdNativeElementText, "3", "third element text");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public test_ItemTemplateFactoryFunction() {
|
||||||
|
var listView = this.testView;
|
||||||
|
|
||||||
|
listView.itemTemplate = () => {
|
||||||
|
var label = new Label();
|
||||||
|
label.id = "testLabel";
|
||||||
|
label.bind({ sourceProperty: "$value", targetProperty: "text", twoWay: false });
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
listView.items = [1, 2, 3];
|
||||||
|
|
||||||
|
TKUnit.waitUntilReady(() => { return this.getNativeViewCount(listView) === listView.items.length; }, ASYNC);
|
||||||
|
|
||||||
|
var firstNativeElementText = this.getTextFromNativeElementAt(listView, 0);
|
||||||
|
var secondNativeElementText = this.getTextFromNativeElementAt(listView, 1);
|
||||||
|
var thirdNativeElementText = this.getTextFromNativeElementAt(listView, 2);
|
||||||
|
|
||||||
|
TKUnit.assertEqual(firstNativeElementText, "1", "first element text");
|
||||||
|
TKUnit.assertEqual(secondNativeElementText, "2", "second element text");
|
||||||
|
TKUnit.assertEqual(thirdNativeElementText, "3", "third element text");
|
||||||
|
}
|
||||||
|
|
||||||
public test_BindingListViewToASimpleArrayWithExpression() {
|
public test_BindingListViewToASimpleArrayWithExpression() {
|
||||||
var listView = this.testView;
|
var listView = this.testView;
|
||||||
|
@ -8,6 +8,7 @@ import layoutBaseModule = require("ui/layouts/layout-base");
|
|||||||
import fs = require("file-system");
|
import fs = require("file-system");
|
||||||
import pageModule = require("ui/page");
|
import pageModule = require("ui/page");
|
||||||
import gestureModule = require("ui/gestures");
|
import gestureModule = require("ui/gestures");
|
||||||
|
import { Label } from "ui/label";
|
||||||
|
|
||||||
// <snippet module="ui/repeater" title="repeater">
|
// <snippet module="ui/repeater" title="repeater">
|
||||||
// # Repeater
|
// # Repeater
|
||||||
@ -367,6 +368,28 @@ export function test_BindingRepeaterToASimpleArray() {
|
|||||||
helper.buildUIAndRunTest(repeater, testAction);
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function test_ItemTemplateFactoryFunction() {
|
||||||
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
function testAction(views: Array<viewModule.View>) {
|
||||||
|
repeater.itemTemplate = () => {
|
||||||
|
var label = new Label();
|
||||||
|
label.id = "testLabel";
|
||||||
|
label.bind({ sourceProperty: "$value", targetProperty: "text", twoWay: false });
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
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() {
|
export function test_BindingRepeaterToASimpleArrayWithExpression() {
|
||||||
var repeater = new repeaterModule.Repeater();
|
var repeater = new repeaterModule.Repeater();
|
||||||
|
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
|
||||||
|
xmlns:tc="xml-declaration/template-builder-tests/template-view">
|
||||||
|
<tc:TemplateView id="template-view">
|
||||||
|
<tc:TemplateView.template>
|
||||||
|
<Button text="Click!" />
|
||||||
|
</tc:TemplateView.template>
|
||||||
|
</tc:TemplateView>
|
||||||
|
</Page>
|
@ -0,0 +1,33 @@
|
|||||||
|
import { View, Template } from "ui/core/view"
|
||||||
|
import { PropertyChangeData, Property, PropertyMetadataSettings } from "ui/core/dependency-observable"
|
||||||
|
import * as proxy from "ui/core/proxy"
|
||||||
|
import { LayoutBase } from "ui/layouts/layout-base"
|
||||||
|
import { parse } from "ui/builder"
|
||||||
|
|
||||||
|
export module knownTemplates {
|
||||||
|
export var template = "template";
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TemplateView extends LayoutBase {
|
||||||
|
public static templateProperty = new Property(
|
||||||
|
"template",
|
||||||
|
"TemplateView",
|
||||||
|
new proxy.PropertyMetadata(
|
||||||
|
undefined,
|
||||||
|
PropertyMetadataSettings.AffectsLayout,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
get template(): string | Template {
|
||||||
|
return this._getValue(TemplateView.templateProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
set template(value: string | Template) {
|
||||||
|
this._setValue(TemplateView.templateProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public parseTemplate() {
|
||||||
|
this.addChild(parse(this.template));
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,8 @@ import stackLayoutModule = require("ui/layouts/stack-layout");
|
|||||||
import {Label} from "ui/label";
|
import {Label} from "ui/label";
|
||||||
import {Page} from "ui/page";
|
import {Page} from "ui/page";
|
||||||
import {Button} from "ui/button";
|
import {Button} from "ui/button";
|
||||||
|
import {View} from "ui/core/view";
|
||||||
|
import {TemplateView} from "./template-builder-tests/template-view";
|
||||||
import myCustomControlWithoutXml = require("./mymodule/MyControl");
|
import myCustomControlWithoutXml = require("./mymodule/MyControl");
|
||||||
import listViewModule = require("ui/list-view");
|
import listViewModule = require("ui/list-view");
|
||||||
import helper = require("../ui/helper");
|
import helper = require("../ui/helper");
|
||||||
@ -818,4 +820,19 @@ export function test_searchbar_donotcrash_whentext_isspace() {
|
|||||||
var sb = <searchBarModule.SearchBar>p.content;
|
var sb = <searchBarModule.SearchBar>p.content;
|
||||||
|
|
||||||
TKUnit.assertEqual(sb.text, " ");
|
TKUnit.assertEqual(sb.text, " ");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function test_parse_template_property() {
|
||||||
|
var page = <Page>builder.load(fs.path.join(__dirname, "template-builder-tests/simple-template-test.xml"));
|
||||||
|
TKUnit.assert(page, "Expected root page.");
|
||||||
|
var templateView = <TemplateView>page.getViewById("template-view");
|
||||||
|
TKUnit.assert(templateView, "Expected TemplateView.");
|
||||||
|
TKUnit.assert(templateView.template, "Expected the template of the TemplateView to be defined");
|
||||||
|
|
||||||
|
TKUnit.assertEqual(templateView.getChildrenCount(), 0, "Expected TemplateView initially to have no children.");
|
||||||
|
templateView.parseTemplate();
|
||||||
|
TKUnit.assertEqual(templateView.getChildrenCount(), 1, "Expected TemplateView initially to have 1 child.");
|
||||||
|
var button = <Button>templateView.getChildAt(0);
|
||||||
|
TKUnit.assert(button, "Expected the TemplateView's template to create a button child.");
|
||||||
|
TKUnit.assertEqual(button.text, "Click!", "Expected child Button to have text 'Click!'");
|
||||||
|
}
|
@ -320,6 +320,7 @@
|
|||||||
"apps/tests/xml-declaration/mymodule/MyControl.ts",
|
"apps/tests/xml-declaration/mymodule/MyControl.ts",
|
||||||
"apps/tests/xml-declaration/mymodulewithxml/MyControl.ts",
|
"apps/tests/xml-declaration/mymodulewithxml/MyControl.ts",
|
||||||
"apps/tests/xml-declaration/xml-declaration-tests.ts",
|
"apps/tests/xml-declaration/xml-declaration-tests.ts",
|
||||||
|
"apps/tests/xml-declaration/template-builder-tests/template-view.ts",
|
||||||
"apps/tests/xml-parser-tests/xml-parser-tests.ts",
|
"apps/tests/xml-parser-tests/xml-parser-tests.ts",
|
||||||
"apps/transforms/app.ts",
|
"apps/transforms/app.ts",
|
||||||
"apps/transforms/main-page.ts",
|
"apps/transforms/main-page.ts",
|
||||||
|
2
ui/builder/builder.d.ts
vendored
2
ui/builder/builder.d.ts
vendored
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
export function load(fileName: string, exports?: any): view.View;
|
export function load(fileName: string, exports?: any): view.View;
|
||||||
export function load(options: LoadOptions): view.View;
|
export function load(options: LoadOptions): view.View;
|
||||||
export function parse(value: string, exports?: any): view.View;
|
export function parse(value: string | view.Template, exports?: any): view.View;
|
||||||
|
|
||||||
export interface LoadOptions {
|
export interface LoadOptions {
|
||||||
path: string;
|
path: string;
|
||||||
|
@ -21,20 +21,24 @@ function isCurentPlatform(value: string): boolean {
|
|||||||
return value && value.toLowerCase() === platform.device.os.toLowerCase();
|
return value && value.toLowerCase() === platform.device.os.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parse(value: string, context: any): view.View {
|
export function parse(value: string | view.Template, context: any): view.View {
|
||||||
var viewToReturn: view.View;
|
if (types.isString(value)) {
|
||||||
|
var viewToReturn: view.View;
|
||||||
if (context instanceof view.View) {
|
|
||||||
context = getExports(context);
|
if (context instanceof view.View) {
|
||||||
|
context = getExports(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
var componentModule = parseInternal(<string>value, context);
|
||||||
|
|
||||||
|
if (componentModule) {
|
||||||
|
viewToReturn = componentModule.component;
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewToReturn;
|
||||||
|
} else if (types.isFunction(value)) {
|
||||||
|
return (<view.Template>value)();
|
||||||
}
|
}
|
||||||
|
|
||||||
var componentModule = parseInternal(value, context);
|
|
||||||
|
|
||||||
if (componentModule) {
|
|
||||||
viewToReturn = componentModule.component;
|
|
||||||
}
|
|
||||||
|
|
||||||
return viewToReturn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseInternal(value: string, context: any): componentBuilder.ComponentModule {
|
function parseInternal(value: string, context: any): componentBuilder.ComponentModule {
|
||||||
@ -104,6 +108,7 @@ function parseInternal(value: string, context: any): componentBuilder.ComponentM
|
|||||||
|
|
||||||
if (templateBuilderDef.isKnownTemplate(name, parent.exports)) {
|
if (templateBuilderDef.isKnownTemplate(name, parent.exports)) {
|
||||||
templateBuilder = new templateBuilderDef.TemplateBuilder({
|
templateBuilder = new templateBuilderDef.TemplateBuilder({
|
||||||
|
context: parent ? getExports(parent.component) : null, // Passing 'context' won't work if you set "codeFile" on the page
|
||||||
parent: parent,
|
parent: parent,
|
||||||
name: name,
|
name: name,
|
||||||
elementName: args.elementName,
|
elementName: args.elementName,
|
||||||
|
2
ui/builder/template-builder.d.ts
vendored
2
ui/builder/template-builder.d.ts
vendored
@ -1,6 +1,7 @@
|
|||||||
//@private
|
//@private
|
||||||
declare module "ui/builder/template-builder" {
|
declare module "ui/builder/template-builder" {
|
||||||
import xml = require("xml");
|
import xml = require("xml");
|
||||||
|
import page = require("ui/page");
|
||||||
import componentBuilder = require("ui/builder/component-builder");
|
import componentBuilder = require("ui/builder/component-builder");
|
||||||
|
|
||||||
class TemplateBuilder {
|
class TemplateBuilder {
|
||||||
@ -18,6 +19,7 @@ declare module "ui/builder/template-builder" {
|
|||||||
export function isKnownTemplate(name: string, exports: any): boolean;
|
export function isKnownTemplate(name: string, exports: any): boolean;
|
||||||
|
|
||||||
interface TemplateProperty {
|
interface TemplateProperty {
|
||||||
|
context?: any;
|
||||||
parent: componentBuilder.ComponentModule;
|
parent: componentBuilder.ComponentModule;
|
||||||
name: string;
|
name: string;
|
||||||
elementName: string;
|
elementName: string;
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import definition = require("ui/builder/template-builder");
|
import definition = require("ui/builder/template-builder");
|
||||||
|
import builder = require("ui/builder");
|
||||||
|
import view = require("ui/core/view");
|
||||||
|
import page = require("ui/page");
|
||||||
import xml = require("xml");
|
import xml = require("xml");
|
||||||
|
|
||||||
var KNOWNTEMPLATES = "knownTemplates";
|
var KNOWNTEMPLATES = "knownTemplates";
|
||||||
|
|
||||||
export class TemplateBuilder {
|
export class TemplateBuilder {
|
||||||
|
private _context: any;
|
||||||
private _items: Array<string>;
|
private _items: Array<string>;
|
||||||
private _templateProperty: definition.TemplateProperty;
|
private _templateProperty: definition.TemplateProperty;
|
||||||
private _nestingLevel: number;
|
private _nestingLevel: number;
|
||||||
|
|
||||||
constructor(templateProperty: definition.TemplateProperty) {
|
constructor(templateProperty: definition.TemplateProperty) {
|
||||||
|
this._context = templateProperty.context;
|
||||||
this._items = new Array<string>();
|
this._items = new Array<string>();
|
||||||
this._templateProperty = templateProperty;
|
this._templateProperty = templateProperty;
|
||||||
this._nestingLevel = 0;
|
this._nestingLevel = 0;
|
||||||
@ -56,7 +61,10 @@ export class TemplateBuilder {
|
|||||||
|
|
||||||
private build() {
|
private build() {
|
||||||
if (this._templateProperty.name in this._templateProperty.parent.component) {
|
if (this._templateProperty.name in this._templateProperty.parent.component) {
|
||||||
this._templateProperty.parent.component[this._templateProperty.name] = this._items.join("");
|
var xml = this._items.join("");
|
||||||
|
var context = this._context;
|
||||||
|
var template: view.Template = () => builder.parse(xml, context);
|
||||||
|
this._templateProperty.parent.component[this._templateProperty.name] = template;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
ui/core/view.d.ts
vendored
12
ui/core/view.d.ts
vendored
@ -511,6 +511,18 @@ declare module "ui/core/view" {
|
|||||||
*/
|
*/
|
||||||
export class CustomLayoutView extends View {
|
export class CustomLayoutView extends View {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines an interface for a View factory function.
|
||||||
|
* Commonly used to specify the visualization of data objects.
|
||||||
|
*/
|
||||||
|
interface Template {
|
||||||
|
/**
|
||||||
|
* Call signature of the factory function.
|
||||||
|
* Returns a new View instance.
|
||||||
|
*/
|
||||||
|
(): View;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines an interface for adding arrays declared in xml.
|
* Defines an interface for adding arrays declared in xml.
|
||||||
|
@ -8,6 +8,7 @@ import builder = require("ui/builder");
|
|||||||
import label = require("ui/label");
|
import label = require("ui/label");
|
||||||
import color = require("color");
|
import color = require("color");
|
||||||
import weakEvents = require("ui/core/weak-event-listener");
|
import weakEvents = require("ui/core/weak-event-listener");
|
||||||
|
import types = require("utils/types");
|
||||||
|
|
||||||
var ITEMS = "items";
|
var ITEMS = "items";
|
||||||
var ITEMTEMPLATE = "itemTemplate";
|
var ITEMTEMPLATE = "itemTemplate";
|
||||||
@ -91,10 +92,10 @@ export class ListView extends view.View implements definition.ListView {
|
|||||||
this._setValue(ListView.itemsProperty, value);
|
this._setValue(ListView.itemsProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
get itemTemplate(): string {
|
get itemTemplate(): string | view.Template {
|
||||||
return this._getValue(ListView.itemTemplateProperty);
|
return this._getValue(ListView.itemTemplateProperty);
|
||||||
}
|
}
|
||||||
set itemTemplate(value: string) {
|
set itemTemplate(value: string | view.Template) {
|
||||||
this._setValue(ListView.itemTemplateProperty, value);
|
this._setValue(ListView.itemTemplateProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
ui/list-view/list-view.d.ts
vendored
2
ui/list-view/list-view.d.ts
vendored
@ -78,7 +78,7 @@ declare module "ui/list-view" {
|
|||||||
/**
|
/**
|
||||||
* Gets or set the item template of the ListView.
|
* Gets or set the item template of the ListView.
|
||||||
*/
|
*/
|
||||||
itemTemplate: string;
|
itemTemplate: string | view.Template;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets or set the items separator line color of the ListView.
|
* Gets or set the items separator line color of the ListView.
|
||||||
|
4
ui/repeater/repeater.d.ts
vendored
4
ui/repeater/repeater.d.ts
vendored
@ -32,9 +32,9 @@ declare module "ui/repeater" {
|
|||||||
items: any;
|
items: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets or set the item template of the Repeater.
|
* Gets or set the item template of the Repeater.
|
||||||
*/
|
*/
|
||||||
itemTemplate: string;
|
itemTemplate: string | view.Template;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets or set the items layout of the Repeater. Default value is StackLayout with orientation="vertical".
|
* Gets or set the items layout of the Repeater. Default value is StackLayout with orientation="vertical".
|
||||||
|
@ -90,10 +90,10 @@ export class Repeater extends viewModule.CustomLayoutView implements definition.
|
|||||||
this._setValue(Repeater.itemsProperty, value);
|
this._setValue(Repeater.itemsProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
get itemTemplate(): string {
|
get itemTemplate(): string | viewModule.Template {
|
||||||
return this._getValue(Repeater.itemTemplateProperty);
|
return this._getValue(Repeater.itemTemplateProperty);
|
||||||
}
|
}
|
||||||
set itemTemplate(value: string) {
|
set itemTemplate(value: string | viewModule.Template) {
|
||||||
this._setValue(Repeater.itemTemplateProperty, value);
|
this._setValue(Repeater.itemTemplateProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user