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+ / 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