|
1 | 1 | import {
|
2 |
| - AfterContentInit, |
3 | 2 | ChangeDetectionStrategy,
|
4 | 3 | Component,
|
5 |
| - ContentChild, |
6 |
| - Directive, |
7 |
| - DoCheck, |
8 | 4 | ElementRef,
|
9 |
| - EmbeddedViewRef, |
10 |
| - EventEmitter, |
11 |
| - Host, |
12 |
| - Input, |
13 |
| - IterableDiffer, |
14 | 5 | IterableDiffers,
|
15 |
| - OnDestroy, |
16 |
| - Output, |
17 |
| - TemplateRef, |
18 |
| - ViewChild, |
19 |
| - ViewContainerRef, |
20 |
| - ɵisListLikeIterable as isListLikeIterable |
| 6 | + forwardRef |
21 | 7 | } from "@angular/core";
|
22 |
| -import { ListView, ItemEventData } from "tns-core-modules/ui/list-view"; |
23 |
| -import { View, KeyedTemplate } from "tns-core-modules/ui/core/view"; |
24 |
| -import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; |
25 |
| -import { ObservableArray } from "tns-core-modules/data/observable-array"; |
26 |
| -import { profile } from "tns-core-modules/profiling"; |
27 |
| - |
28 |
| -import { getSingleViewRecursive } from "../element-registry"; |
29 |
| -import { listViewLog, listViewError, isLogEnabled } from "../trace"; |
30 |
| - |
31 |
| -const NG_VIEW = "_ngViewRef"; |
32 |
| - |
33 |
| -export class ListItemContext { |
34 |
| - constructor( |
35 |
| - public $implicit?: any, |
36 |
| - public item?: any, |
37 |
| - public index?: number, |
38 |
| - public even?: boolean, |
39 |
| - public odd?: boolean |
40 |
| - ) { |
41 |
| - } |
42 |
| -} |
43 |
| - |
44 |
| -export interface SetupItemViewArgs { |
45 |
| - view: EmbeddedViewRef<any>; |
46 |
| - data: any; |
47 |
| - index: number; |
48 |
| - context: ListItemContext; |
49 |
| -} |
| 8 | +import { ListView } from "tns-core-modules/ui/list-view"; |
| 9 | +import { TEMPLATED_ITEMS_COMPONENT, TemplatedItemsComponent } from "./templated-items-comp"; |
50 | 10 |
|
51 | 11 | @Component({
|
52 | 12 | selector: "ListView",
|
53 | 13 | template: `
|
54 | 14 | <DetachedContainer>
|
55 | 15 | <Placeholder #loader></Placeholder>
|
56 | 16 | </DetachedContainer>`,
|
57 |
| - changeDetection: ChangeDetectionStrategy.OnPush |
| 17 | + changeDetection: ChangeDetectionStrategy.OnPush, |
| 18 | + providers: [{ provide: TEMPLATED_ITEMS_COMPONENT, useExisting: forwardRef(() => ListViewComponent)}] |
58 | 19 | })
|
59 |
| -export class ListViewComponent implements DoCheck, OnDestroy, AfterContentInit { |
| 20 | +export class ListViewComponent extends TemplatedItemsComponent { |
60 | 21 | public get nativeElement(): ListView {
|
61 |
| - return this.listView; |
62 |
| - } |
63 |
| - |
64 |
| - private listView: ListView; |
65 |
| - private _items: any; |
66 |
| - private _differ: IterableDiffer<KeyedTemplate>; |
67 |
| - private _templateMap: Map<string, KeyedTemplate>; |
68 |
| - |
69 |
| - @ViewChild("loader", { read: ViewContainerRef }) loader: ViewContainerRef; |
70 |
| - |
71 |
| - @Output() public setupItemView = new EventEmitter<SetupItemViewArgs>(); |
72 |
| - |
73 |
| - @ContentChild(TemplateRef) itemTemplateQuery: TemplateRef<ListItemContext>; |
74 |
| - |
75 |
| - itemTemplate: TemplateRef<ListItemContext>; |
76 |
| - |
77 |
| - @Input() |
78 |
| - get items() { |
79 |
| - return this._items; |
| 22 | + return this.templatedItemsView; |
80 | 23 | }
|
81 | 24 |
|
82 |
| - set items(value: any) { |
83 |
| - this._items = value; |
84 |
| - let needDiffer = true; |
85 |
| - if (value instanceof ObservableArray) { |
86 |
| - needDiffer = false; |
87 |
| - } |
88 |
| - if (needDiffer && !this._differ && isListLikeIterable(value)) { |
89 |
| - this._differ = this._iterableDiffers.find(this._items) |
90 |
| - .create((_index, item) => { return item; }); |
91 |
| - } |
92 |
| - |
93 |
| - this.listView.items = this._items; |
94 |
| - } |
| 25 | + protected templatedItemsView: ListView; |
95 | 26 |
|
96 | 27 | constructor(_elementRef: ElementRef,
|
97 |
| - private _iterableDiffers: IterableDiffers) { |
98 |
| - this.listView = _elementRef.nativeElement; |
99 |
| - |
100 |
| - this.listView.on("itemLoading", this.onItemLoading, this); |
101 |
| - } |
102 |
| - |
103 |
| - ngAfterContentInit() { |
104 |
| - if (isLogEnabled()) { |
105 |
| - listViewLog("ListView.ngAfterContentInit()"); |
106 |
| - } |
107 |
| - this.setItemTemplates(); |
108 |
| - } |
109 |
| - |
110 |
| - ngOnDestroy() { |
111 |
| - this.listView.off("itemLoading", this.onItemLoading, this); |
112 |
| - } |
113 |
| - |
114 |
| - private setItemTemplates() { |
115 |
| - // The itemTemplateQuery may be changed after list items are added that contain <template> inside, |
116 |
| - // so cache and use only the original template to avoid errors. |
117 |
| - this.itemTemplate = this.itemTemplateQuery; |
118 |
| - |
119 |
| - if (this._templateMap) { |
120 |
| - if (isLogEnabled()) { |
121 |
| - listViewLog("Setting templates"); |
122 |
| - } |
123 |
| - |
124 |
| - const templates: KeyedTemplate[] = []; |
125 |
| - this._templateMap.forEach(value => { |
126 |
| - templates.push(value); |
127 |
| - }); |
128 |
| - this.listView.itemTemplates = templates; |
129 |
| - } |
130 |
| - } |
131 |
| - |
132 |
| - public registerTemplate(key: string, template: TemplateRef<ListItemContext>) { |
133 |
| - if (isLogEnabled()) { |
134 |
| - listViewLog(`registerTemplate for key: ${key}`); |
135 |
| - } |
136 |
| - if (!this._templateMap) { |
137 |
| - this._templateMap = new Map<string, KeyedTemplate>(); |
138 |
| - } |
139 |
| - |
140 |
| - const keyedTemplate = { |
141 |
| - key, |
142 |
| - createView: () => { |
143 |
| - if (isLogEnabled()) { |
144 |
| - listViewLog(`registerTemplate for key: ${key}`); |
145 |
| - } |
146 |
| - |
147 |
| - const viewRef = this.loader.createEmbeddedView(template, new ListItemContext(), 0); |
148 |
| - const resultView = getItemViewRoot(viewRef); |
149 |
| - resultView[NG_VIEW] = viewRef; |
150 |
| - |
151 |
| - return resultView; |
152 |
| - } |
153 |
| - }; |
154 |
| - |
155 |
| - this._templateMap.set(key, keyedTemplate); |
156 |
| - } |
157 |
| - |
158 |
| - @profile |
159 |
| - public onItemLoading(args: ItemEventData) { |
160 |
| - if (!args.view && !this.itemTemplate) { |
161 |
| - return; |
162 |
| - } |
163 |
| - |
164 |
| - const index = args.index; |
165 |
| - const items = (<any>args.object).items; |
166 |
| - const currentItem = typeof items.getItem === "function" ? items.getItem(index) : items[index]; |
167 |
| - let viewRef: EmbeddedViewRef<ListItemContext>; |
168 |
| - |
169 |
| - if (args.view) { |
170 |
| - if (isLogEnabled()) { |
171 |
| - listViewLog(`onItemLoading: ${index} - Reusing existing view`); |
172 |
| - } |
173 |
| - viewRef = args.view[NG_VIEW]; |
174 |
| - // Getting angular view from original element (in cases when ProxyViewContainer |
175 |
| - // is used NativeScript internally wraps it in a StackLayout) |
176 |
| - if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) { |
177 |
| - viewRef = args.view.getChildAt(0)[NG_VIEW]; |
178 |
| - } |
179 |
| - |
180 |
| - if (!viewRef) { |
181 |
| - if (isLogEnabled()) { |
182 |
| - listViewError(`ViewReference not found for item ${index}. View recycling is not working`); |
183 |
| - } |
184 |
| - } |
185 |
| - } |
186 |
| - |
187 |
| - if (!viewRef) { |
188 |
| - if (isLogEnabled()) { |
189 |
| - listViewLog(`onItemLoading: ${index} - Creating view from template`); |
190 |
| - } |
191 |
| - viewRef = this.loader.createEmbeddedView(this.itemTemplate, new ListItemContext(), 0); |
192 |
| - args.view = getItemViewRoot(viewRef); |
193 |
| - args.view[NG_VIEW] = viewRef; |
194 |
| - } |
195 |
| - |
196 |
| - this.setupViewRef(viewRef, currentItem, index); |
197 |
| - |
198 |
| - this.detectChangesOnChild(viewRef, index); |
199 |
| - } |
200 |
| - |
201 |
| - public setupViewRef(viewRef: EmbeddedViewRef<ListItemContext>, data: any, index: number): void { |
202 |
| - const context = viewRef.context; |
203 |
| - context.$implicit = data; |
204 |
| - context.item = data; |
205 |
| - context.index = index; |
206 |
| - context.even = (index % 2 === 0); |
207 |
| - context.odd = !context.even; |
208 |
| - |
209 |
| - this.setupItemView.next({ view: viewRef, data: data, index: index, context: context }); |
210 |
| - } |
211 |
| - |
212 |
| - @profile |
213 |
| - private detectChangesOnChild(viewRef: EmbeddedViewRef<ListItemContext>, index: number) { |
214 |
| - if (isLogEnabled()) { |
215 |
| - listViewLog(`Manually detect changes in child: ${index}`); |
216 |
| - } |
217 |
| - viewRef.markForCheck(); |
218 |
| - viewRef.detectChanges(); |
219 |
| - } |
220 |
| - |
221 |
| - ngDoCheck() { |
222 |
| - if (this._differ) { |
223 |
| - if (isLogEnabled()) { |
224 |
| - listViewLog("ngDoCheck() - execute differ"); |
225 |
| - } |
226 |
| - const changes = this._differ.diff(this._items); |
227 |
| - if (changes) { |
228 |
| - if (isLogEnabled()) { |
229 |
| - listViewLog("ngDoCheck() - refresh"); |
230 |
| - } |
231 |
| - this.listView.refresh(); |
232 |
| - } |
233 |
| - } |
234 |
| - } |
235 |
| -} |
236 |
| - |
237 |
| -export interface ComponentView { |
238 |
| - rootNodes: Array<any>; |
239 |
| - destroy(): void; |
240 |
| -} |
241 |
| - |
242 |
| -export type RootLocator = (nodes: Array<any>, nestLevel: number) => View; |
243 |
| - |
244 |
| -export function getItemViewRoot(viewRef: ComponentView, rootLocator: RootLocator = getSingleViewRecursive): View { |
245 |
| - const rootView = rootLocator(viewRef.rootNodes, 0); |
246 |
| - return rootView; |
247 |
| -} |
248 |
| - |
249 |
| -@Directive({ selector: "[nsTemplateKey]" }) |
250 |
| -export class TemplateKeyDirective { |
251 |
| - constructor( |
252 |
| - private templateRef: TemplateRef<any>, |
253 |
| - @Host() private list: ListViewComponent) { |
254 |
| - } |
255 |
| - |
256 |
| - @Input() |
257 |
| - set nsTemplateKey(value: any) { |
258 |
| - if (this.list && this.templateRef) { |
259 |
| - this.list.registerTemplate(value, this.templateRef); |
260 |
| - } |
| 28 | + _iterableDiffers: IterableDiffers) { |
| 29 | + super(_elementRef, _iterableDiffers); |
261 | 30 | }
|
262 | 31 | }
|
0 commit comments