diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index aa1a527fa..5bde19c96 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -1,25 +1,25 @@
+import { HomeComponent } from './examples/home.component';
import { GridAddItemComponent } from './examples/grid-additem.component';
-import { GridMenuComponent } from './examples/grid-menu.component';
+import { GridAngularComponent } from './examples/grid-angular.component';
import { GridBasicComponent } from './examples/grid-basic.component';
import { GridClientSideComponent } from './examples/grid-clientside.component';
import { GridColspanComponent } from './examples/grid-colspan.component';
import { GridDraggableGroupingComponent } from './examples/grid-draggrouping.component';
import { GridEditorComponent } from './examples/grid-editor.component';
-import { GridEditorAngularComponent } from './examples/grid-editor-angular.component';
import { GridFormatterComponent } from './examples/grid-formatter.component';
import { GridFrozenComponent } from './examples/grid-frozen.component';
+import { GridGraphqlComponent } from './examples/grid-graphql.component';
import { GridGroupingComponent } from './examples/grid-grouping.component';
import { GridHeaderButtonComponent } from './examples/grid-headerbutton.component';
import { GridHeaderMenuComponent } from './examples/grid-headermenu.component';
import { GridLocalizationComponent } from './examples/grid-localization.component';
+import { GridMenuComponent } from './examples/grid-menu.component';
import { GridOdataComponent } from './examples/grid-odata.component';
-import { GridGraphqlComponent } from './examples/grid-graphql.component';
import { GridRemoteComponent } from './examples/grid-remote.component';
import { GridRowDetailComponent } from './examples/grid-rowdetail.component';
import { GridRowMoveComponent } from './examples/grid-rowmove.component';
import { GridRowSelectionComponent } from './examples/grid-rowselection.component';
import { GridStateComponent } from './examples/grid-state.component';
-import { HomeComponent } from './examples/home.component';
import { SwtCommonGridTestComponent } from './examples/swt-common-grid-test.component';
import { NgModule } from '@angular/core';
@@ -28,11 +28,11 @@ import { TranslateModule } from '@ngx-translate/core';
const routes: Routes = [
{ path: 'home', component: HomeComponent },
+ { path: 'angular-components', component: GridAngularComponent },
{ path: 'additem', component: GridAddItemComponent },
{ path: 'basic', component: GridBasicComponent },
{ path: 'colspan', component: GridColspanComponent },
{ path: 'editor', component: GridEditorComponent },
- { path: 'editor-angular', component: GridEditorAngularComponent },
{ path: 'formatter', component: GridFormatterComponent },
{ path: 'frozen', component: GridFrozenComponent },
{ path: 'headerbutton', component: GridHeaderButtonComponent },
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 5e436a0c4..c6daf9ab5 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -92,7 +92,7 @@
21- Row Detail View
- 22- Editors Angular Components
+ 22- Editors Angular Components
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 1335896b9..9f743d8f4 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -9,6 +9,7 @@ import { TranslateModule, TranslateLoader, TranslateService } from '@ngx-transla
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { AppComponent } from './app.component';
+import { CustomTitleFormatterComponent } from './examples/custom-titleFormatter.component';
import { EditorNgSelectComponent } from './examples/editor-ng-select.component';
import { GridAddItemComponent } from './examples/grid-additem.component';
import { GridBasicComponent } from './examples/grid-basic.component';
@@ -16,7 +17,7 @@ import { GridClientSideComponent } from './examples/grid-clientside.component';
import { GridColspanComponent } from './examples/grid-colspan.component';
import { GridDraggableGroupingComponent } from './examples/grid-draggrouping.component';
import { GridEditorComponent } from './examples/grid-editor.component';
-import { GridEditorAngularComponent } from './examples/grid-editor-angular.component';
+import { GridAngularComponent } from './examples/grid-angular.component';
import { GridFormatterComponent } from './examples/grid-formatter.component';
import { GridFrozenComponent } from './examples/grid-frozen.component';
import { GridGraphqlComponent } from './examples/grid-graphql.component';
@@ -71,14 +72,15 @@ export function appInitializerFactory(translate: TranslateService, injector: Inj
@NgModule({
declarations: [
AppComponent,
+ CustomTitleFormatterComponent,
EditorNgSelectComponent,
GridAddItemComponent,
+ GridAngularComponent,
GridBasicComponent,
GridClientSideComponent,
GridColspanComponent,
GridDraggableGroupingComponent,
GridEditorComponent,
- GridEditorAngularComponent,
GridFormatterComponent,
GridFrozenComponent,
GridGraphqlComponent,
@@ -125,6 +127,7 @@ export function appInitializerFactory(translate: TranslateService, injector: Inj
],
entryComponents: [
// dynamically created components
+ CustomTitleFormatterComponent,
EditorNgSelectComponent,
RowDetailPreloadComponent,
RowDetailViewComponent,
diff --git a/src/app/examples/custom-angularComponentEditor.ts b/src/app/examples/custom-angularComponentEditor.ts
index 5bb683b47..e40d34418 100644
--- a/src/app/examples/custom-angularComponentEditor.ts
+++ b/src/app/examples/custom-angularComponentEditor.ts
@@ -5,6 +5,7 @@ import {
Editor,
EditorValidator,
EditorValidatorOutput,
+ GridOption,
} from './../modules/angular-slickgrid';
/*
@@ -21,13 +22,21 @@ export class CustomAngularComponentEditor implements Editor {
/** default item object */
defaultItem: any;
+ /** SlickGrid grid object */
+ grid: any;
+
constructor(private args: any) {
+ this.grid = args && args.grid;
this.init();
}
- /** Angular Util Service */
+ /** Angular Util Service (could be inside the Grid Options Params or the Editor Params ) */
get angularUtilService(): AngularUtilService {
- return this.columnDef && this.columnDef && this.columnDef.internalColumnEditor && this.columnDef.internalColumnEditor.params.angularUtilService;
+ let angularUtilService = this.gridOptions && this.gridOptions.params && this.gridOptions.params.angularUtilService;
+ if (!angularUtilService || !(angularUtilService instanceof AngularUtilService)) {
+ angularUtilService = this.columnEditor && this.columnEditor.params && this.columnEditor.params.angularUtilService;
+ }
+ return angularUtilService;
}
/** Get the Collection */
@@ -45,8 +54,13 @@ export class CustomAngularComponentEditor implements Editor {
return this.columnDef && this.columnDef.internalColumnEditor || {};
}
+ /** Getter for the Grid Options pulled through the Grid Object */
+ get gridOptions(): GridOption {
+ return (this.grid && this.grid.getOptions) ? this.grid.getOptions() : {};
+ }
+
get hasAutoCommitEdit() {
- return this.args.grid.getOptions().autoCommitEdit;
+ return this.gridOptions.autoCommitEdit;
}
/** Get the Validator function, can be passed in Editor property or Column Definition */
@@ -55,12 +69,15 @@ export class CustomAngularComponentEditor implements Editor {
}
init() {
- if (!this.columnEditor || !this.columnEditor.params.component) {
+ if (!this.columnEditor || !this.columnEditor.params.component || !(this.angularUtilService instanceof AngularUtilService)) {
throw new Error(`[Angular-Slickgrid] For the Editors.angularComponent to work properly, you need to provide your component to the "component" property and make sure to add it to your "entryComponents" array.
- Example: this.columnDefs = [{ id: 'title', field: 'title', editor: { component: MyComponent, model: Editors.angularComponent, collection: [...] },`);
+ You also need to provide the "AngularUtilService" via the Editor Params OR the Grid Options Params
+ Example: this.columnDefs = [{ id: 'title', field: 'title', editor: { model: CustomAngularComponentEditor, collection: [...] }, params: { component: MyComponent, angularUtilService: this.angularUtilService }];
+ OR this.columnDefs = [{ id: 'title', field: 'title', editor: { model: CustomAngularComponentEditor, collection: [...] }]; this.gridOptions = { params: { angularUtilService: this.angularUtilService }}`);
}
if (this.columnEditor && this.columnEditor.params.component) {
- this.componentRef = this.columnEditor.params.angularUtilService.createAngularComponentAppendToDom(this.columnEditor.params.component, this.args.container);
+ const componentOutput = this.angularUtilService.createAngularComponentAppendToDom(this.columnEditor.params.component, this.args.container);
+ this.componentRef = componentOutput && componentOutput.componentRef;
Object.assign(this.componentRef.instance, { collection: this.collection });
this.componentRef.instance.onModelChanged.subscribe((item) => {
diff --git a/src/app/examples/custom-titleFormatter.component.ts b/src/app/examples/custom-titleFormatter.component.ts
new file mode 100644
index 000000000..db318fd64
--- /dev/null
+++ b/src/app/examples/custom-titleFormatter.component.ts
@@ -0,0 +1,8 @@
+import { Component } from '@angular/core';
+
+@Component({
+ template: `{{item?.assignee?.name}}`
+})
+export class CustomTitleFormatterComponent {
+ item: any;
+}
diff --git a/src/app/examples/grid-editor-angular.component.html b/src/app/examples/grid-angular.component.html
similarity index 100%
rename from src/app/examples/grid-editor-angular.component.html
rename to src/app/examples/grid-angular.component.html
diff --git a/src/app/examples/grid-editor-angular.component.ts b/src/app/examples/grid-angular.component.ts
similarity index 73%
rename from src/app/examples/grid-editor-angular.component.ts
rename to src/app/examples/grid-angular.component.ts
index b4f414470..883ffe8ee 100644
--- a/src/app/examples/grid-editor-angular.component.ts
+++ b/src/app/examples/grid-angular.component.ts
@@ -1,5 +1,4 @@
-import { Component, Injectable, OnInit } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
+import { Component, Injectable, OnInit, EmbeddedViewRef } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import {
AngularGridInstance,
@@ -15,24 +14,29 @@ import {
} from './../modules/angular-slickgrid';
import { EditorNgSelectComponent } from './editor-ng-select.component';
import { CustomAngularComponentEditor } from './custom-angularComponentEditor';
+import { CustomTitleFormatterComponent } from './custom-titleFormatter.component';
// using external non-typed js libraries
declare var Slick: any;
+declare var $: any;
const NB_ITEMS = 100;
@Component({
- templateUrl: './grid-editor-angular.component.html'
+ templateUrl: './grid-angular.component.html'
})
@Injectable()
-export class GridEditorAngularComponent implements OnInit {
- title = 'Example 22: Editors with Angular Components';
+export class GridAngularComponent implements OnInit {
+ title = 'Example 22: Multiple Angular Components';
subTitle = `
- Grid with Inline Editors and onCellClick actions (Wiki docs).
+ Grid with usage of Angular Components as Editor & AsyncPostRender (similar to Formatter).
- - Support of Angular Component as Custom Editor (click on "Assignee" column)
- - The column "Assignee" shown below uses ng-select as a custom editor with Angular Component
-
- Increased rowHeight to 45 so that the "ng-select" fits in the cell. Ideally it would be better to override the ng-select component styling to change it's max height
+ - Support of Angular Component as Custom Editor (click on any "Assignee" name cell)
+
+ - That column uses ng-select as a custom editor as an Angular Component
+
- Increased Grid Options "rowHeight" to 45 so that the "ng-select" fits in the cell. Ideally it would be better to override the ng-select css styling to change it's max height
+
+ - The 2nd "Assignee" column (showing in bold text) uses "asyncPostRender" with an Angular Component
`;
@@ -52,7 +56,7 @@ export class GridEditorAngularComponent implements OnInit {
{ id: '3', name: 'Paul' },
];
- constructor(private angularUtilService: AngularUtilService, private http: HttpClient, private translate: TranslateService) {}
+ constructor(private angularUtilService: AngularUtilService, private translate: TranslateService) {}
ngOnInit(): void {
this.prepareGrid();
@@ -90,14 +94,13 @@ export class GridEditorAngularComponent implements OnInit {
type: FieldType.string,
formatter: Formatters.complexObject,
params: {
- complexField: 'assignee.name'
+ complexField: 'assignee.name',
},
exportWithFormatter: true,
editor: {
model: CustomAngularComponentEditor,
collection: this.assignees,
params: {
- angularUtilService: this.angularUtilService,
component: EditorNgSelectComponent,
}
},
@@ -106,19 +109,26 @@ export class GridEditorAngularComponent implements OnInit {
this.alertWarning = `Updated Title: ${args.dataContext.title}`;
}
}, {
- id: 'duration',
- name: 'Duration (days)',
- field: 'duration',
+ id: 'assignee2',
+ name: 'Assignee with Angular Component',
+ field: 'assignee',
minWidth: 100,
filterable: true,
sortable: true,
- type: FieldType.number,
- filter: { model: Filters.slider, params: { hideSliderNumber: false } },
- editor: {
- model: Editors.slider,
- minValue: 0,
- maxValue: 100,
- }
+ type: FieldType.string,
+
+ // loading formatter, text to display while Post Render gets processed
+ formatter: () => '...',
+
+ // to load an Angular Component, you cannot use a Formatter since Angular needs at least 1 cycle to render everything
+ // you can use a PostRenderer but you will visually see the data appearing,
+ // which is why it's still better to use regular Formatter (with jQuery if need be) instead of Angular Component
+ asyncPostRender: this.renderAngularComponent.bind(this),
+ params: {
+ component: CustomTitleFormatterComponent,
+ angularUtilService: this.angularUtilService,
+ },
+ exportWithFormatter: true,
}, {
id: 'complete',
name: '% Complete',
@@ -201,11 +211,16 @@ export class GridEditorAngularComponent implements OnInit {
enableColumnPicker: true,
enableExcelCopyBuffer: true,
enableFiltering: true,
+ enableAsyncPostRender: true, // for the Angular PostRenderer, don't forget to enable it
+ asyncPostRenderDelay: 0, // also make sure to remove any delay to render it
editCommandHandler: (item, column, editCommand) => {
this._commandQueue.push(editCommand);
editCommand.execute();
},
- i18n: this.translate
+ i18n: this.translate,
+ params: {
+ angularUtilService: this.angularUtilService // provide the service to all at once (Editor, Filter, AsyncPostRender)
+ }
};
this.dataset = this.mockData(NB_ITEMS);
@@ -283,4 +298,14 @@ export class GridEditorAngularComponent implements OnInit {
this.gridObj.gotoCell(command.row, command.cell, false);
}
}
+
+ renderAngularComponent(cellNode: HTMLElement, row: number, dataContext: any, colDef: Column) {
+ if (colDef.params.component) {
+ const componentOutput = this.angularUtilService.createAngularComponent(colDef.params.component);
+ Object.assign(componentOutput.componentRef.instance, { item: dataContext });
+
+ // use a delay to make sure Angular ran at least a full cycle and it finished rendering the Component
+ setTimeout(() => $(cellNode).empty().html(componentOutput.domElement));
+ }
+ }
}
diff --git a/src/app/modules/angular-slickgrid/extensions/rowDetailViewExtension.ts b/src/app/modules/angular-slickgrid/extensions/rowDetailViewExtension.ts
index ebc97fcbd..ea51e1f3e 100644
--- a/src/app/modules/angular-slickgrid/extensions/rowDetailViewExtension.ts
+++ b/src/app/modules/angular-slickgrid/extensions/rowDetailViewExtension.ts
@@ -324,12 +324,14 @@ export class RowDetailViewExtension implements Extension {
private renderViewModel(item: any) {
const containerElements = document.getElementsByClassName(`${ROW_DETAIL_CONTAINER_PREFIX}${item.id}`);
if (containerElements && containerElements.length) {
- const compRef = this.angularUtilService.createAngularComponentAppendToDom(this._viewComponent, containerElements[0]);
- Object.assign(compRef.instance, { model: item });
+ const componentOutput = this.angularUtilService.createAngularComponentAppendToDom(this._viewComponent, containerElements[0]);
+ if (componentOutput && componentOutput.componentRef && componentOutput.componentRef.instance) {
+ Object.assign(componentOutput.componentRef.instance, { model: item });
- const viewObj = this._views.find((obj) => obj.id === item.id);
- if (viewObj) {
- viewObj.componentRef = compRef;
+ const viewObj = this._views.find((obj) => obj.id === item.id);
+ if (viewObj) {
+ viewObj.componentRef = componentOutput.componentRef;
+ }
}
}
}
diff --git a/src/app/modules/angular-slickgrid/models/angularComponentOutput.interface.ts b/src/app/modules/angular-slickgrid/models/angularComponentOutput.interface.ts
new file mode 100644
index 000000000..7a225a636
--- /dev/null
+++ b/src/app/modules/angular-slickgrid/models/angularComponentOutput.interface.ts
@@ -0,0 +1,6 @@
+import { ComponentRef } from '@angular/core';
+
+export interface AngularComponentOutput {
+ componentRef: ComponentRef;
+ domElement: HTMLElement;
+}
diff --git a/src/app/modules/angular-slickgrid/models/index.ts b/src/app/modules/angular-slickgrid/models/index.ts
index 285c508a2..f99cd7c0f 100644
--- a/src/app/modules/angular-slickgrid/models/index.ts
+++ b/src/app/modules/angular-slickgrid/models/index.ts
@@ -1,4 +1,5 @@
export * from './aggregator.interface';
+export * from './angularComponentOutput.interface';
export * from './angularGridInstance.interface';
export * from './autoResizeOption.interface';
export * from './backendService.interface';
diff --git a/src/app/modules/angular-slickgrid/services/angularUtilService.ts b/src/app/modules/angular-slickgrid/services/angularUtilService.ts
index 1b23f170e..e787ba099 100644
--- a/src/app/modules/angular-slickgrid/services/angularUtilService.ts
+++ b/src/app/modules/angular-slickgrid/services/angularUtilService.ts
@@ -1,3 +1,4 @@
+import { AngularComponentOutput } from './../models/angularComponentOutput.interface';
import { ApplicationRef, ComponentFactoryResolver, ComponentRef, EmbeddedViewRef, Injectable, Injector } from '@angular/core';
@Injectable()
@@ -9,7 +10,7 @@ export class AngularUtilService {
) { }
// ref https://hackernoon.com/angular-pro-tip-how-to-dynamically-create-components-in-body-ba200cc289e6
- createAngularComponentAppendToDom(component: any, targetElement?: HTMLElement | Element): ComponentRef {
+ createAngularComponent(component: any): AngularComponentOutput {
// Create a component reference from the component
const componentRef = this.compFactoryResolver
.resolveComponentFactory(component)
@@ -19,16 +20,26 @@ export class AngularUtilService {
this.appRef.attachView(componentRef.hostView);
// Get DOM element from component
- const domElem = (componentRef.hostView as EmbeddedViewRef)
- .rootNodes[0] as HTMLElement;
+ let domElem;
+ const viewRef = (componentRef.hostView as EmbeddedViewRef);
+ if (viewRef && Array.isArray(viewRef.rootNodes) && viewRef.rootNodes[0]) {
+ domElem = viewRef.rootNodes[0] as HTMLElement;
+ }
+
+ return { componentRef, domElement: domElem };
+ }
+
+ // ref https://hackernoon.com/angular-pro-tip-how-to-dynamically-create-components-in-body-ba200cc289e6
+ createAngularComponentAppendToDom(component: any, targetElement?: HTMLElement | Element): AngularComponentOutput {
+ const componentOutput = this.createAngularComponent(component);
// Append DOM element to the HTML element specified
if (targetElement && targetElement.appendChild) {
- targetElement.appendChild(domElem);
+ targetElement.appendChild(componentOutput.domElement);
} else {
- document.body.appendChild(domElem); // when no target provided, we'll simply add it to the HTML Body
+ document.body.appendChild(componentOutput.domElement); // when no target provided, we'll simply add it to the HTML Body
}
- return componentRef;
+ return componentOutput;
}
}