Rendering Angular components in Handsontable Cells

AngularTypescriptDomDynamicHandsontable

Angular Problem Overview


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

Angular Solutions


Solution 1 - Angular

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'
  }]

});

Solution 2 - Angular

may be you need to try changeDetection as below, forcing the new changes to your component.

changeDetection: ChangeDetectionStrategy.OnPush

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionphhbrView Question on Stackoverflow
Solution 1 - AngularAmar SawantView Answer on Stackoverflow
Solution 2 - AngularpenDriveView Answer on Stackoverflow