Skip to content

Commit 3cab59e

Browse files
committed
implement #30 - angular
1 parent 87b441f commit 3cab59e

File tree

9 files changed

+128
-66
lines changed

9 files changed

+128
-66
lines changed

angular/grid-view-comp.ts

Lines changed: 70 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ END - original License
1919

2020
import {
2121
AfterContentInit,
22-
AfterViewInit,
2322
ChangeDetectionStrategy,
2423
Component,
2524
ContentChild,
25+
Directive,
2626
DoCheck,
2727
ElementRef,
2828
EmbeddedViewRef,
2929
EventEmitter,
30+
Host,
3031
Inject,
3132
Input,
3233
IterableDiffer,
@@ -40,32 +41,12 @@ import {
4041
} from "@angular/core";
4142
import { ObservableArray } from "tns-core-modules/data/observable-array";
4243
import { profile } from "tns-core-modules/profiling";
43-
import { messageType, write } from "tns-core-modules/trace";
44-
import {
45-
KeyedTemplate,
46-
View,
47-
} from "tns-core-modules/ui/core/view";
44+
import { KeyedTemplate, View } from "tns-core-modules/ui/core/view";
4845
import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base";
49-
import {
50-
GridItemEventData,
51-
GridView,
52-
} from "../grid-view";
53-
54-
import {
55-
getSingleViewRecursive,
56-
isKnownView,
57-
registerElement,
58-
} from "nativescript-angular/element-registry";
59-
60-
export const gridViewTraceCategory = "ns-grid-view";
46+
import { GridItemEventData, GridView } from "../grid-view";
47+
import { gridViewError, gridViewLog } from "./trace";
6148

62-
export function gridViewLog(message: string): void {
63-
write(message, gridViewTraceCategory);
64-
}
65-
66-
export function listViewError(message: string): void {
67-
write(message, gridViewTraceCategory, messageType.error);
68-
}
49+
import { getSingleViewRecursive, isKnownView, registerElement } from "nativescript-angular/element-registry";
6950

7051
const NG_VIEW = "_ngViewRef";
7152

@@ -80,7 +61,7 @@ export class GridItemContext {
8061
}
8162
}
8263

