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