In a project of mine, I try to display Angular Components (like an Autocomplete Dropdown Search) in a table. Because of the requirements I have (like multi-selecting different cells with ctrl+click) I decided to give it a go with handsontable.
I've used the handsontable renderer and add the components dynamically.
The code looks like this
matrix.component.ts
this.hot = new Handsontable(this.handsontable.nativeElement, {
data: this.tableData,
colWidths: [80, 300],
colHeaders: ['Id', 'Custom Component'],
columns: [
{
data: 'id',
},
{
data: 'id',
renderer: (instance: any, td: any, row: any, col: any, prop: any, value: any, cellProperties: any) => {
if (cellProperties.hasOwnProperty('ref')) {
(cellProperties.ref as ComponentRef<CellContainerComponent>).instance.value = row;
} else {
cellProperties.ref = this.loadComponentAtDom(
CellContainerComponent,
td,
((component: any) => {
component.template = this.button4Matrix;
component.value = row;
}));
}
return td;
},
readOnly: true,
},
],
});
private loadComponentAtDom<T>(component: Type<T>, dom: Element, onInit?: (component: T) => void): ComponentRef<T> {
let componentRef;
try {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
componentRef = componentFactory.create(this.injector, [], dom);
onInit ? onInit(componentRef.instance) : console.log('no init');
this.appRef.attachView(componentRef.hostView);
} catch (e) {
console.error('Unable to load component', component, 'at', dom);
throw e;
}
return componentRef;
}
What's my current issue is the lifecycle of the rendered angular components.
Stuff I tried:
Do nothing
Tried Solution: Doing nothing and leaving everything to Angular
Problem: Angular never calling the ngOnDestroy of the CellContainer.
Saving componentRefs
Tried Solution: Saving the componentRef in an Array and after a certain amount of rendering trying to destroy the components I rendered some time ago. Counting via time, handsontable hooks (verticalScroll/beforeRender/afterRender), in the render-method
Problem: Destroy of the angular component always throws an error ('cannot read property'nativeNode' of null') or the components get displayed completely wrong
Check during rendering if an element is there
Tried Solution: During the render: I checked if there's already a component and if it was I used to recycle the already-there component by adding a new value only.
Problem: The values get completely mixed up during scrolling.
A link to my solution (and an implemented solution #3) is available on github.
Does anyone have an idea of how to handle this in a clean way? If not the application gets slow & unusable after a little bit of scrolling & using the table. Better refer : https://handsontable.com/docs/8.2.0/tutorial-cell-function.html
Using a cell renderer. Use the renderer name of your choice when configuring the column:
const container = document.getElementById('container');
const hot = new Handsontable(container,
{
data: someData,
columns:
[{
renderer: 'numeric'
}]
});
may be you need to try changeDetection as below, forcing the new changes to your component.
changeDetection: ChangeDetectionStrategy.OnPush
Success story sharing