Add Template factory function and use it in the Repeater and ListView components

This commit is contained in:
Panayot Cankov
2015-11-20 12:43:12 +02:00
parent 03e69cbf85
commit e95820b04a
13 changed files with 110 additions and 23 deletions

View File

@ -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>

View File

@ -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));
}
}

View File

@ -14,6 +14,8 @@ import stackLayoutModule = require("ui/layouts/stack-layout");
import {Label} from "ui/label";
import {Page} from "ui/page";
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 listViewModule = require("ui/list-view");
import helper = require("../ui/helper");
@ -819,3 +821,18 @@ export function test_searchbar_donotcrash_whentext_isspace() {
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!'");
}

View File

@ -320,6 +320,7 @@
"apps/tests/xml-declaration/mymodule/MyControl.ts",
"apps/tests/xml-declaration/mymodulewithxml/MyControl.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/transforms/app.ts",
"apps/transforms/main-page.ts",

View File

@ -4,7 +4,7 @@
export function load(fileName: string, exports?: any): 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 {
path: string;

View File

@ -21,20 +21,24 @@ function isCurentPlatform(value: string): boolean {
return value && value.toLowerCase() === platform.device.os.toLowerCase();
}
export function parse(value: string, context: any): view.View {
var viewToReturn: view.View;
export function parse(value: string | view.Template, context: any): 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 {
@ -104,6 +108,7 @@ function parseInternal(value: string, context: any): componentBuilder.ComponentM
if (templateBuilderDef.isKnownTemplate(name, parent.exports)) {
templateBuilder = new templateBuilderDef.TemplateBuilder({
context: parent ? getExports(parent.component) : null, // Passing 'context' won't work if you set "codeFile" on the page
parent: parent,
name: name,
elementName: args.elementName,

View File

@ -1,6 +1,7 @@
//@private
declare module "ui/builder/template-builder" {
import xml = require("xml");
import page = require("ui/page");
import componentBuilder = require("ui/builder/component-builder");
class TemplateBuilder {
@ -18,6 +19,7 @@ declare module "ui/builder/template-builder" {
export function isKnownTemplate(name: string, exports: any): boolean;
interface TemplateProperty {
context?: any;
parent: componentBuilder.ComponentModule;
name: string;
elementName: string;

View File

@ -1,14 +1,19 @@
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");
var KNOWNTEMPLATES = "knownTemplates";
export class TemplateBuilder {
private _context: any;
private _items: Array<string>;
private _templateProperty: definition.TemplateProperty;
private _nestingLevel: number;
constructor(templateProperty: definition.TemplateProperty) {
this._context = templateProperty.context;
this._items = new Array<string>();
this._templateProperty = templateProperty;
this._nestingLevel = 0;
@ -56,7 +61,10 @@ export class TemplateBuilder {
private build() {
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
View File

@ -512,6 +512,18 @@ declare module "ui/core/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.
*/

View File

@ -8,6 +8,7 @@ import builder = require("ui/builder");
import label = require("ui/label");
import color = require("color");
import weakEvents = require("ui/core/weak-event-listener");
import types = require("utils/types");
var ITEMS = "items";
var ITEMTEMPLATE = "itemTemplate";
@ -91,10 +92,10 @@ export class ListView extends view.View implements definition.ListView {
this._setValue(ListView.itemsProperty, value);
}
get itemTemplate(): string {
get itemTemplate(): string | view.Template {
return this._getValue(ListView.itemTemplateProperty);
}
set itemTemplate(value: string) {
set itemTemplate(value: string | view.Template) {
this._setValue(ListView.itemTemplateProperty, value);
}

View File

@ -78,7 +78,7 @@ declare module "ui/list-view" {
/**
* 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.

View File

@ -34,7 +34,7 @@ declare module "ui/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".

View File

@ -90,10 +90,10 @@ export class Repeater extends viewModule.CustomLayoutView implements definition.
this._setValue(Repeater.itemsProperty, value);
}
get itemTemplate(): string {
get itemTemplate(): string | viewModule.Template {
return this._getValue(Repeater.itemTemplateProperty);
}
set itemTemplate(value: string) {
set itemTemplate(value: string | viewModule.Template) {
this._setValue(Repeater.itemTemplateProperty, value);
}