Skip to content

Commit 2e75c32

Browse files
authored
Merge pull request #2827 from adumesny/master
angular support for lazyLoad + demo
2 parents e0403fd + 409c73b commit 2e75c32

File tree

10 files changed

+62
-26
lines changed

10 files changed

+62
-26
lines changed

angular/projects/demo/src/app/app.component.html

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
<button (click)="onShow(5)" [class.active]="show===5">Component Dynamic</button>
1010
<button (click)="onShow(6)" [class.active]="show===6">Nested Grid</button>
1111
<button (click)="onShow(7)" [class.active]="show===7">Two Grids + sidebar</button>
12-
<button (click)="onShow(8)" [class.active]="show===8">Leak Test</button>
12+
<button (click)="onShow(8)" [class.active]="show===8">Lazy Load</button>
13+
<button (click)="onShow(9)" [class.active]="show===9">Leak Test</button>
1314
</div>
1415

1516
<div class="test-container">
@@ -99,6 +100,13 @@
99100
</div>
100101

101102
<div *ngIf="show===8">
103+
<p>delay loading of components</p>
104+
<div style="height: 120px; overflow-y: auto">
105+
<gridstack [options]="gridOptionsDelay"></gridstack>
106+
</div>
107+
</div>
108+
109+
<div *ngIf="show===9">
102110
<p>load() + clear() to memory leak test between the two</p>
103111
<button (click)="clearGrid()">Clear</button>
104112
<button (click)="load(sub0)">load</button>

angular/projects/demo/src/app/app.component.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ export class AppComponent implements OnInit {
4646
children: this.sub0,
4747
}
4848

