l-lin / angular-datatables

DataTables with Angular
https://l-lin.github.io/angular-datatables/
MIT License
1.57k stars 481 forks source link

ngTemplateRef does not work on responsive windows-mode when columns are stacked #1772

Open lingold-gseb opened 4 months ago

lingold-gseb commented 4 months ago

:beetle: bug report

When I use the responsive extension, it only shows the plain text from the source and not the template-version if the windows size is in responsive mode. Back in full window size mode the template is rendered as expected.

Is there a way to fix my problem with code adjustments or is this a bug? Thanks for the support!

:microscope: Minimal Reproduction

StackBlitz/GitHub Link: https://stackblitz.com/edit/stackblitz-starters-rx7mzq?file=package.json Step-by-step Instructions: Minimize and maximize the window on StackBlitz to see the difference.

:8ball: Expected behavior

The ngTemplateRef column should display the referenced template and not the pure rendered text from the source, in the same way as it displays the content for non-stacked/-responsive views.

:camera: Screenshots

Normal View - component shown: image Responsive - plain data text shown: image

:memo: Additional context

see closed issue: https://github.com/l-lin/angular-datatables/issues/1723 columnDefs does not solve the issue with the rendering on responsive mode.

shanmukhateja commented 4 months ago

Hi @lingold-gseb

Can you try redrawing the table on https://datatables.net/reference/event/responsive-resize or https://datatables.net/reference/event/responsive-display?

I suspect this happens because responsive plugin resets the transform we apply on the table's cell during init.

lingold-gseb commented 4 months ago

Hi @shanmukhateja thanks for the answer! I tried to redraw the table on the suggested events, but it has no effect on the correct rendering of the templates in responsive mode. See ngAfterViewInit-method in https://stackblitz.com/edit/stackblitz-starters-yg83ku?file=src%2Fexample%2Fexample.component.ts

dtInstance.on(
          'responsive-resize',
          (e: any, datatable: any, columns: any) => {
            const count = columns.reduce((a: any, b: any) => {
              return b === false ? a + 1 : a;
            }, 0);

            console.log(count + ' column(s) are hidden');
            dtInstance.draw();
            console.log('called draw()');
          }
        );
      });

Is there a mistake in my implementation of the redraw-approach or do you have another idea how to solve the problem? A thought perhaps: If the page is initially loaded in a minimized responsive window, the elements already will not load correctly.

Thanks!

shanmukhateja commented 3 months ago

@lingold-gseb Hi sorry for the late reply.

In that case, could you try dtTrigger.next() instead of dtInstance.draw()?

The goal is to force a redraw of the table.

I think we need to investigate a way to use something like "before cell draw" callback (if it exists) and trigger our custom transformations for TemplateRef or Pipe.

Edit: I spent a few hours on this and I believe I have a solution. Are you comfortable with compiling the library locally?

// Replace the function with the same name inside "src/angular-datatables.directive.ts"

private applyNgRefTemplate(row: Node, columns: ADTColumns[], data: Object): void {
    // Filter columns using `ngTemplateRef`
    const colsWithTemplate = columns.filter(x => x.ngTemplateRef && !x.ngPipeInstance);
    colsWithTemplate.forEach(el => {
      const { ref, context } = el.ngTemplateRef;
      // get <td> element which holds data using index
      const i = columns.filter(c => c.visible !== false).findIndex(e => e.id === el.id);
      let cellFromIndex = row.childNodes.item(i);
      // Responsive plugin messes with table columns
      // The code below will trigger when `rowChild` is about to be initialized.
      if (!cellFromIndex && this.dtOptions.responsive) {
        try {
          const rowChildParent = this.dt.row(row).child();
          if (rowChildParent) {
            // now we need to locate the <td> which has the matching column index
            const objListFromIndex = rowChildParent.first().find(`*[data-dtr-index="${i}"]`)?.find('.dtr-data');
            if (objListFromIndex.length > 0) {
              cellFromIndex = objListFromIndex.get()[0];
            }
          }
        } catch (ignore) {}
      }
      // reset cell before applying transform
      $(cellFromIndex).html('');
      // render onto DOM
      // finalize context to be sent to user
      const _context = Object.assign({}, context, context?.userData, {
        adtData: data
      });
      const instance = this.vcr.createEmbeddedView(ref, _context);
      this.renderer.appendChild(cellFromIndex, instance.rootNodes[0]);
    });
  }

Let me know if this works :)