1

I'm encountering an issue while working with Angular and Angular Material. After fetching data from an API and displaying it in an Angular Materials table, I'm trying to add an extra column with edit and delete buttons. However, I'm running into an error that indicates a duplicate column name. I've shared the relevant HTML template and TypeScript class code below for reference:


 <div class="containerS1">
    <button (click)="showRetourAchat()">Retour Achat</button>
    <button (click)="showRetourVente()">Retour Vente</button>       
    <div class="AddProduct">
        <button (click)="openDialog()">Ajouter un retour</button>
    </div>
    
    <div class="containerS2">
        <mat-form-field>
            <mat-label>Filter</mat-label>
            <input matInput (keyup)="applyFilter($event)" placeholder="Chercher" #input>
          </mat-form-field>
          
          <div class="mat-elevation-z8">
            <table mat-table [dataSource]="dataRetour">
                <ng-container *ngFor="let column of displayedColumns" [matColumnDef]="column">
                  <th mat-header-cell *matHeaderCellDef>{{ column }}</th>
                  <td mat-cell *matCellDef="let element">{{ element[column] }}</td>
                </ng-container>
                <ng-container matColumnDef="action">
                    <th mat-header-cell *matHeaderCellDef mat-sort-header> Action </th>
                    <td mat-cell *matCellDef="let element"> 
                      <button mat-icon-button  color="primary"><mat-icon>edit</mat-icon></button>
                      <button mat-icon-button   color="warn"><mat-icon>delete</mat-icon></button> 
                    </td>
                </ng-container>
              
                <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
                <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
              </table>
          
            <mat-paginator [pageSizeOptions]="[5, 10, 25, 100]" aria-label="Select page of users"></mat-paginator>
          </div>
        </div>
          
  
 </div>

and that's my ts class:

export class RetourComponent implements OnInit{
  constructor(public dialog: MatDialog,private stockService: StockApiService){}
  dataRetour!: MatTableDataSource<any>;
  RetourAchat = true;
  RetourVente = false;
  displayedColumns:string[]=[];
  @ViewChild(MatPaginator) paginator!: MatPaginator;
  @ViewChild(MatSort) sort!: MatSort;

  getRetour() {
    this.stockService.getRetour().subscribe({
      next: (res) => {
        // Filter the data based on the typeRetour value
        const filteredData = res.filter(item => {
          if (this.RetourAchat) {
            return item.typeRetour === 'Achat';
          } else if (this.RetourVente) {
            return item.typeRetour === 'Vente';
          }
          return true; // If neither RetourAchat nor RetourVente is selected, show all rows
        });
        
        this.dataRetour = new MatTableDataSource(filteredData);
        this.dataRetour.paginator = this.paginator;
        this.dataRetour.sort = this.sort;
      },
      error: (err) => {
        console.log(err);
      }
    });
  }
  
  showRetourAchat() {
    this.RetourAchat = true;
    this.RetourVente = false;
    this.displayedColumns = [ 'typeRetour','codeDeSuivi', 'dateRetour', 'fournisseur', 'produitsRetournes', 'entrepotDorigin', 'quantite', 'montant', 'etat','action'];
    this.getRetour();
  }
  
  showRetourVente() {
    this.RetourAchat = false;
    this.RetourVente = true;
    this.displayedColumns = ['typeRetour','codeDeSuivi',  'dateRetour', 'client', 'produitsRetournes', 'entrepotDeDestination', 'dateDarrivee', 'quantite', 'montant', 'etat','action'];
    this.getRetour();
  }
  
  ngOnInit() {
    this.RetourAchat = true;
    this.displayedColumns = ['typeRetour','codeDeSuivi','dateRetour','fournisseur','produitsRetournes','entrepotDorigin','quantite','montant','etat','action'];
    this.getRetour();
  }

  
 

  openDialog() {
    this.dialog.open(DialogRetourComponent, {
      width:'30%'
    }).afterClosed().subscribe(val=>{
      console.log('Dialog closed with value:', val);
      if (val === 'Ajouter') {
        console.log('Refreshing product list...');
        this.getRetour();
      }
    })
  }

  
  applyFilter(event: Event) {
    const filterValue = (event.target as HTMLInputElement).value;
    this.dataRetour.filter = filterValue.trim().toLowerCase();

    if (this.dataRetour.paginator) {
      this.dataRetour.paginator.firstPage();
    }
  }

  
  editRetour(row:any){
    this.dialog.open(DialogRetourComponent, {
      width:'30%',
      data:row
    }).afterClosed().subscribe(val=>{
      if(val==='Editer'){this.getRetour();}
    })
  }

  deleteRetour(id:number){
    this.stockService.deleteRetour(id).subscribe({
      next:(res)=>{
        this.stockService.showAlert("Sale deleted","Success");
        this.getRetour();
    },
      error:(err)=>{this.stockService.showAlert("error","Failure");
     
    }
    })
  }

}

The problem seems to be related to defining the additional 'action' column with the buttons. I'd appreciate any insights or guidance on how to properly add this column without causing a duplication error. Thank you in advance for any assistance!"

Feel free to use this summary as a basis to seek help in Angular communities. It's always a good idea to provide a clear and concise description of the issue along with relevant code snippets to get effective assistance.

  • Have you tried removing the "action" string from you `displayedColumns`? It seems like it is included in your `ngFor` loop as well as defined again in the HTML. – Jake Smith Aug 15 '23 at 16:11
  • yeah, i did it , i remove action from my displayedColumns array and the table doesn't show that column definitively – Nadia akrach Aug 15 '23 at 16:36
  • For testing purposes, can you create another collection called something like `displayedColumnsWithoutAction` and set it to the same array without the action column. Then use that in your ngFor above the action column definition? I think the original problem is that you had two column defs named "action", but when we remove it from `displayedColumns`, it isn't included in the ` – Jake Smith Aug 15 '23 at 16:43

1 Answers1

0

Update You can defined all the columns you need and only change the "displayedColumns" (if you have a column definition but it's not included in the displayedColumns, mat-table simply not show it)

allColumns=[..........]
<table mat-table [dataSource]="dataRetour">
         <!--you loop over allColumns-->
         <ng-container *ngFor="let column of allColumns" 
                    [matColumnDef]="column">
            ...
         </ng-container>
         <!--your column action-->
         <ng-container matColumnDef="action">
            ...
         </ng-container>
<!--but use displayedColumns in tr-->
         <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
         <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

Original response

As @JakeSmith says. You need two variables

displayedColumnsWithoutAction:string[]=[]
displayedColumns=[...this.displayedColumnsWithoutAction,"actions"]

You loop over displayedColumnsWithoutActions and use displayedColumns when defined the tr

<table mat-table [dataSource]="dataRetour">
     <!--you loop over displatedColumnsWithoutActions  -->
     <ng-container *ngFor="let column of displatedColumnsWithoutActions" 
                [matColumnDef]="column">
        ...
     </ng-container>
     <!--your column action-->
     <ng-container matColumnDef="action">
        ...
     </ng-container>
     <!--but use displayedColumns in tr-->
     <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
     <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
Eliseo
  • 50,109
  • 4
  • 29
  • 67