Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rendering Angular components in Handsontable Cells

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:

  1. Do nothing

Tried Solution: Doing nothing and leaving everything to Angular

Problem: Angular never calling the ngOnDestroy of the CellContainer.

  1. 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

  1. 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

like image 473
phhbr Avatar asked Mar 15 '18 09:03

phhbr


1 Answers

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'   }]  }); 
like image 91
Amar Sawant Avatar answered Sep 18 '22 12:09

Amar Sawant