49+
public lazyChildren: NgGridStackWidget[] = [];
50+
public gridOptionsDelay: NgGridStackOptions = {
51+
...this.gridOptions,
52+
lazyLoad: true,
53+
children: this.lazyChildren,
54+
}
55+
4956
// nested grid options
5057
private subOptions: GridStackOptions = {
5158
cellHeight: 50, // should be 50 - top/bottom
@@ -99,6 +106,8 @@ export class AppComponent implements OnInit {
99106
];
100107

101108
constructor() {
109+
for (let y = 0; y <= 5; y++) this.lazyChildren.push({x:0, y, id:String(y), selector: y%2 ? 'app-b' : 'app-a'});
110+
102111
// give them content and unique id to make sure we track them during changes below...
103112
[...this.items, ...this.subChildren, ...this.sub1, ...this.sub2, ...this.sub0].forEach((w: NgGridStackWidget) => {
104113
if (!w.selector && !w.content && !w.subGridOpts) w.content = `item ${ids++}`;

angular/projects/demo/src/app/dummy.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { BaseWidget, NgCompInputs } from 'gridstack/dist/angular';
1919
export class AComponent extends BaseWidget implements OnDestroy {
2020
@Input() text: string = 'foo'; // test custom input data
2121
public override serialize(): NgCompInputs | undefined { return this.text ? {text: this.text} : undefined; }
22+
constructor() { super(); console.log('Comp A created'); }
2223
ngOnDestroy() { console.log('Comp A destroyed'); } // test to make sure cleanup happens
2324
}
2425

@@ -27,6 +28,7 @@ export class AComponent extends BaseWidget implements OnDestroy {
2728
template: 'Comp B',
2829
})
2930
export class BComponent extends BaseWidget implements OnDestroy {
31+
constructor() { super(); console.log('Comp B created'); }
3032
ngOnDestroy() { console.log('Comp B destroyed'); }
3133
}
3234

angular/projects/lib/src/lib/gridstack-item.component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ export class GridstackItemComponent implements OnDestroy {
4444

4545
/** list of options for creating/updating this item */
4646
@Input() public set options(val: GridStackNode) {
47-
if (this.el.gridstackNode?.grid) {
47+
const grid = this.el.gridstackNode?.grid;
48+
if (grid) {
4849
// already built, do an update...
49-
this.el.gridstackNode.grid.update(this.el, val);
50+
grid.update(this.el, val);
5051
} else {
5152
// store our custom element in options so we can update it and not re-create a generic div!
5253
this._options = {...val, el: this.el};

angular/projects/lib/src/lib/gridstack.component.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
211211
/**
212212
* can be used when a new item needs to be created, which we do as a Angular component, or deleted (skip)
213213
**/
214-
export function gsCreateNgComponents(host: GridCompHTMLElement | HTMLElement, w: NgGridStackWidget | GridStackNode, add: boolean, isGrid: boolean): HTMLElement | undefined {
214+
export function gsCreateNgComponents(host: GridCompHTMLElement | HTMLElement, n: NgGridStackNode, add: boolean, isGrid: boolean): HTMLElement | undefined {
215215
if (add) {
216216
//
217217
// create the component dynamically - see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
@@ -225,15 +225,15 @@ export function gsCreateNgComponents(host: GridCompHTMLElement | HTMLElement, w:
225225
// grid = createComponent(GridstackComponent, {environmentInjector, hostElement})?.instance;
226226
// }
227227

228-
const gridItemCom = (host.parentElement as GridItemCompHTMLElement)?._gridItemComp;
229-
if (!gridItemCom) return;
228+
const gridItemComp = (host.parentElement as GridItemCompHTMLElement)?._gridItemComp;
229+
if (!gridItemComp) return;
230230
// check if gridItem has a child component with 'container' exposed to create under..
231-
const container = (gridItemCom.childWidget as any)?.container || gridItemCom.container;
231+
const container = (gridItemComp.childWidget as any)?.container || gridItemComp.container;
232232
const gridRef = container?.createComponent(GridstackComponent);
233233
const grid = gridRef?.instance;
234234
if (!grid) return;
235235
grid.ref = gridRef;
236-
grid.options = w as GridStackOptions;
236+
grid.options = n;
237237
return grid.el;
238238
} else {
239239
const gridComp = (host as GridCompHTMLElement)._gridComp;
@@ -243,15 +243,30 @@ export function gsCreateNgComponents(host: GridCompHTMLElement | HTMLElement, w:
243243
gridItem.ref = gridItemRef
244244

245245
// define what type of component to create as child, OR you can do it GridstackItemComponent template, but this is more generic
246-
const selector = (w as NgGridStackWidget).selector;
246+
const selector = n.selector;
247247
const type = selector ? GridstackComponent.selectorToType[selector] : undefined;
248248
if (type) {
249-
const childWidget = gridItem.container?.createComponent(type)?.instance as BaseWidget;
250-
// if proper BaseWidget subclass, save it and load additional data
251-
if (childWidget && typeof childWidget.serialize === 'function' && typeof childWidget.deserialize === 'function') {
252-
gridItem.childWidget = childWidget;
253-
childWidget.deserialize(w);
249+
// shared code to create our selector component
250+
const createComp = () => {
251+
const childWidget = gridItem.container?.createComponent(type)?.instance as BaseWidget;
252+
// if proper BaseWidget subclass, save it and load additional data
253+
if (childWidget && typeof childWidget.serialize === 'function' && typeof childWidget.deserialize === 'function') {
254+
gridItem.childWidget = childWidget;
255+
childWidget.deserialize(n);
256+
}
254257
}
258+
259+
const lazyLoad = n.lazyLoad || n.grid?.opts?.lazyLoad && n.lazyLoad !== false;
260+
if (lazyLoad) {
261+
if (!n.visibleObservable) {
262+
n.visibleObservable = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) {
263+
n.visibleObservable?.disconnect();
264+
delete n.visibleObservable;
265+
createComp();
266+
}});
267+
window.setTimeout(() => n.visibleObservable?.observe(gridItem.el)); // wait until callee sets position attributes
268+
}
269+
} else createComp();
255270
}
256271

257272
return gridItem.el;
@@ -261,7 +276,6 @@ export function gsCreateNgComponents(host: GridCompHTMLElement | HTMLElement, w:
261276
// REMOVE - have to call ComponentRef:destroy() for dynamic objects to correctly remove themselves
262277
// Note: this will destroy all children dynamic components as well: gridItem -> childWidget
263278
//
264-
const n = w as GridStackNode;
265279
if (isGrid) {
266280
const grid = (n.el as GridCompHTMLElement)?._gridComp;
267281
if (grid?.ref) grid.ref.destroy();

demo/lazy_load.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ <h1>Lazy loading demo</h1>
2525
console.log('created node id ', w.id);
2626
};
2727
let children = [];
28-
for (let y = 1; y <= 5; y++) children.push({x:0, y, id:y, content: String(y)});
28+
for (let y = 0; y <= 5; y++) children.push({x:0, y, id:String(y), content: String(y)});
2929
let grid = GridStack.init({
3030
cellHeight: 200,
3131
children,

doc/CHANGES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ Change log
119119
* fix: [#2231](https://github.com/gridstack/gridstack.js/bug/2231),[#1840](https://github.com/gridstack/gridstack.js/bug/1840),[#2354](https://github.com/gridstack/gridstack.js/bug/2354)
120120
big overall to how we do sidepanel drag&drop helper. see release notes.
121121
* feat: [#2818](https://github.com/gridstack/gridstack.js/pull/2818) support for Angular Component hosting true sub-grids (that size according to parent) without requring them to be only child of grid-item-content.
122-
* feat: Lazy loading of widget content until visible (`GridStackOptions.lazyLoad` and `GridStackWidget.lazyLoad`)
122+
* feat: [#2826](https://github.com/gridstack/gridstack.js/pull/2826) Lazy loading of widget content until visible (`GridStackOptions.lazyLoad` and `GridStackWidget.lazyLoad`)
123123

124124
## 10.3.1 (2024-07-21)
125125
* fix: [#2734](https://github.com/gridstack/gridstack.js/bug/2734) rotate() JS error

doc/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ gridstack.js API
1010
- [Breakpoint](#breakpoint)
1111
- [DDDragOpt](#dddragopt)
1212
- [Grid attributes](#grid-attributes)
13-
- [Item Options](#item-options)
13+
- [Item Options - GridStackWidget](#item-options---gridstackwidget)
1414
- [Item attributes](#item-attributes)
1515
- [Events](#events)
1616
- [added(event, items)](#addedevent-items)

src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,8 @@ export interface GridStackNode extends GridStackWidget {
418418
grid?: GridStack;
419419
/** actual sub-grid instance */
420420
subGrid?: GridStack;
421+
/** allow delay creation when visible */
422+
visibleObservable?: IntersectionObserver;
421423
/** @internal internal id used to match when cloning engines or saving column layouts */
422424
_id?: number;
423425
/** @internal does the node attr ned to be updated due to changed x,y,w,h values */
@@ -452,6 +454,4 @@ export interface GridStackNode extends GridStackWidget {
452454
_removeDOM?: boolean;
453455
/** @internal had drag&drop been initialized */
454456
_initDD?: boolean;
455-
/** @internal allow delay creation when visible */
456-
_visibleObservable?: IntersectionObserver;
457457
}

src/utils.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,14 @@ export class Utils {
117117

118118
const lazyLoad = n.lazyLoad || n.grid?.opts?.lazyLoad && n.lazyLoad !== false;
119119
if (lazyLoad) {
120-
n._visibleObservable = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) {
121-
n._visibleObservable.disconnect();
122-
delete n._visibleObservable;
123-
GridStack.renderCB(cont, n)
124-
}});
125-
window.setTimeout(() => n._visibleObservable.observe(el)); // wait until callee sets position attributes
120+
if (!n.visibleObservable) {
121+
n.visibleObservable = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) {
122+
n.visibleObservable?.disconnect();
123+
delete n.visibleObservable;
124+
GridStack.renderCB(cont, n)
125+
}});
126+
window.setTimeout(() => n.visibleObservable?.observe(el)); // wait until callee sets position attributes
127+
}
126128
} else GridStack.renderCB(cont, n);
127129

128130
return el;

0 commit comments

Comments
 (0)