Skip to content

Commit 3a81e62

Browse files
m-absPeterStaev
authored andcommitted
implements #12 - Usage in NS-NG2 app (#21)
* chor: implements #12 - Usage in NS-NG2 app This adds an angular directive based on nativescript-angular-3.1.0's list-view-comp and the grid-view.module.ts, which an angular developer will have to import in his/hers own NgModule. * fix deps * chor: follow up on review * fix: rendering issue with angular on android We need to an extra refresh on android when we navigate back to the page with the GridView. * chor: follow up on review - 2 - Forgot to update the README - Don't use /index in nativescript-grid-view/angular/index - Moved super.onLayout(...) to the bottom of the function
1 parent afc66b2 commit 3a81e62

File tree

87 files changed

+1348
-28
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1348
-28
lines changed

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,44 @@ You need to add `xmlns:gv="nativescript-grid-view"` to your page tag, and then s
3333
</Page>
3434
```
3535

36+
### In nativescript-angular:
37+
38+
Template:
39+
```HTML
40+
<GridView [items]="items" colWidth="33%" rowHeight="50%" padding="5">
41+
<ng-template let-item="item">
42+
<StackLayout [nsRouterLink]="['/item', item.id]" borderColor="blue" borderWidth="2" borderRadius="5" verticalAlignment="stretch" class="list-group-item">
43+
<Label verticalAlignment="center" [text]="item.name" class="list-group-item-text"></Label>
44+
</StackLayout>
45+
</ng-template>
46+
</GridView>
47+
```
48+
49+
In your app-module:
50+
```typescript
51+
import { GridViewModule } from 'nativescript-grid-view/angular';
52+
53+
@NgModule({
54+
bootstrap: [
55+
...
56+
],
57+
imports: [
58+
...
59+
GridViewModule,
60+
],
61+
declarations: [
62+
...
63+
],
64+
providers: [
65+
...
66+
],
67+
schemas: [
68+
NO_ERRORS_SCHEMA
69+
]
70+
})
71+
export class AppModule { }
72+
```
73+
3674
## API
3775

3876
### Events

angular/grid-view-comp.ts

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

angular/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// External
2+
import {
3+
NO_ERRORS_SCHEMA,
4+
NgModule,
5+
} from "@angular/core";
6+
7+
import { GridViewComponent } from "./grid-view-comp";
8+
9+
@NgModule({
10+
declarations: [
11+
GridViewComponent,
12+
],
13+
exports: [
14+
GridViewComponent,
15+
],
16+
schemas: [
17+
NO_ERRORS_SCHEMA,
18+
],
19+
})
20+
export class GridViewModule {
21+
}

demo-ng/.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
node_modules
2+
hooks
3+
*.d.ts
4+
*.js
5+
platforms
6+
app/vendor-platform.android.ts
7+
app/vendor-platform.ios.ts
8+
app/vendor.ts
9+
*.css
10+
report
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="__PACKAGE__"
4+
android:versionCode="1"
5+
android:versionName="1.0">
6+
7+
<supports-screens
8+
android:smallScreens="true"
9+
android:normalScreens="true"
10+
android:largeScreens="true"
11+
android:xlargeScreens="true"/>
12+
13+
<uses-sdk
14+
android:minSdkVersion="17"
15+
android:targetSdkVersion="__APILEVEL__"/>
16+
17+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
18+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
19+
<uses-permission android:name="android.permission.INTERNET"/>
20+
21+
<application
22+
android:name="com.tns.NativeScriptApplication"
23+
android:allowBackup="true"
24+
android:icon="@drawable/icon"
25+
android:label="@string/app_name"
26+
android:theme="@style/AppTheme">
27+
28+
<activity
29+
android:name="com.tns.NativeScriptActivity"
30+
android:label="@string/title_activity_kimera"
31+
android:configChanges="keyboardHidden|orientation|screenSize"
32+
android:theme="@style/LaunchScreenTheme">
33+
34+
<meta-data android:name="SET_THEME_ON_LAUNCH" android:resource="@style/AppTheme" />
35+
36+
<intent-filter>
37+
<action android:name="android.intent.action.MAIN" />
38+
<category android:name="android.intent.category.LAUNCHER" />
39+
</intent-filter>
40+
</activity>
41+
<activity android:name="com.tns.ErrorReportActivity"/>
42+
</application>
43+
</manifest>

0 commit comments

Comments
 (0)