4
4
*/
5
5
6
6
import { AfterContentInit , Component , ContentChildren , ElementRef , EventEmitter , Input ,
7
- OnDestroy , OnInit , Output , QueryList , Type , ViewChild , ViewContainerRef , reflectComponentType } from '@angular/core' ;
7
+ OnDestroy , OnInit , Output , QueryList , Type , ViewChild , ViewContainerRef , reflectComponentType , ComponentRef } from '@angular/core' ;
8
8
import { Subject } from 'rxjs' ;
9
9
import { takeUntil } from 'rxjs/operators' ;
10
10
import { GridHTMLElement , GridItemHTMLElement , GridStack , GridStackNode , GridStackOptions , GridStackWidget } from 'gridstack' ;
11
11
12
12
import { GridItemCompHTMLElement , GridstackItemComponent } from './gridstack-item.component' ;
13
+ import { BaseWidget } from './base-widgets' ;
13
14
14
15
/** events handlers emitters signature for different events */
15
16
export type eventCB = { event : Event } ;
16
17
export type elementCB = { event : Event , el : GridItemHTMLElement } ;
17
18
export type nodesCB = { event : Event , nodes : GridStackNode [ ] } ;
18
19
export type droppedCB = { event : Event , previousNode : GridStackNode , newNode : GridStackNode } ;
19
20
21
+ export type NgCompInputs = { [ key : string ] : any } ;
22
+
20
23
/** extends to store Ng Component selector, instead/inAddition to content */
21
24
export interface NgGridStackWidget extends GridStackWidget {
22
- type ?: string ; // component type to create as content
25
+ selector ?: string ; // component type to create as content
26
+ input ?: NgCompInputs ; // serialized data for the component input fields
23
27
}
24
28
export interface NgGridStackNode extends GridStackNode {
25
- type ?: string ; // component type to create as content
29
+ selector ?: string ; // component type to create as content
26
30
}
27
31
export interface NgGridStackOptions extends GridStackOptions {
28
32
children ?: NgGridStackWidget [ ] ;
@@ -96,6 +100,9 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
96
100
/** return the GridStack class */
97
101
public get grid ( ) : GridStack | undefined { return this . _grid ; }
98
102
103
+ /** ComponentRef of ourself - used by dynamic object to correctly get removed */
104
+ public ref : ComponentRef < GridstackComponent > | undefined ;
105
+
99
106
/**
100
107
* stores the selector -> Type mapping, so we can create items dynamically from a string.
101
108
* Unfortunately Ng doesn't provide public access to that mapping.
@@ -107,8 +114,7 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
107
114
}
108
115
/** return the ng Component selector */
109
116
public static getSelector ( type : Type < Object > ) : string {
110
- const mirror = reflectComponentType ( type ) ! ;
111
- return mirror . selector ;
117
+ return reflectComponentType ( type ) ! . selector ;
112
118
}
113
119
114
120
private _options ?: GridStackOptions ;
@@ -145,6 +151,7 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
145
151
}
146
152
147
153
public ngOnDestroy ( ) : void {
154
+ delete this . ref ;
148
155
this . ngUnsubscribe . next ( ) ;
149
156
this . ngUnsubscribe . complete ( ) ;
150
157
this . grid ?. destroy ( ) ;
@@ -197,44 +204,84 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
197
204
/**
198
205
* can be used when a new item needs to be created, which we do as a Angular component, or deleted (skip)
199
206
**/
200
- export function gsCreateNgComponents ( host : GridCompHTMLElement | HTMLElement , w : NgGridStackWidget | GridStackOptions , add : boolean , isGrid : boolean ) : HTMLElement | undefined {
201
- // only care about creating ng components here...
202
- if ( ! add || ! host ) return ;
203
-
204
- // create the component dynamically - see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
205
- if ( isGrid ) {
206
- let grid : GridstackComponent | undefined ;
207
- const gridItemComp = ( host . parentElement as GridItemCompHTMLElement ) ?. _gridItemComp ;
208
- if ( gridItemComp ) {
209
- grid = gridItemComp . container ?. createComponent ( GridstackComponent ) ?. instance ;
210
- } else {
207
+ export function gsCreateNgComponents ( host : GridCompHTMLElement | HTMLElement , w : NgGridStackWidget | GridStackNode , add : boolean , isGrid : boolean ) : HTMLElement | undefined {
208
+ if ( add ) {
209
+ //
210
+ // create the component dynamically - see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
211
+ //
212
+ if ( ! host ) return ;
213
+ if ( isGrid ) {
214
+ const container = ( host . parentElement as GridItemCompHTMLElement ) ?. _gridItemComp ?. container ;
211
215
// TODO: figure out how to create ng component inside regular Div. need to access app injectors...
212
- // const hostElement: Element = host;
213
- // const environmentInjector: EnvironmentInjector;
214
- // grid = createComponent(GridstackComponent, {environmentInjector, hostElement})?.instance;
216
+ // if (!container) {
217
+ // const hostElement: Element = host;
218
+ // const environmentInjector: EnvironmentInjector;
219
+ // grid = createComponent(GridstackComponent, {environmentInjector, hostElement})?.instance;
220
+ // }
221
+ const gridRef = container ?. createComponent ( GridstackComponent ) ;
222
+ const grid = gridRef ?. instance ;
223
+ if ( ! grid ) return ;
224
+ grid . ref = gridRef ;
225
+ grid . options = w as GridStackOptions ;
226
+ return grid . el ;
227
+ } else {
228
+ const gridComp = ( host as GridCompHTMLElement ) . _gridComp ;
229
+ const gridItemRef = gridComp ?. container ?. createComponent ( GridstackItemComponent ) ;
230
+ const gridItem = gridItemRef ?. instance ;
231
+ if ( ! gridItem ) return ;
232
+ gridItem . ref = gridItemRef
233
+
234
+ // IFF we're not a subGrid, define what type of component to create as child, OR you can do it GridstackItemComponent template, but this is more generic
235
+ const selector = ( w as NgGridStackWidget ) . selector ;
236
+ const type = selector ? GridstackComponent . selectorToType [ selector ] : undefined ;
237
+ if ( ! w . subGridOpts && type ) {
238
+ const childWidget = gridItem . container ?. createComponent ( type ) ?. instance as BaseWidget ;
239
+ if ( typeof childWidget ?. serialize === 'function' && typeof childWidget ?. deserialize === 'function' ) {
240
+ // proper BaseWidget subclass, save it and load additional data
241
+ gridItem . childWidget = childWidget ;
242
+ childWidget . deserialize ( w ) ;
243
+ }
244
+ }
245
+
246
+ return gridItem . el ;
215
247
}
216
- if ( grid ) grid . options = w as GridStackOptions ;
217
- return grid ?. el ;
218
248
} else {
219
- const gridComp = ( host as GridCompHTMLElement ) . _gridComp ;
220
- const gridItem = gridComp ?. container ?. createComponent ( GridstackItemComponent ) ?. instance ;
221
-
222
- // IFF we're not a subGrid, define what type of component to create as child, OR you can do it GridstackItemComponent template, but this is more generic
223
- const selector = ( w as NgGridStackWidget ) . type ;
224
- const type = selector ? GridstackComponent . selectorToType [ selector ] : undefined ;
225
- if ( ! w . subGridOpts && type ) {
226
- gridItem ?. container ?. createComponent ( type ) ;
249
+ //
250
+ // REMOVE - have to call ComponentRef:destroy() for dynamic objects to correctly remove themselves
251
+ // Note: this will destroy all children dynamic components as well: gridItem -> childWidget
252
+ //
253
+ const n = w as GridStackNode ;
254
+ if ( isGrid ) {
255
+ const grid = ( n . el as GridCompHTMLElement ) ?. _gridComp ;
256
+ if ( grid ?. ref ) grid . ref . destroy ( ) ;
257
+ else grid ?. ngOnDestroy ( ) ;
258
+ } else {
259
+ const gridItem = ( n . el as GridItemCompHTMLElement ) ?. _gridItemComp ;
260
+ if ( gridItem ?. ref ) gridItem . ref . destroy ( ) ;
261
+ else gridItem ?. ngOnDestroy ( ) ;
227
262
}
228
-
229
- return gridItem ?. el ;
230
263
}
264
+ return ;
231
265
}
232
266
233
267
/**
234
- * can be used when saving the grid - make sure we save the content from the field (not HTML as we get ng markups)
235
- * and can put the extra info of type, otherwise content
268
+ * called for each item in the grid - check if additional information needs to be saved.
269
+ * Note: since this is options minus gridstack private members using Utils.removeInternalForSave(),
270
+ * this typically doesn't need to do anything. However your custom Component @Input() are now supported
271
+ * using BaseWidget.serialize()
236
272
*/
237
273
export function gsSaveAdditionalNgInfo ( n : NgGridStackNode , w : NgGridStackWidget ) {
238
- if ( n . type ) w . type = n . type ;
239
- else if ( n . content ) w . content = n . content ;
274
+ const gridItem = ( n . el as GridItemCompHTMLElement ) ?. _gridItemComp ;
275
+ if ( gridItem ) {
276
+ const input = gridItem . childWidget ?. serialize ( ) ;
277
+ if ( input ) {
278
+ w . input = input ;
279
+ }
280
+ return ;
281
+ }
282
+ // else check if Grid
283
+ const grid = ( n . el as GridCompHTMLElement ) ?. _gridComp ;
284
+ if ( grid ) {
285
+ //.... save any custom data
286
+ }
240
287
}
0 commit comments