Skip to content

Commit

Permalink
Angular demo update
Browse files Browse the repository at this point in the history
* show creating grid items content components on the fly by type (rather than hard code in template)
  • Loading branch information
adumesny committed Apr 8, 2023
1 parent 66baaf7 commit 4bc23c3
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 36 deletions.
10 changes: 5 additions & 5 deletions demo/angular/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component } from '@angular/core';
import { GridStackOptions, GridStackWidget } from 'gridstack';
import { GridstackComponent, elementCB, nodesCB } from './gridstack.component';
import { GridstackComponent, NgGridStackWidget, elementCB, nodesCB } from './gridstack.component';

// unique ids sets for each item for correct ngFor updating
let ids = 1;
Expand Down Expand Up @@ -30,8 +30,8 @@ export class AppComponent {
}

// nested grid options
public sub1: GridStackWidget[] = [ {x:0, y:0}, {x:1, y:0}, {x:2, y:0}, {x:3, y:0}, {x:0, y:1}, {x:1, y:1}];
public sub2: GridStackWidget[] = [ {x:0, y:0}, {x:0, y:1, w:2}];
public sub1: NgGridStackWidget[] = [ {x:0, y:0, type:'app-a'}, {x:1, y:0, type:'app-b'}, {x:2, y:0, type:'app-c'}, {x:3, y:0}, {x:0, y:1}, {x:1, y:1}];
public sub2: NgGridStackWidget[] = [ {x:0, y:0}, {x:0, y:1, w:2}];
public subOptions: GridStackOptions = {
cellHeight: 50, // should be 50 - top/bottom
column: 'auto', // size to match container. make sure to include gridstack-extra.min.css
Expand All @@ -54,8 +54,8 @@ export class AppComponent {

constructor() {
// give them content and unique id to make sure we track them during changes below...
[...this.items, ...this.sub1, ...this.sub2].forEach(w => {
w.content = `item ${ids}`;
[...this.items, ...this.sub1, ...this.sub2].forEach((w: NgGridStackWidget) => {
if (!w.type && !w.subGrid) w.content = `item ${ids}`;
w.id = String(ids++);
});
}
Expand Down
28 changes: 16 additions & 12 deletions demo/angular/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { GridstackItemComponent } from './gridstack-item.component';
import { GridstackComponent } from './gridstack.component';
import { AngularNgForTestComponent } from './ngFor';
import { AngularNgForCmdTestComponent } from './ngFor_cmd';
import { AngularSimpleComponent } from './simple';

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { GridstackItemComponent } from './gridstack-item.component';
import { GridstackComponent } from './gridstack.component';
import { AngularNgForTestComponent } from './ngFor';
import { AngularNgForCmdTestComponent } from './ngFor_cmd';
import { AngularSimpleComponent } from './simple';
import { AComponent, BComponent, CComponent } from './dummy.component';

@NgModule({
declarations: [
AngularNgForCmdTestComponent,
Expand All @@ -16,6 +17,9 @@ import { AngularSimpleComponent } from './simple';
AppComponent,
GridstackComponent,
GridstackItemComponent,
AComponent,
BComponent,
CComponent,
],
imports: [
BrowserModule
Expand All @@ -26,5 +30,5 @@ import { AngularSimpleComponent } from './simple';
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
})
export class AppModule { }
39 changes: 39 additions & 0 deletions demo/angular/src/app/dummy.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* gridstack.component.ts 7.3.0-dev
* Copyright (c) 2022 Alain Dumesny - see GridStack root license
*/

// dummy testing component that will be grid items content

import { Component, Type } from '@angular/core';

@Component({
selector: 'app-a',
template: 'Comp A',
})
export class AComponent {
}

@Component({
selector: 'app-b',
template: 'Comp B',
})
export class BComponent {
}

@Component({
selector: 'app-c',
template: 'Comp C',
})
export class CComponent {
}

/**
* stores the selector -> Type mapping, so we can create items dynamically from a string.
* Unfortunately Ng doesn't provide public access to that mapping.
*/
export const selectorToComponent: {[key: string]: Type<Object>} = {
'app-a': AComponent,
'app-b': BComponent,
'app-c': CComponent,
};
6 changes: 4 additions & 2 deletions demo/angular/src/app/gridstack-item.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ export interface GridItemCompHTMLElement extends GridItemHTMLElement {
selector: 'gridstack-item',
template: `
<div class="grid-stack-item-content">
<!-- this is where you would create the right component based on some internal type or id. doing .content for demo purpose -->
<!-- TODO: this is where you would create the right component based on some internal type or id IFF !this.options.subGrid
Doing options.content for demo purpose -->
{{options.content}}
<!-- any static (defined in dom) content goes here -->
<ng-content></ng-content>
<!-- where dynamic items go (like sub-grids) -->
<!-- where dynamic items go (like sub-grids, other dynamic NgComponents, etc...) -->
<ng-template #container></ng-template>
</div>`,
styles: [`
Expand Down
48 changes: 31 additions & 17 deletions demo/angular/src/app/gridstack.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@ import { takeUntil } from 'rxjs/operators';
import { GridHTMLElement, GridItemHTMLElement, GridStack, GridStackNode, GridStackOptions, GridStackWidget } from 'gridstack';

import { GridItemCompHTMLElement, GridstackItemComponent } from './gridstack-item.component';
import { selectorToComponent } from './dummy.component';

/** events handlers emitters signature for different events */
export type eventCB = {event: Event};
export type elementCB = {event: Event, el: GridItemHTMLElement};
export type nodesCB = {event: Event, nodes: GridStackNode[]};
export type droppedCB = {event: Event, previousNode: GridStackNode, newNode: GridStackNode};


/** extends to store Ng Component selector, instead/inAddition to content */
export interface NgGridStackWidget extends GridStackWidget {
type?: string; // component type to create as content
}

/** store element to Ng Class pointer back */
export interface GridCompHTMLElement extends GridHTMLElement {
_gridComp?: GridstackComponent;
Expand Down Expand Up @@ -96,7 +103,7 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
public ngOnInit(): void {
// inject our own addRemove so we can create GridItemComponent instead of simple divs
const opts: GridStackOptions = this._options || {};
opts.addRemoveCB = GridstackComponent._addRemoveCB;
opts.addRemoveCB = addRemoveCB;

// init ourself before any template children are created since we track them below anyway - no need to double create+update widgets
this.loaded = !!this.options?.children?.length;
Expand Down Expand Up @@ -162,24 +169,31 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
.on('resizestart', (event: Event, el: GridItemHTMLElement) => this.zone.run(() => this.resizeStartCB.emit({event, el})))
.on('resizestop', (event: Event, el: GridItemHTMLElement) => this.zone.run(() => this.resizeStopCB.emit({event, el})))
}
}

/** called by GS when a new item needs to be created, which we do as a Angular component, or deleted (skip) */
private static _addRemoveCB(parent: GridCompHTMLElement | HTMLElement, w: GridStackWidget | GridStackOptions, add: boolean, isGrid: boolean): HTMLElement | undefined {
if (add) {
if (!parent) return;
// create the grid item dynamically - see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
if (isGrid) {
const gridItemComp = (parent.parentElement as GridItemCompHTMLElement)._gridItemComp;
const grid = gridItemComp?.container?.createComponent(GridstackComponent)?.instance;
if (grid) grid.options = w as GridStackOptions;
return grid?.el;
} else {
// TODO: use GridStackWidget to define what type of component to create as child, or do it in GridstackItemComponent template...
const gridComp = (parent as GridCompHTMLElement)._gridComp;
const gridItem = gridComp?.container?.createComponent(GridstackItemComponent)?.instance;
return gridItem?.el;
/** called by GS when a new item needs to be created, which we do as a Angular component, or deleted (skip) */
function addRemoveCB(parent: GridCompHTMLElement | HTMLElement, w: NgGridStackWidget | GridStackOptions, add: boolean, isGrid: boolean): HTMLElement | undefined {
if (add) {
if (!parent) return;
// create the grid item dynamically - see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
if (isGrid) {
const gridItemComp = (parent.parentElement as GridItemCompHTMLElement)._gridItemComp;
const grid = gridItemComp?.container?.createComponent(GridstackComponent)?.instance;
if (grid) grid.options = w as GridStackOptions;
return grid?.el;
} else {
const gridComp = (parent as GridCompHTMLElement)._gridComp;
const gridItem = gridComp?.container?.createComponent(GridstackItemComponent)?.instance;

// 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
const type = (w as NgGridStackWidget).type;
if (!w.subGrid && type && selectorToComponent[type]) {
gridItem?.container?.createComponent(selectorToComponent[type]);
}

return gridItem?.el;
}
return;
}
return;
}

0 comments on commit 4bc23c3

Please sign in to comment.