|
1 | | -/* |
2 | | -Based on https://github.com/NativeScript/nativescript-angular/blob/3.1.0/nativescript-angular/directives/list-view-comp.ts |
3 | | -Original License |
4 | | - Copyright (c) 2015-2016 Telerik AD |
| 1 | +/*! ***************************************************************************** |
| 2 | +Copyright (c) 2019 Tangra Inc. |
5 | 3 |
|
6 | | - Licensed under the Apache License, Version 2.0 (the "License"); |
7 | | - you may not use this file except in compliance with the License. |
8 | | - You may obtain a copy of the License at |
| 4 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +you may not use this file except in compliance with the License. |
| 6 | +You may obtain a copy of the License at |
9 | 7 |
|
10 | | - http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
11 | 9 |
|
12 | | - Unless required by applicable law or agreed to in writing, software |
13 | | - distributed under the License is distributed on an "AS IS" BASIS, |
14 | | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | | - See the License for the specific language governing permissions and |
16 | | - limitations under the License. |
17 | | -END - original License |
18 | | - */ |
| 10 | +Unless required by applicable law or agreed to in writing, software |
| 11 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +See the License for the specific language governing permissions and |
| 14 | +limitations under the License. |
| 15 | +***************************************************************************** */ |
19 | 16 |
|
20 | 17 | import { |
21 | | - AfterContentInit, |
22 | 18 | ChangeDetectionStrategy, |
23 | 19 | Component, |
24 | | - ContentChild, |
25 | | - Directive, |
26 | | - DoCheck, |
27 | 20 | ElementRef, |
28 | | - EmbeddedViewRef, |
29 | | - EventEmitter, |
30 | | - Host, |
31 | | - Inject, |
32 | | - Input, |
33 | | - IterableDiffer, |
34 | 21 | IterableDiffers, |
35 | | - OnDestroy, |
36 | | - Output, |
37 | | - TemplateRef, |
38 | | - ViewChild, |
39 | | - ViewContainerRef, |
40 | | - ɵisListLikeIterable as isListLikeIterable, |
| 22 | + forwardRef |
41 | 23 | } from "@angular/core"; |
42 | | -import { ObservableArray } from "tns-core-modules/data/observable-array"; |
43 | | -import { profile } from "tns-core-modules/profiling"; |
44 | | -import { KeyedTemplate, View } from "tns-core-modules/ui/core/view"; |
45 | | -import { LayoutBase } from "tns-core-modules/ui/layouts/layout-base"; |
46 | | -import { GridItemEventData, GridView } from "../grid-view"; |
47 | | -import { gridViewError, gridViewLog } from "./trace"; |
48 | 24 |
|
49 | | -import { getSingleViewRecursive, isKnownView, registerElement } from "nativescript-angular/element-registry"; |
| 25 | +import { TEMPLATED_ITEMS_COMPONENT, TemplatedItemsComponent } from "nativescript-angular/directives/templated-items-comp"; |
| 26 | +import { isKnownView, registerElement } from "nativescript-angular/element-registry"; |
50 | 27 |
|
51 | | -const NG_VIEW = "_ngViewRef"; |
52 | | - |
53 | | -export class GridItemContext { |
54 | | - constructor( |
55 | | - public $implicit?: any, |
56 | | - public item?: any, |
57 | | - public index?: number, |
58 | | - public even?: boolean, |
59 | | - public odd?: boolean |
60 | | - ) { |
61 | | - } |
62 | | -} |
63 | | - |
64 | | -export interface SetupItemViewArgs { |
65 | | - view: EmbeddedViewRef<any>; |
66 | | - data: any; |
67 | | - index: number; |
68 | | - context: GridItemContext; |
69 | | -} |
| 28 | +import { GridView } from "../grid-view"; |
70 | 29 |
|
71 | 30 | @Component({ |
72 | 31 | selector: "GridView", |
73 | 32 | template: ` |
74 | 33 | <DetachedContainer> |
75 | 34 | <Placeholder #loader></Placeholder> |
76 | 35 | </DetachedContainer>`, |
77 | | - changeDetection: ChangeDetectionStrategy.OnPush |
| 36 | + changeDetection: ChangeDetectionStrategy.OnPush, |
| 37 | + providers: [{ provide: TEMPLATED_ITEMS_COMPONENT, useExisting: forwardRef(() => GridViewComponent)}] |
78 | 38 | }) |
79 | | -export class GridViewComponent implements DoCheck, OnDestroy, AfterContentInit { |
| 39 | +export class GridViewComponent extends TemplatedItemsComponent { |
80 | 40 | public get nativeElement(): GridView { |
81 | | - return this.gridView; |
82 | | - } |
83 | | - |
84 | | - @ViewChild("loader", { read: ViewContainerRef }) public loader: ViewContainerRef; |
85 | | - @Output() public setupItemView = new EventEmitter<SetupItemViewArgs>(); |
86 | | - @ContentChild(TemplateRef) public itemTemplateQuery: TemplateRef<GridItemContext>; |
87 | | - |
88 | | - @Input() |
89 | | - public get items() { |
90 | | - return this._items; |
91 | | - } |
92 | | - public set items(value: any) { |
93 | | - this._items = value; |
94 | | - let needDiffer = true; |
95 | | - if (value instanceof ObservableArray) { |
96 | | - needDiffer = false; |
97 | | - } |
98 | | - if (needDiffer && !this._differ && isListLikeIterable(value)) { |
99 | | - this._differ = this._iterableDiffers.find(this._items) |
100 | | - .create((_index, item) => item); |
101 | | - } |
102 | | - |
103 | | - this.gridView.items = this._items; |
| 41 | + return this.templatedItemsView; |
104 | 42 | } |
105 | 43 |
|
106 | | - private gridView: GridView; |
107 | | - private _items: any; |
108 | | - private _differ: IterableDiffer<KeyedTemplate>; |
109 | | - private itemTemplate: TemplateRef<GridItemContext>; |
110 | | - private _templateMap: Map<string, KeyedTemplate>; |
| 44 | + protected templatedItemsView: GridView; |
111 | 45 |
|
112 | 46 | constructor( |
113 | | - @Inject(ElementRef) _elementRef: ElementRef, |
114 | | - @Inject(IterableDiffers) private _iterableDiffers: IterableDiffers, |
115 | | - ) { |
116 | | - this.gridView = _elementRef.nativeElement; |
117 | | - |
118 | | - this.gridView.on(GridView.itemLoadingEvent, this.onItemLoading, this); |
119 | | - } |
120 | | - |
121 | | - public ngAfterContentInit() { |
122 | | - gridViewLog("GridView.ngAfterContentInit()"); |
123 | | - this.setItemTemplates(); |
124 | | - } |
125 | | - |
126 | | - public ngOnDestroy() { |
127 | | - this.gridView.off(GridView.itemLoadingEvent, this.onItemLoading, this); |
128 | | - } |
129 | | - |
130 | | - public ngDoCheck() { |
131 | | - gridViewLog("ngDoCheck() - execute differ? " + this._differ); |
132 | | - if (this._differ) { |
133 | | - gridViewLog("ngDoCheck() - execute differ"); |
134 | | - const changes = this._differ.diff(this._items); |
135 | | - if (changes) { |
136 | | - gridViewLog("ngDoCheck() - refresh"); |
137 | | - this.refresh(); |
138 | | - } |
139 | | - } |
140 | | - } |
141 | | - |
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 | | - |
156 | | - @profile |
157 | | - public onItemLoading(args: GridItemEventData) { |
158 | | - if (!args.view && !this.itemTemplate) { |
159 | | - return; |
160 | | - } |
161 | | - |
162 | | - const index = args.index; |
163 | | - const items = args.object.items as any; |
164 | | - const currentItem = typeof items.getItem === "function" ? items.getItem(index) : items[index]; |
165 | | - let viewRef: EmbeddedViewRef<GridItemContext>; |
166 | | - |
167 | | - if (args.view) { |
168 | | - gridViewLog("onItemLoading: " + index + " - Reusing existing view"); |
169 | | - viewRef = args.view[NG_VIEW]; |
170 | | - // Getting angular view from original element (in cases when ProxyViewContainer |
171 | | - // is used NativeScript internally wraps it in a StackLayout) |
172 | | - if (!viewRef && args.view instanceof LayoutBase && args.view.getChildrenCount() > 0) { |
173 | | - viewRef = args.view.getChildAt(0)[NG_VIEW]; |
174 | | - } |
175 | | - |
176 | | - if (!viewRef) { |
177 | | - gridViewError("ViewReference not found for item " + index + ". View recycling is not working"); |
178 | | - } |
179 | | - } |
180 | | - |
181 | | - if (!viewRef) { |
182 | | - gridViewLog("onItemLoading: " + index + " - Creating view from template"); |
183 | | - viewRef = this.loader.createEmbeddedView(this.itemTemplate, new GridItemContext(), 0); |
184 | | - args.view = getItemViewRoot(viewRef); |
185 | | - args.view[NG_VIEW] = viewRef; |
186 | | - } |
187 | | - |
188 | | - this.setupViewRef(viewRef, currentItem, index); |
189 | | - |
190 | | - this.detectChangesOnChild(viewRef, index); |
191 | | - } |
192 | | - |
193 | | - public setupViewRef(view: EmbeddedViewRef<GridItemContext>, data: any, index: number): void { |
194 | | - const context = view.context; |
195 | | - context.$implicit = data; |
196 | | - context.item = data; |
197 | | - context.index = index; |
198 | | - context.even = (index % 2 === 0); |
199 | | - context.odd = !context.even; |
200 | | - |
201 | | - this.setupItemView.next({ |
202 | | - context, |
203 | | - data, |
204 | | - index, |
205 | | - view, |
206 | | - }); |
207 | | - } |
208 | | - |
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 | | - |
219 | | - private setItemTemplates() { |
220 | | - // The itemTemplateQuery may be changed after list items are added that contain <template> inside, |
221 | | - // so cache and use only the original template to avoid errors. |
222 | | - this.itemTemplate = this.itemTemplateQuery; |
223 | | - |
224 | | - if (this._templateMap) { |
225 | | - gridViewLog("Setting templates"); |
226 | | - |
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 | | - } |
236 | | - } |
237 | | - |
238 | | - @profile |
239 | | - private detectChangesOnChild(viewRef: EmbeddedViewRef<GridItemContext>, index: number) { |
240 | | - gridViewLog("Manually detect changes in child: " + index); |
241 | | - viewRef.markForCheck(); |
242 | | - viewRef.detectChanges(); |
243 | | - } |
244 | | - |
245 | | - private refresh() { |
246 | | - if (this.gridView) { |
247 | | - this.gridView.refresh(); |
248 | | - } |
249 | | - } |
250 | | -} |
251 | | - |
252 | | -export interface ComponentView { |
253 | | - rootNodes: any[]; |
254 | | - destroy(): void; |
255 | | -} |
256 | | - |
257 | | -export type RootLocator = (nodes: any[], nestLevel: number) => View; |
258 | | - |
259 | | -export function getItemViewRoot(viewRef: ComponentView, rootLocator: RootLocator = getSingleViewRecursive): View { |
260 | | - const rootView = rootLocator(viewRef.rootNodes, 0); |
261 | | - return rootView; |
262 | | -} |
263 | | - |
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 | | - } |
| 47 | + _elementRef: ElementRef, |
| 48 | + _iterableDiffers: IterableDiffers) { |
| 49 | + super(_elementRef, _iterableDiffers); |
| 50 | + |
278 | 51 | } |
279 | 52 | } |
280 | 53 |
|
281 | 54 | if (!isKnownView("GridView")) { |
282 | | - registerElement("GridView", () => GridView); |
| 55 | + registerElement("GridView", () => GridView); |
283 | 56 | } |
0 commit comments