Home > OS >  Call actions on a generic component from the child component
Call actions on a generic component from the child component

Time:08-04

I have an Angular project with two components, a generic component where the table is defined and a component from which that table is invoked by passing the required data.

The problem is that the table generates a button to delete the record, the delete function has the component itself, not the generic component.

Is it possible to call only the component table.component and then call the function?

This is the generic component, table.component.html:

<mat-table [dataSource]="dataSource" matSort >

    <ng-container [matColumnDef]="column" *ngFor="let column of displayedColumns">

        <ng-container *ngIf="column != 'actions'">
            <mat-header-cell *matHeaderCellDef mat-sort-header> {{ column | titlecase }} </mat-header-cell>
            <mat-cell *matCellDef="let element">{{ element[column] }}</mat-cell>
        </ng-container>

        <ng-container *ngIf="column == 'actions'">
            <mat-header-cell *matHeaderCellDef> Actions </mat-header-cell>
            <mat-cell *matCellDef="let row;let i = index" >
            <button at-icon-button matTooltip="Delete" (click)="testDelete(dataSource.data[i])">
                <mat-icon>delete_forever</mat-icon>
            </button>
            </mat-cell>
        </ng-container>

    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>

</mat-table>

This is the table.component.ts:

@Input('dataSource') public dataSource: MatTableDataSource<any>;
@Input('displayedColumns') public displayedColumns: string[];

Child component, test1.component.html:

<app-table-edit
  [dataSource]="dataSource"
  [displayedColumns]="displayedColumns">  
</app-table-edit>

test1.component.ts:

public dataSource: MatTableDataSource<Company>;

const company = [
   {
      "name":"test2Dyn",
      "phoneNumber":null,
      "province":null
   },
   {
      "name":"test3Prov",
      "phoneNumber":null,
      "province":"Álava"
   }
]

this.dataSource = new MatTableDataSource(company);

public displayedColumns: string[] = ['name','phoneNumber','province','actions']

This is the generic service from which the endPoints are invoked, api.service.ts:

constructor(
    private httpClient: HttpClient,
    private tConstructor: { new (m: Partial<T>, ...args: unknown[]): T },
    protected apiUrl: string
  ) {}

public delete(id: number): Observable<void> {
    return this.httpClient.delete<void>(`${this.apiUrl}/${id}`);
  }

This is the child component's own service, test1.service.ts:

constructor(
    private http: HttpClient
  ) {
    super(http, Company, 'http://localhost:8080/company');
  }

My problem: what is the best way to call the delete() service from the table button?

CodePudding user response:

I'm not sure I understand your question correctly, but if I do, you're looking for a simple service implementation. Please read about angular services for further info.

You have to add your generic service to the constructor in table.component.ts like this:

table.component.ts:

constructor(private genericService: GenericService) {}

then you can call the delete function of that service this way.

 function testDelete(data)
 {
      this.genericService.delete(data.id) // note that you might access id 
                                       // differently
      .subscribe(() => (console.log(this.response)));
 }

here i'm logging out any responses that you migth have, but in the case of a delete you might want to show only errors. Here's a reference https://rxjs.dev/guide/subscription

CodePudding user response:

For the most generic approach, I'd suggest you to take a look at this datatable which is showcased here.

The code where the datatable is used is hosted here:

<bs-datatable #tabel [settings]="settings" [data]="artists" (reloadData)="loadArtists()">
  <div *bsDatatableColumn="{ sortable: true, name: 'Name' }">
    1. Artist
  </div>
  <div *bsDatatableColumn="{ sortable: true, name: 'YearStarted' }">
    2. Year started
  </div>
  <div *bsDatatableColumn="{ sortable: true, name: 'YearQuit' }">
    3. Year quit
  </div>

  <ng-template bsRowTemplate let-artist>
    <tr>
      <td >{{ artist.name }}</td>
      <td >{{ artist.yearStarted }}</td>
      <td >{{ artist.yearQuit }}</td>
      <td >
        <button (click)="doSomething()">Delete</button>
      </td>
    </tr>
  </ng-template>
</bs-datatable>

Note that:

  • *bsDatatableColumn is the same as <ng-template bsDatatableColumn><div>...</div></ng-template>. This sets the template on the BsDatatableComponent.
  • <ng-template bsRowTemplate let-artist> can also be written as <tr *bsRowTemplate="{ artist: $implicit }">

The BsRowTemplateDirective takes in the BsDatatableComponent and TemplateRef (it's annotated by a * as explained above), and simply sets the templateRef of the BsDatatableComponent

@Directive({ selector: '[bsRowTemplate]' })
export class BsRowTemplateDirective {
  constructor(private datatableComponent: BsDatatableComponent, templateRef: TemplateRef<any>) {
    this.datatableComponent.rowTemplate = templateRef;
  }
}

However, I fear that refactoring your code snippet to move the rowTemplate to the consuming component will not be a walk in the park.

  • Related