Skip to content

Commit c3d97f7

Browse files
committed
fix and improve angular ng-for demo
1 parent 2f3b74f commit c3d97f7

File tree

1 file changed

+117
-61
lines changed

1 file changed

+117
-61
lines changed

demo/angular-ngFor.ts

Lines changed: 117 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,122 @@
11
/**
22
* Example using Angular ngFor to loop through items and create DOM items
33
*/
4-
import { Component, AfterViewInit, OnChanges, SimpleChanges, Input, ChangeDetectionStrategy } from '@angular/core';
4+
import {
5+
Component,
6+
AfterViewInit,
7+
Input,
8+
ViewChildren,
9+
QueryList,
10+
} from "@angular/core";
511

6-
import { GridStack, GridStackWidget } from 'gridstack';
7-
import 'gridstack/dist/h5/gridstack-dd-native';
8-
9-
@Component({
10-
selector: 'app-angular-ng-for-test',
11-
template: `
12-
<button (click)="add()">add item</button><button (click)="delete()">remove item</button><button (click)="change()">modify item</button>
13-
<div class="grid-stack">
14-
<!-- using angular templating to create DOM, otherwise an easier way is to simply call grid.load(items) -->
15-
<div *ngFor="let n of items; let i = index" class="grid-stack-item" [attr.gs-w]="n.w" [attr.gs-h]="n.h" [attr.gs-x]="n.x" [attr.gs-y]="n.y">
16-
<div class="grid-stack-item-content">item {{i}}</div>
17-
</div>
18-
</div>
19-
`,
20-
// gridstack.min.css and other custom styles should be included in global styles.scss
21-
22-
// tell Angular only @Input will change - doesn't help
23-
// changeDetection: ChangeDetectionStrategy.OnPush
24-
})
25-
export class AngularNgForTestComponent implements AfterViewInit, OnChanges {
12+
import { GridStack, GridStackWidget } from "gridstack";
13+
import "gridstack/dist/h5/gridstack-dd-native";
14+
import { Subject, zip } from "rxjs";
15+
16+
@Component({
17+
selector: "app-angular-ng-for-test",
18+
template: `
19+
<button (click)="add()">add item</button
20+
><button (click)="delete()">remove item</button
21+
><button (click)="change()">modify item</button>
22+
<div class="grid-stack">
23+
<!-- using angular templating to create DOM, otherwise an easier way is to simply call grid.load(items) -->
24+
<div
25+
*ngFor="let n of items; let i = index; trackBy: identify"
26+
[id]="i"
27+
class="grid-stack-item"
28+
[attr.gs-w]="n.w"
29+
[attr.gs-h]="n.h"
30+
[attr.gs-x]="n.x"
31+
[attr.gs-y]="n.y"
32+
#gridStackItem
33+
>
34+
<div class="grid-stack-item-content">item {{ i }}</div>
35+
</div>
36+
</div>
37+
`,
38+
styles: [
39+
`
40+
// !!!IMPORTANT!!! Import this through your styles.scss or angular.json! This is just for demo purposes
41+
:host {
42+
::ng-deep {
43+
@import "gridstack/dist/gridstack.min";
44+
}
45+
}
46+
.grid-stack {
47+
margin: 15px;
48+
background-color: #d66666;
49+
min-height: calc(100vh - 50px);
50+
}
51+
52+
.grid-stack-item {
53+
border: 1px solid #d1d1d1;
54+
}
55+
`,
56+
],
57+
})
58+
export class AngularNgForTestComponent implements AfterViewInit {
59+
@ViewChildren("gridStackItem") gridstackItems: QueryList<any>;
2660
@Input() public items: GridStackWidget[] = [
27-
{ x: 0, y: 0, w: 9, h: 6},
28-
{ x: 9, y: 0, w: 3, h: 3},
29-
{ x: 9, y: 3, w: 3, h: 3},
30-
];
31-
private grid: GridStack;
32-
33-
constructor() {}
34-
35-
// wait until after DOM is ready to init gridstack - can't be ngOnInit() as angular ngFor needs to run first!
36-
public ngAfterViewInit() {
37-
this.grid = GridStack.init({
38-
cellHeight: 70,
39-
});
40-
}
41-
42-
/**
43-
* this would be easier with addWidget(), removeWidget(), update() but simulating angular change detection instead...
44-
*/
45-
public add() {
46-
// this SHOULD trigger ngOnChanges() but not seeing it... and doing ngDoCheck() seem extreme ?
47-
// https://www.reddit.com/r/angular/comments/azjefs/change_detection_for_arraysobjects/
48-
// https://angular.io/guide/lifecycle-hooks#docheck
49-
this.items = [...this.items, {x: 1, y: 6, w: 3}];
50-
// this.items.push({x: 1, y: 6, w: 3});
51-
}
52-
public delete() {
53-
this.items.pop(); // todo
54-
}
55-
public change() {
56-
this.items[0].w = 1; // todo
57-
}
58-
59-
public ngOnChanges(changes: SimpleChanges) {
60-
if (changes.items) {
61-
// TODO: ... figure what is new and call makeWidget(), old -> removeWidget(el,false) and changed -> update()
62-
console.log('items changed');
63-
}
64-
}
65-
}
66-
61+
{ x: 0, y: 0, w: 1, h: 1 },
62+
{ x: 1, y: 1, w: 1, h: 1 },
63+
{ x: 2, y: 2, w: 1, h: 1 },
64+
];
65+
66+
private grid: GridStack;
67+
private widgetToMake: Subject<{
68+
action: "add" | "remove" | "update";
69+
id: number;
70+
}> = new Subject(); // consider to use a statemanagement like ngrx component store to do this
71+
72+
constructor() {}
73+
74+
// wait until after DOM is ready to init gridstack - can't be ngOnInit() as angular ngFor needs to run first!
75+
public ngAfterViewInit() {
76+
this.grid = GridStack.init({
77+
alwaysShowResizeHandle:
78+
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
79+
navigator.userAgent
80+
),
81+
margin: 5,
82+
float: true,
83+
});
84+
85+
// To sync dom manipulation done by Angular and widget manipulation done by gridstack we need to zip the observables
86+
zip(this.gridstackItems.changes, this.widgetToMake).subscribe(
87+
([changedWidget, widgetToMake]) => {
88+
if (widgetToMake.action === "add") {
89+
this.grid.makeWidget(`#${widgetToMake.id}`);
90+
} else if (widgetToMake.action === "remove") {
91+
const removeEl = this.grid
92+
.getGridItems()
93+
.find((el) => el.id === `${widgetToMake.id}`);
94+
this.grid.removeWidget(removeEl);
95+
}
96+
}
97+
);
98+
}
99+
100+
/**
101+
* CRUD operations
102+
*/
103+
public add() {
104+
this.items = [...this.items, { x: 3, y: 0, w: 3 }];
105+
this.widgetToMake.next({ action: "add", id: this.items.length - 1 });
106+
}
107+
108+
public delete() {
109+
this.items.pop();
110+
this.widgetToMake.next({ action: "remove", id: this.items.length });
111+
}
112+
113+
// a change of a widget doesn´t change to amount of items in ngFor therefore we don´t need to do it through the zip function above
114+
public change() {
115+
const updateEl = this.grid.getGridItems().find((el) => el.id === `${0}`);
116+
this.grid.update(updateEl, { w: 2 });
117+
}
118+
119+
identify(index: number) {
120+
return index;
121+
}
122+
}

0 commit comments

Comments
 (0)