1
1
/**
2
2
* Example using Angular ngFor to loop through items and create DOM items
3
3
*/
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" ;
5
11
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 > ;
26
60
@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
+ / A n d r o i d | w e b O S | i P h o n e | i P a d | i P o d | B l a c k B e r r y | I E M o b i l e | O p e r a M i n i / 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