33 * Copyright (c) 2022 Alain Dumesny - see GridStack root license
44 */
55
6- import { AfterContentInit , ChangeDetectionStrategy , Component , ContentChildren , ElementRef , EventEmitter , Input ,
7- NgZone , OnDestroy , OnInit , Output , QueryList , Type , ViewChild , ViewContainerRef , createComponent , EnvironmentInjector } from '@angular/core' ;
6+ import { AfterContentInit , Component , ContentChildren , ElementRef , EventEmitter , Input ,
7+ OnDestroy , OnInit , Output , QueryList , Type , ViewChild , ViewContainerRef , reflectComponentType } from '@angular/core' ;
88import { Subject } from 'rxjs' ;
99import { takeUntil } from 'rxjs/operators' ;
1010import { GridHTMLElement , GridItemHTMLElement , GridStack , GridStackNode , GridStackOptions , GridStackWidget } from 'gridstack' ;
@@ -17,7 +17,6 @@ export type elementCB = {event: Event, el: GridItemHTMLElement};
1717export type nodesCB = { event : Event , nodes : GridStackNode [ ] } ;
1818export type droppedCB = { event : Event , previousNode : GridStackNode , newNode : GridStackNode } ;
1919
20-
2120/** extends to store Ng Component selector, instead/inAddition to content */
2221export interface NgGridStackWidget extends GridStackWidget {
2322 type ?: string ; // component type to create as content
@@ -31,6 +30,7 @@ export interface GridCompHTMLElement extends GridHTMLElement {
3130 _gridComp ?: GridstackComponent ;
3231}
3332
33+ /** selector string to runtime Type mapping */
3434export type SelectorToType = { [ key : string ] : Type < Object > } ;
3535
3636/**
@@ -49,7 +49,7 @@ export type SelectorToType = {[key: string]: Type<Object>};
4949 styles : [ `
5050 :host { display: block; }
5151 ` ] ,
52- changeDetection : ChangeDetectionStrategy . OnPush ,
52+ // changeDetection: ChangeDetectionStrategy.OnPush, // IFF you want to optimize and control when ChangeDetection needs to happen...
5353} )
5454export class GridstackComponent implements OnInit , AfterContentInit , OnDestroy {
5555
@@ -97,8 +97,14 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
9797 * Unfortunately Ng doesn't provide public access to that mapping.
9898 */
9999 public static selectorToType : SelectorToType = { } ;
100- public static addSelector ( key : string , type : Type < Object > ) {
101- GridstackComponent . selectorToType [ key ] = type ;
100+ /** add a list of ng Component to be mapped to selector */
101+ public static addComponentToSelectorType ( typeList : Array < Type < Object > > ) {
102+ typeList . forEach ( type => GridstackComponent . selectorToType [ GridstackComponent . getSelector ( type ) ] = type ) ;
103+ }
104+ /** return the ng Component selector */
105+ public static getSelector ( type : Type < Object > ) : string {
106+ const mirror = reflectComponentType ( type ) ! ;
107+ return mirror . selector ;
102108 }
103109
104110 private _options ?: GridStackOptions ;
@@ -107,7 +113,8 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
107113 private ngUnsubscribe : Subject < void > = new Subject ( ) ;
108114
109115 constructor (
110- private readonly zone : NgZone ,
116+ // private readonly zone: NgZone,
117+ // private readonly cd: ChangeDetectorRef,
111118 private readonly elementRef : ElementRef < GridCompHTMLElement > ,
112119 ) {
113120 this . el . _gridComp = this ;
@@ -118,19 +125,19 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
118125 this . loaded = ! ! this . options ?. children ?. length ;
119126 this . _grid = GridStack . init ( this . _options , this . el ) ;
120127 delete this . _options ; // GS has it now
128+
129+ this . checkEmpty ( ) ;
121130 }
122131
123132 /** wait until after all DOM is ready to init gridstack children (after angular ngFor and sub-components run first) */
124133 public ngAfterContentInit ( ) : void {
125- this . zone . runOutsideAngular ( ( ) => {
126- // track whenever the children list changes and update the layout...
127- this . gridstackItems ?. changes
128- . pipe ( takeUntil ( this . ngUnsubscribe ) )
129- . subscribe ( ( ) => this . updateAll ( ) ) ;
130- // ...and do this once at least unless we loaded children already
131- if ( ! this . loaded ) this . updateAll ( ) ;
132- this . hookEvents ( this . grid ) ;
133- } ) ;
134+ // track whenever the children list changes and update the layout...
135+ this . gridstackItems ?. changes
136+ . pipe ( takeUntil ( this . ngUnsubscribe ) )
137+ . subscribe ( ( ) => this . updateAll ( ) ) ;
138+ // ...and do this once at least unless we loaded children already
139+ if ( ! this . loaded ) this . updateAll ( ) ;
140+ this . hookEvents ( this . grid ) ;
134141 }
135142
136143 public ngOnDestroy ( ) : void {
@@ -158,32 +165,35 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
158165 /** check if the grid is empty, if so show alternative content */
159166 public checkEmpty ( ) {
160167 if ( ! this . grid ) return ;
161- this . isEmpty = ! this . grid . engine . nodes . length ;
168+ const isEmpty = ! this . grid . engine . nodes . length ;
169+ if ( isEmpty === this . isEmpty ) return ;
170+ this . isEmpty = isEmpty ;
171+ // this.cd.detectChanges();
162172 }
163173
164174 /** get all known events as easy to use Outputs for convenience */
165175 private hookEvents ( grid ?: GridStack ) {
166176 if ( ! grid ) return ;
167177 grid
168- . on ( 'added' , ( event : Event , nodes : GridStackNode [ ] ) => this . zone . run ( ( ) => { this . checkEmpty ( ) ; this . addedCB . emit ( { event, nodes} ) ; } ) )
169- . on ( 'change' , ( event : Event , nodes : GridStackNode [ ] ) => this . zone . run ( ( ) => this . changeCB . emit ( { event, nodes} ) ) )
170- . on ( 'disable' , ( event : Event ) => this . zone . run ( ( ) => this . disableCB . emit ( { event} ) ) )
171- . on ( 'drag' , ( event : Event , el : GridItemHTMLElement ) => this . zone . run ( ( ) => this . dragCB . emit ( { event, el} ) ) )
172- . on ( 'dragstart' , ( event : Event , el : GridItemHTMLElement ) => this . zone . run ( ( ) => this . dragStartCB . emit ( { event, el} ) ) )
173- . on ( 'dragstop' , ( event : Event , el : GridItemHTMLElement ) => this . zone . run ( ( ) => this . dragStopCB . emit ( { event, el} ) ) )
174- . on ( 'dropped' , ( event : Event , previousNode : GridStackNode , newNode : GridStackNode ) => this . zone . run ( ( ) => this . droppedCB . emit ( { event, previousNode, newNode} ) ) )
175- . on ( 'enable' , ( event : Event ) => this . zone . run ( ( ) => this . enableCB . emit ( { event} ) ) )
176- . on ( 'removed' , ( event : Event , nodes : GridStackNode [ ] ) => this . zone . run ( ( ) => { this . checkEmpty ( ) ; this . removedCB . emit ( { event, nodes} ) ; } ) )
177- . on ( 'resize' , ( event : Event , el : GridItemHTMLElement ) => this . zone . run ( ( ) => this . resizeCB . emit ( { event, el} ) ) )
178- . on ( 'resizestart' , ( event : Event , el : GridItemHTMLElement ) => this . zone . run ( ( ) => this . resizeStartCB . emit ( { event, el} ) ) )
179- . on ( 'resizestop' , ( event : Event , el : GridItemHTMLElement ) => this . zone . run ( ( ) => this . resizeStopCB . emit ( { event, el} ) ) )
178+ . on ( 'added' , ( event : Event , nodes : GridStackNode [ ] ) => { this . checkEmpty ( ) ; this . addedCB . emit ( { event, nodes} ) ; } )
179+ . on ( 'change' , ( event : Event , nodes : GridStackNode [ ] ) => this . changeCB . emit ( { event, nodes} ) )
180+ . on ( 'disable' , ( event : Event ) => this . disableCB . emit ( { event} ) )
181+ . on ( 'drag' , ( event : Event , el : GridItemHTMLElement ) => this . dragCB . emit ( { event, el} ) )
182+ . on ( 'dragstart' , ( event : Event , el : GridItemHTMLElement ) => this . dragStartCB . emit ( { event, el} ) )
183+ . on ( 'dragstop' , ( event : Event , el : GridItemHTMLElement ) => this . dragStopCB . emit ( { event, el} ) )
184+ . on ( 'dropped' , ( event : Event , previousNode : GridStackNode , newNode : GridStackNode ) => this . droppedCB . emit ( { event, previousNode, newNode} ) )
185+ . on ( 'enable' , ( event : Event ) => this . enableCB . emit ( { event} ) )
186+ . on ( 'removed' , ( event : Event , nodes : GridStackNode [ ] ) => { this . checkEmpty ( ) ; this . removedCB . emit ( { event, nodes} ) ; } )
187+ . on ( 'resize' , ( event : Event , el : GridItemHTMLElement ) => this . resizeCB . emit ( { event, el} ) )
188+ . on ( 'resizestart' , ( event : Event , el : GridItemHTMLElement ) => this . resizeStartCB . emit ( { event, el} ) )
189+ . on ( 'resizestop' , ( event : Event , el : GridItemHTMLElement ) => this . resizeStopCB . emit ( { event, el} ) )
180190 }
181191}
182192
183193/**
184- * called by GS when a new item needs to be created, which we do as a Angular component, or deleted (skip)
194+ * can be used when a new item needs to be created, which we do as a Angular component, or deleted (skip)
185195 **/
186- function createNgComponents ( host : GridCompHTMLElement | HTMLElement , w : NgGridStackWidget | GridStackOptions , add : boolean , isGrid : boolean ) : HTMLElement | undefined {
196+ export function gsCreateNgComponents ( host : GridCompHTMLElement | HTMLElement , w : NgGridStackWidget | GridStackOptions , add : boolean , isGrid : boolean ) : HTMLElement | undefined {
187197 if ( add ) {
188198 if ( ! host ) return ;
189199 // create the grid item dynamically - see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
@@ -217,14 +227,10 @@ function createNgComponents(host: GridCompHTMLElement | HTMLElement, w: NgGridSt
217227}
218228
219229/**
220- * called when saving the grid - put the extra info of type, otherwise content
230+ * can be used when saving the grid - make sure we save the content from the field (not HTML as we get ng markups)
231+ * and can put the extra info of type, otherwise content
221232 */
222- function saveAdditionNgInfo ( n : NgGridStackNode , w : NgGridStackWidget ) {
223- // NOT needed as we get that by default in this case
224- // if (n.type) w.type = n.type;
225- // else if (n.content) w.content = n.content;
233+ export function gsSaveAdditionNgInfo ( n : NgGridStackNode , w : NgGridStackWidget ) {
234+ if ( n . type ) w . type = n . type ;
235+ else if ( n . content ) w . content = n . content ;
226236}
227-
228- // set globally our method to create the right widget type
229- GridStack . addRemoveCB = createNgComponents ;
230- GridStack . saveCB = saveAdditionNgInfo ;
0 commit comments