83-
export interface SetupGridViewArgs {
64+
export interface SetupItemViewArgs {
8465
view: EmbeddedViewRef<any>;
8566
data: any;
8667
index: number;
@@ -95,22 +76,19 @@ export interface SetupGridViewArgs {
9576
</DetachedContainer>`,
9677
changeDetection: ChangeDetectionStrategy.OnPush
9778
})
98-
export class GridViewComponent implements DoCheck, OnDestroy, AfterContentInit, AfterViewInit {
79+
export class GridViewComponent implements DoCheck, OnDestroy, AfterContentInit {
9980
public get nativeElement(): GridView {
10081
return this.gridView;
10182
}
10283

10384
@ViewChild("loader", { read: ViewContainerRef }) public loader: ViewContainerRef;
104-
105-
@Output() public setupGridView = new EventEmitter<SetupGridViewArgs>();
106-
85+
@Output() public setupItemView = new EventEmitter<SetupItemViewArgs>();
10786
@ContentChild(TemplateRef) public itemTemplateQuery: TemplateRef<GridItemContext>;
10887

10988
@Input()
11089
public get items() {
11190
return this._items;
11291
}
113-
11492
public set items(value: any) {
11593
this._items = value;
11694
let needDiffer = true;
@@ -129,9 +107,12 @@ export class GridViewComponent implements DoCheck, OnDestroy, AfterContentInit,
129107
private _items: any;
130108
private _differ: IterableDiffer<KeyedTemplate>;
131109
private itemTemplate: TemplateRef<GridItemContext>;
110+
private _templateMap: Map<string, KeyedTemplate>;
132111

133-
constructor(@Inject(ElementRef) _elementRef: ElementRef,
134-
@Inject(IterableDiffers) private _iterableDiffers: IterableDiffers) {
112+
constructor(
113+
@Inject(ElementRef) _elementRef: ElementRef,
114+
@Inject(IterableDiffers) private _iterableDiffers: IterableDiffers,
115+
) {
135116
this.gridView = _elementRef.nativeElement;
136117

137118
this.gridView.on(GridView.itemLoadingEvent, this.onItemLoading, this);
@@ -142,10 +123,6 @@ export class GridViewComponent implements DoCheck, OnDestroy, AfterContentInit,
142123
this.setItemTemplates();
143124
}
144125

145-
public ngAfterViewInit() {
146-
gridViewLog("GridView.ngAfterViewInit()");
147-
}
148-
149126
public ngOnDestroy() {
150127
this.gridView.off(GridView.itemLoadingEvent, this.onItemLoading, this);
151128
}
@@ -162,6 +139,20 @@ export class GridViewComponent implements DoCheck, OnDestroy, AfterContentInit,
162139
}
163140
}
164141

142+
public registerTemplate(key: string, template: TemplateRef<GridItemContext>) {
143+
gridViewLog("registerTemplate for key: " + key);
144+
if (!this._templateMap) {
145+
this._templateMap = new Map<string, KeyedTemplate>();
146+
}
147+
148+
const keyedTemplate = {
149+
key,
150+
createView: this.createNativeViewFactoryFromTemplate(template),
151+
};
152+
153+
this._templateMap.set(key, keyedTemplate);
154+
}
155+
165156
@profile
166157
public onItemLoading(args: GridItemEventData) {
167158
if (!args.view && !this.itemTemplate) {
@@ -183,14 +174,14 @@ export class GridViewComponent implements DoCheck, OnDestroy, AfterContentInit,
183174
}
184175

185176
if (!viewRef) {
186-
listViewError("ViewReference not found for item " + index + ". View recycling is not working");
177+
gridViewError("ViewReference not found for item " + index + ". View recycling is not working");
187178
}
188179
}
189180

190181
if (!viewRef) {
191182
gridViewLog("onItemLoading: " + index + " - Creating view from template");
192183
viewRef = this.loader.createEmbeddedView(this.itemTemplate, new GridItemContext(), 0);
193-
args.view = getGridItemRoot(viewRef);
184+
args.view = getItemViewRoot(viewRef);
194185
args.view[NG_VIEW] = viewRef;
195186
}
196187

@@ -207,26 +198,41 @@ export class GridViewComponent implements DoCheck, OnDestroy, AfterContentInit,
207198
context.even = (index % 2 === 0);
208199
context.odd = !context.even;
209200

210-
this.setupGridView.next({
201+
this.setupItemView.next({
211202
context,
212203
data,
213204
index,
214205
view,
215206
});
216207
}
217208

209+
private createNativeViewFactoryFromTemplate(template: TemplateRef<GridItemContext>) {
210+
return () => {
211+
const viewRef = this.loader.createEmbeddedView(template, new GridItemContext(), 0);
212+
const resultView = getItemViewRoot(viewRef);
213+
resultView[NG_VIEW] = viewRef;
214+
215+
return resultView;
216+
};
217+
}
218+
218219
private setItemTemplates() {
219220
// The itemTemplateQuery may be changed after list items are added that contain <template> inside,
220221
// so cache and use only the original template to avoid errors.
221222
this.itemTemplate = this.itemTemplateQuery;
222223

223-
this.gridView.itemTemplate = () => {
224-
const viewRef = this.loader.createEmbeddedView(this.itemTemplate, new GridItemContext(), 0);
225-
const resultView = getGridItemRoot(viewRef);
226-
resultView[NG_VIEW] = viewRef;
224+
if (this._templateMap) {
225+
gridViewLog("Setting templates");
227226

228-
return resultView;
229-
};
227+
const templates: KeyedTemplate[] = [];
228+
this._templateMap.forEach((value) => {
229+
templates.push(value);
230+
});
231+
this.gridView.itemTemplates = templates;
232+
}
233+
else { // If the map was not initialized this means that there are no named templates, so we register the default one.
234+
this.gridView.itemTemplate = this.createNativeViewFactoryFromTemplate(this.itemTemplate);
235+
}
230236
}
231237

232238
@profile
@@ -250,11 +256,28 @@ export interface ComponentView {
250256

251257
export type RootLocator = (nodes: any[], nestLevel: number) => View;
252258

253-
export function getGridItemRoot(viewRef: ComponentView, rootLocator: RootLocator = getSingleViewRecursive): View {
259+
export function getItemViewRoot(viewRef: ComponentView, rootLocator: RootLocator = getSingleViewRecursive): View {
254260
const rootView = rootLocator(viewRef.rootNodes, 0);
255261
return rootView;
256262
}
257263

264+
@Directive({ selector: "[gvTemplateKey]" })
265+
export class TemplateKeyDirective {
266+
constructor(
267+
private templateRef: TemplateRef<any>,
268+
@Host() private grid: GridViewComponent,
269+
) {
270+
}
271+
272+
@Input()
273+
set gvTemplateKey(value: any) {
274+
gridViewLog("gvTemplateKey: " + value);
275+
if (this.grid && this.templateRef) {
276+
this.grid.registerTemplate(value, this.templateRef);
277+
}
278+
}
279+
}
280+
258281
if (!isKnownView("GridView")) {
259282
registerElement("GridView", () => GridView);
260283
}

angular/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import {
44
NgModule,
55
} from "@angular/core";
66

7-
import { GridViewComponent } from "./grid-view-comp";
7+
import { GridViewComponent, TemplateKeyDirective } from "./grid-view-comp";
88

99
@NgModule({
1010
declarations: [
1111
GridViewComponent,
12+
TemplateKeyDirective,
1213
],
1314
exports: [
1415
GridViewComponent,
16+
TemplateKeyDirective,
1517
],
1618
schemas: [
1719
NO_ERRORS_SCHEMA,

angular/trace.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { messageType, write } from "tns-core-modules/trace";
2+
3+
const gridViewTraceCategory = "ns-grid-view";
4+
5+
export function gridViewLog(message: string): void {
6+
write(message, gridViewTraceCategory);
7+
}
8+
9+
export function gridViewError(message: string): void {
10+
write(message, gridViewTraceCategory, messageType.error);
11+
}

demo-ng/app/item/items.component.html

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,29 @@
3131
</StackLayout>
3232
</ng-template>
3333
</GridView>
34-
<GridView row="1" [items]="items" colWidth="33%" rowHeight="100" orientation="horizontal">
35-
<ng-template let-item="item" let-odd="odd">
34+
<GridView row="1" [items]="items" colWidth="33%" rowHeight="100" orientation="horizontal" [itemTemplateSelector]="templateSelector">
35+
<ng-template gvTemplateKey="Defender" let-item="item" let-odd="odd">
3636
<StackLayout [nsRouterLink]="['/item', item.id]" borderColor="blue" borderWidth="2" borderRadius="5" verticalAlignment="stretch" class="list-group-item" [class.odd]="odd">
3737
<Label verticalAlignment="center" [text]="item.name" class="list-group-item-text" textWrap="true"></Label>
3838
</StackLayout>
3939
</ng-template>
40+
41+
<ng-template gvTemplateKey="Goalkeeper" let-item="item" let-odd="odd">
42+
<StackLayout [nsRouterLink]="['/item', item.id]" borderColor="black" borderWidth="2" borderRadius="5" verticalAlignment="stretch" class="list-group-item" [class.odd]="odd">
43+
<Label verticalAlignment="center" [text]="item.name" class="list-group-item-text" textWrap="true"></Label>
44+
</StackLayout>
45+
</ng-template>
46+
47+
<ng-template gvTemplateKey="Midfielder" let-item="item" let-odd="odd">
48+
<StackLayout [nsRouterLink]="['/item', item.id]" borderColor="yellow" borderWidth="2" borderRadius="5" verticalAlignment="stretch" class="list-group-item" [class.odd]="odd">
49+
<Label verticalAlignment="center" [text]="item.name" class="list-group-item-text" textWrap="true"></Label>
50+
</StackLayout>
51+
</ng-template>
52+
53+
<ng-template gvTemplateKey="Forward" let-item="item" let-odd="odd">
54+
<StackLayout [nsRouterLink]="['/item', item.id]" borderColor="red" borderWidth="2" borderRadius="5" verticalAlignment="stretch" class="list-group-item" [class.odd]="odd">
55+
<Label verticalAlignment="center" [text]="item.name" class="list-group-item-text" textWrap="true"></Label>
56+
</StackLayout>
57+
</ng-template>
4058
</GridView>
4159
</GridLayout>

demo-ng/app/item/items.component.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,17 @@ import { ItemService } from "./item.service";
99
templateUrl: "./items.component.html",
1010
})
1111
export class ItemsComponent implements OnInit {
12-
items: Item[];
12+
public items: Item[];
1313

1414
// This pattern makes use of Angular’s dependency injection implementation to inject an instance of the ItemService service into this class.
1515
// Angular knows about this service because it is included in your app’s main NgModule, defined in app.module.ts.
1616
constructor(private itemService: ItemService) { }
1717

18-
ngOnInit(): void {
18+
public ngOnInit(): void {
1919
this.items = this.itemService.getItems();
2020
}
21+
22+
public templateSelector(item: Item) {
23+
return item.role;
24+
}
2125
}

demo-ng/app/main.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { platformNativeScriptDynamic } from "nativescript-angular/platform";
33

44
import { AppModule } from "./app.module";
55

6+
// import * as trace from "trace";
7+
// trace.setCategories("ns-grid-view");
8+
// trace.enable();
9+
610
// A traditional NativeScript application starts by initializing global objects, setting up global CSS rules, creating, and navigating to the main page.
711
// Angular applications need to take care of their own initialization: modules, components, directives, routes, DI providers.
812
// A NativeScript Angular app needs to make both paradigms work together, so we provide a wrapper platform object, platformNativeScriptDynamic,

grid-view-common.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,18 @@ export const itemTemplateProperty = new Property<GridViewBase, string | Template
164164
});
165165
itemTemplateProperty.register(GridViewBase);
166166

167+
export const itemTemplatesProperty = new Property<GridViewBase, string | KeyedTemplate[]>({
168+
name: "itemTemplates",
169+
valueConverter: (value) => {
170+
if (typeof value === "string") {
171+
return parseMultipleTemplates(value);
172+
}
173+
174+
return value;
175+
}
176+
});
177+
itemTemplatesProperty.register(GridViewBase);
178+
167179
const defaultRowHeight: PercentLength = "auto";
168180
export const rowHeightProperty = new CoercibleProperty<GridViewBase, PercentLength>({
169181
name: "rowHeight",
@@ -209,15 +221,3 @@ export const orientationProperty = new Property<GridViewBase, Orientation>({
209221
valueConverter: converter
210222
});
211223
orientationProperty.register(GridViewBase);
212-
213-
export const itemTemplatesProperty = new Property<GridViewBase, string | KeyedTemplate[]>({
214-
name: "itemTemplates",
215-
valueConverter: (value) => {
216-
if (typeof value === "string") {
217-
return parseMultipleTemplates(value);
218-
}
219-
220-
return value;
221-
}
222-
});
223-
itemTemplatesProperty.register(GridViewBase);

grid-view.android.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,6 @@ function initGridViewAdapter() {
355355
const owner = this.owner.get();
356356
const template = owner._itemTemplatesInternal[viewType];
357357
const view = template.createView();
358-
console.log("CreateViewHolder", viewType, owner._realizedItems.size);
359358

360359
owner._addView(view);
361360

grid-view.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
***************************************************************************** */
1616

17-
import { PercentLength, Template, View } from "ui/core/view";
17+
import { KeyedTemplate, PercentLength, Template, View } from "ui/core/view";
1818
import { ItemsSource } from "ui/list-view";
1919
import { EventData } from "data/observable";
2020

@@ -27,6 +27,7 @@ export class GridView extends View {
2727

2828
public items: any[] | ItemsSource;
2929
public itemTemplate: string | Template;
30+
public itemTemplates: string | KeyedTemplate[];
3031
public rowHeight: PercentLength;
3132
public colWidth: PercentLength;
3233
public orientation: Orientation;

0 commit comments

Comments
 (0)