From 3992729e016989892312f6d5a4795e777fe6716f Mon Sep 17 00:00:00 2001 From: vakrilov Date: Fri, 8 Jan 2016 18:07:06 +0200 Subject: [PATCH] List View related directives --- ng-sample/app/app.css | 8 ++ ng-sample/app/list-test.ts | 75 ++++++++++++++++ ng-sample/app/main-page.ts | 6 +- ng-sample/references.d.ts | 1 + ng-sample/tsconfig.json | 26 +----- src/nativescript-angular/application.ts | 2 + .../directives/list-view.ts | 90 +++++++++++++++++++ .../directives/ns-directives.ts | 4 + src/nativescript-angular/view_node.ts | 4 +- 9 files changed, 190 insertions(+), 26 deletions(-) create mode 100644 ng-sample/app/list-test.ts create mode 100644 ng-sample/references.d.ts create mode 100644 src/nativescript-angular/directives/list-view.ts create mode 100644 src/nativescript-angular/directives/ns-directives.ts diff --git a/ng-sample/app/app.css b/ng-sample/app/app.css index 17e1cb37f..100bc6a6f 100644 --- a/ng-sample/app/app.css +++ b/ng-sample/app/app.css @@ -17,3 +17,11 @@ button { font-size: 42; horizontal-align: center; } + +.odd { + background-color: hotpink +} + +.even { + background-color: lightseagreen; +} \ No newline at end of file diff --git a/ng-sample/app/list-test.ts b/ng-sample/app/list-test.ts new file mode 100644 index 000000000..c7824c411 --- /dev/null +++ b/ng-sample/app/list-test.ts @@ -0,0 +1,75 @@ +import {Component, Input} from 'angular2/core'; + +class DataItem { + constructor(public id: number, public name: string) { } +} + +@Component({ + selector: 'item-component', + template: ` + + + + + ` +}) +export class ItemComponent { + @Input() data: DataItem; + @Input() odd: boolean; + @Input() even: boolean; + constructor() { } +} + +@Component({ + selector: 'list-test', + directives: [ItemComponent], + template: ` + + + + + + + + + + + + + + + + ` + // TEMPLATE WITH COMPONENT + // + + // IN-PLACE TEMPLATE + // +}) +export class ListTest { + public myItems: Array; + + constructor() { + this.myItems = []; + for (var i = 0; i < 30; i++) { + this.myItems.push(new DataItem(i, "data item " + i)); + } + } + + public onItemTap(args) { + console.log("------------------------ ItemTapped: " + args.index); + } +} + diff --git a/ng-sample/app/main-page.ts b/ng-sample/app/main-page.ts index 892f5eb06..545c1ce19 100644 --- a/ng-sample/app/main-page.ts +++ b/ng-sample/app/main-page.ts @@ -4,8 +4,9 @@ import {TextView} from 'ui/text-view'; import {Page} from 'ui/page'; import {nativeScriptBootstrap} from './nativescript-angular/application'; -import {RendererTest} from './renderer-test'; +// import {RendererTest} from './renderer-test'; //import {Benchmark} from './benchmark'; +import {ListTest} from './list-test'; export function createPage() { var page = new Page(); @@ -17,7 +18,8 @@ export function createPage() { profiling.start('ng-bootstrap'); console.log('BOOTSTRAPPING...'); //nativeScriptBootstrap(Benchmark, []).then((appRef) => { - nativeScriptBootstrap(RendererTest, []).then((appRef) => { + // nativeScriptBootstrap(RendererTest, []).then((appRef) => { + nativeScriptBootstrap(ListTest, []).then((appRef) => { profiling.stop('ng-bootstrap'); console.log('ANGULAR BOOTSTRAP DONE.'); }, (err) =>{ diff --git a/ng-sample/references.d.ts b/ng-sample/references.d.ts new file mode 100644 index 000000000..b14f3837d --- /dev/null +++ b/ng-sample/references.d.ts @@ -0,0 +1 @@ +/// Needed for autocompletion and compilation. \ No newline at end of file diff --git a/ng-sample/tsconfig.json b/ng-sample/tsconfig.json index f3b8d24ad..261a73583 100644 --- a/ng-sample/tsconfig.json +++ b/ng-sample/tsconfig.json @@ -10,30 +10,12 @@ "emitDecoratorMetadata": true, "noEmitOnError": true }, - "files": [ - "app/app.ts", - "app/benchmark.ts", - "app/global.d.ts", - "app/main-page.ts", - "app/main-view-model.ts", - "app/nativescript-angular/application.d.ts", - "app/nativescript-angular/application.ts", - "app/nativescript-angular/dom_adapter.ts", - "app/nativescript-angular/element-registry.d.ts", - "app/nativescript-angular/element-registry.ts", - "app/nativescript-angular/polyfills/array.ts", - "app/nativescript-angular/renderer.ts", - "app/nativescript-angular/view_node.ts", - "app/nativescript-angular/xhr.ts", - "app/nativescript-angular/zone.ts", - "app/nativescript-angular/zone_patch.ts", - "app/profiling.ts", - "app/renderer-test.ts", - "app/starter.ts", - "node_modules/tns-core-modules/tns-core-modules.d.ts" - ], "filesGlob": [ "node_modules/tns-core-modules/tns-core-modules.d.ts", "app/**/*.ts" + ], + "exclude": [ + "node_modules", + "platforms" ] } \ No newline at end of file diff --git a/src/nativescript-angular/application.ts b/src/nativescript-angular/application.ts index 293a566d0..73281b687 100644 --- a/src/nativescript-angular/application.ts +++ b/src/nativescript-angular/application.ts @@ -24,6 +24,7 @@ import {APPLICATION_COMMON_PROVIDERS} from 'angular2/src/core/application_common import {COMPILER_PROVIDERS} from 'angular2/src/compiler/compiler'; import {PLATFORM_COMMON_PROVIDERS} from 'angular2/src/core/platform_common_providers'; import {COMMON_DIRECTIVES, COMMON_PIPES, FORM_PROVIDERS} from "angular2/common"; +import {NS_DIRECTIVES} from './directives/ns-directives'; import {bootstrap as angularBootstrap} from 'angular2/bootstrap'; @@ -41,6 +42,7 @@ export function nativeScriptBootstrap(appComponentType: any, provide(PLATFORM_PIPES, {useValue: COMMON_PIPES, multi: true}), provide(PLATFORM_DIRECTIVES, {useValue: COMMON_DIRECTIVES, multi: true}), + provide(PLATFORM_DIRECTIVES, {useValue: NS_DIRECTIVES, multi: true}), APPLICATION_COMMON_PROVIDERS, COMPILER_PROVIDERS, diff --git a/src/nativescript-angular/directives/list-view.ts b/src/nativescript-angular/directives/list-view.ts new file mode 100644 index 000000000..7beaf2984 --- /dev/null +++ b/src/nativescript-angular/directives/list-view.ts @@ -0,0 +1,90 @@ +import {HostListener, Host, Directive, Component, ContentChild, ViewRef, TemplateRef, ViewContainerRef} from 'angular2/core'; +import {View} from 'ui'; +import {ViewNode, DummyViewNode} from '../view_node'; + +const NG_VIEW = "_ngViewRef"; + +@Directive({ + selector: 'ListView', +}) +export class ListViewDirective { + private itemTemplate: ListItemTemplate; + public registerItemTemplate(container: ListItemTemplate) { + this.itemTemplate = container; + } + + @HostListener("itemLoading", ['$event']) + public onItemLoading(args) { + if (!this.itemTemplate) { + return; + } + + let index = args.index; + let items = args.object.items; + let currentItem = typeof (items.getItem) === "function" ? items.getItem(index) : items[index]; + let viewRef: ViewRef; + + if (args.view) { + console.log("ListView.onItemLoading: " + index + " - Reusing exisiting view"); + viewRef = args.view[NG_VIEW]; + } + else { + console.log("ListView.onItemLoading: " + index + " - Creating view from template"); + viewRef = this.itemTemplate.instantiateTemplate(); + args.view = getSingleViewFromViewRef(viewRef); + args.view[NG_VIEW] = viewRef; + } + this.setupViewRef(viewRef, currentItem, index); + } + + public setupViewRef(viewRef: ViewRef, data: any, index: number): void { + viewRef.setLocal('\$implicit', data.item); + viewRef.setLocal("item", data); + viewRef.setLocal("index", index); + viewRef.setLocal('even', (index % 2 == 0)); + viewRef.setLocal('odd', (index % 2 == 1)); + } +} + +@Component({ + selector: 'item-template', + template: ``, +}) +export class ListItemTemplate { + @ContentChild(TemplateRef) template: TemplateRef; + + constructor( + @Host() listDirective: ListViewDirective, + private _viewContainer: ViewContainerRef) { + + listDirective.registerItemTemplate(this); + } + + public instantiateTemplate(): ViewRef { + return this._viewContainer.createEmbeddedView(this.template); + } +} + +function getSingleViewFromViewRef(viewRef: ViewRef): View { + // Hacky Hacky Hacky !!! + var getSingleViewRecursive = (nodes: Array, nestLevel: number) => { + var actualNodes = nodes.filter((n) => !(n instanceof DummyViewNode)); + + if (actualNodes.length === 0) { + throw new Error("No suitable views found in list template! Nesting level: " + nestLevel); + } + else if (actualNodes.length > 1) { + throw new Error("More than one view found in list template! Nesting level: " + nestLevel); + } + else { + if (actualNodes[0].nativeView) { + return actualNodes[0].nativeView; + } + else { + return getSingleViewRecursive(actualNodes[0].children, nestLevel + 1) + } + } + } + + return getSingleViewRecursive((viewRef).renderFragment.nodes, 0); +} \ No newline at end of file diff --git a/src/nativescript-angular/directives/ns-directives.ts b/src/nativescript-angular/directives/ns-directives.ts new file mode 100644 index 000000000..fb58f7417 --- /dev/null +++ b/src/nativescript-angular/directives/ns-directives.ts @@ -0,0 +1,4 @@ +import {Type} from 'angular2/src/facade/lang'; +import {ListItemTemplate, ListViewDirective} from './list-view'; + +export const NS_DIRECTIVES: Type[] = [ListItemTemplate, ListViewDirective]; diff --git a/src/nativescript-angular/view_node.ts b/src/nativescript-angular/view_node.ts index 1e53f0faa..a32089bec 100644 --- a/src/nativescript-angular/view_node.ts +++ b/src/nativescript-angular/view_node.ts @@ -131,8 +131,8 @@ export class ViewNode { // complex property - we will deal with this in postAttachUI() } else { - console.log('parentNativeView: ' + this.parentNativeView); - throw new Error("Parent view can't have children! " + this.parentNativeView); + // Child adding will be handled elsewhere. For example ListView + console.log('Child not added for parent. child: ' + this.viewName + ', parent: ' + this.parentNativeView); } }