How to make an Angular component Generic with customized configuration without using @Input option?

259 views Asked by At

Hello angular community,

I want to ask about a good way to make an icon-component generic and avoid code duplication

delete-icon-component:

@Component({
  selector: 'app-delete-row-icon',
  template: `
    <style>
      button {
        background-color: Transparent;
        border: none;
        cursor: pointer;
        overflow: hidden;
        outline: none;
      }

      mat-icon {
        font-size: 18px;
      }
    </style>
    <button (click)="onClickDeleteIcon($event)">
      <mat-icon
        matTooltip="Unselect file"
        matTooltipPosition="below"
        class="material-icons-outlined"
        >{{ iconRegistry.REMOVE }}</mat-icon
      >
    </button>
  `,
  styles: [],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class DeleteRowIconComponent implements ICellRendererAngularComp {
  params;
  disabled = false;
  iconRegistry = IconRegistry;

  agInit(params): void {
    this.refresh(params);
  }

  refresh(params): boolean {
    this.params = params;
    return true;
  }

  onClickDeleteIcon($event): void {
    const params = {
      rowData: this.params.data,
      rowIndex: this.params.rowIndex
    };
    this.params.onClick(params);
  }

remove-icon-component:

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ICellRendererAngularComp } from 'ag-grid-angular';
import { IconRegistry } from '../../../icon-registry/icon-registry';

@Component({
  selector: 'app-remove-icon-render',
  template: `
    <style>
      button {
        background-color: Transparent;
        border: none;
        cursor: pointer;
        overflow: hidden;
        outline: none;
      }
      button :hover {
        background-color: #efeadf;
      }
      mat-icon {
        font-size: 18px;
      }
    </style>
    <button
      *ngIf="params?.data && !!params.data.objectId && !!params.data.objectPath"
      (click)="onClick($event)"
      [disabled]="params?.disabledGetter ? params.disabledGetter(params) : false"
      data-cy="icon-cell-Delete"
    >
      <mat-icon [matTooltip]="tooltip" matTooltipPosition="below" class="material-icons-outlined">
        {{ iconRegistry.MINUS }}
      </mat-icon>
    </button>
  `,
  styles: [],
  changeDetection: ChangeDetectionStrategy.OnPush
})

export class RemoveIconRenderComponent implements ICellRendererAngularComp {
  params;
  iconRegistry = IconRegistry;
  tooltip: string;

  agInit(params): void {
    this.params = params;
    if (this.params?.tooltipGetter) {
      this.tooltip = this.params.tooltipGetter(this.params);
    } else {
      this.tooltip = this.params?.tooltip;
    }
  }

  onClick($event): void {
    $event.stopPropagation();
    const params = {
      rowData: this.params.node.data
    };
    this.params.onClick(params);
  }

  refresh(params): boolean {
    this.params = params;
    return true;
  }

the only difference is the ICON and the logic behind agInit function

I cannot use the @Input feature, because I will use these components inside ts code and not HTML code this way:

  this.frameworkComponents = {
      deleteButtonRenderer: DeleteIconRenderComponent
    };

So any good recommended way to make one generic component that can accept any icon ?

1

There are 1 answers

2
MoxxiManagarm On

You could create an abstract RowIconComponent and extend from it. Something like this:

export abstract class IconComponent {
  params;
  iconRegistry = IconRegistry;
  tooltip: string;

  refresh(params): boolean {
    this.params = params;
    return true;
  }

  abstract onClick($event): void;
  abstract agInit(params): void;
}

And changed example:

@Component({
  // ...
})
export class DeleteRowIconComponent extends IconComponent implements ICellRendererAngularComp {
  agInit(params): void {
    this.refresh(params);
  }

  onClick($event): void {
    const params = {
      rowData: this.params.data,
      rowIndex: this.params.rowIndex
    };
    this.params.onClick(params);
  }
}