Error: Cannot read properties of undefined (reading 'headerCell') - Creating reactive form inside a angular material table

14.2k Views Asked by At

I'm trying to create an angular material table with reactive form. I need to add some properties, so I'm using formArray. But my table is not showing any result, and angular keeps me returning the same error:

ERROR TypeError: Cannot read properties of undefined (reading 'headerCell')

This is what I got until now:

HTML

<button mat-button mat-raised-button (click)="addParameterWhatsApp()" color="primary">Add</button>

    <form [formGroup]="form" autocomplete="off">
    
      <table #table mat-table [dataSource]="dataSource" class="mat-elevation-z8" formGroupName="whatsapp">
        <ng-container formArrayName="parameters">
          <ng-container *ngFor="let dados of parameters.controls; let i = index ">
    
            <div [formGroupName]="i">
    
              <ng-container matColumnDef="posicao">
                <th mat-header-cell *matHeaderCellDef> Posicao </th>
                <td mat-cell *matCellDef="let element">
                  <mat-form-field appearance="outline">
                    <mat-label>Posicao</mat-label>
                    <input matInput formControlName="posicao">
                  </mat-form-field>
                </td>
              </ng-container>
    
              <ng-container matColumnDef="valor">
                <th mat-header-cell *matHeaderCellDef>Valor</th>
                <td mat-cell *matCellDef="let element">
                  <mat-form-field appearance="outline">
                    <mat-label>Valor</mat-label>
                    <input matInput formControlName="valor">
                  </mat-form-field>
                </td>
              </ng-container>
          </ng-container>
        </ng-container>
        <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
        <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
      </table>
    </form>

TS

form!: FormGroup;
  dataSource;
  displayedColumns = ['posicao', 'valor'];
  constructor(private _formBuilder: FormBuilder) {}

  ngOnInit(): void {
    this.createForm();
  }

  createForm() {
    this.form = this._formBuilder.group({
      whatsapp: this._formBuilder.group({
        name: [],
        parameters: this.buildArrayParametersWhatsApp(),
      }),
    });
  }

  get parameters() {
    return this.form.get('whatsapp')?.get('parameters') as FormArray;
  }

  buildArrayParametersWhatsApp() {
    return this._formBuilder.array([]);
  }

  addParameterWhatsApp() {
    const parametersArray = this._formBuilder?.group({
      posicao: [],
      valor: [],
    });

    if (parametersArray) {
      this.parameters.push(parametersArray);
    }
  }

Image:

enter image description here

What I am missing?

6

There are 6 best solutions below

1
Juan Vicente Berzosa Tejero On

At least in the code you show us, you are not given value to "dataSource" variable, only have its declaration (it should be 'undefined').

That is probably what is causing it to throw errors.

0
abdella On

I don't know why angular shows you not related error ERROR TypeError: Cannot read properties of undefined (reading 'headerCell'). But the reason your app did not work is because you missed to close <div [formGroupName]="i">. you you can see here after this div is closed

1
Metcalfe On

Possible solutions for others visiting this page. I got the same error for what seems to be a different reason.

I hadn't created a matColumnDef for all the columns passed to matHeaderRowDef.

i.e. I passed the table two columns "name" and "description" but only used "name" in the html table.

I could also have fixed the error by removing "description" from the displayedColumns array and leaving the .html file as it was.

Component.ts

@Component({
  selector: 'app-myComponent',
  templateUrl: './myComponent.component.html',
  styleUrls: ['./myComponent.component.scss']
})
export class myComponent implements OnInit {

  public displayedColumns = [
    "name",
    "description"
  ];

  public myData = [
    {
      "id": 1,
      "name": "A",
      "description": "This is A"
    },
    {
      "id": 2,
      "name": "B",
      "description": "This is B"
    }
  ];

  constructor() { }

  ngOnInit(): void {}
}

component.html (threw the error) Only one ng-container with a matColumnDef="name".

<div class="list-container" fxFlex fxLayout="column" >

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

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

        <ng-container matColumnDef="name">
            <mat-header-cell *matHeaderCellDef >Name</mat-header-cell>
            <mat-cell *matCellDef="let element">{{element.name}}</mat-cell>
        </ng-container>

    </mat-table>

</div>

component.html (worked) Now two ng-containers. One for "name" and one for "description".

<div class="list-container" fxFlex fxLayout="column" >

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

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

        <ng-container matColumnDef="name">
            <mat-header-cell *matHeaderCellDef >Name</mat-header-cell>
            <mat-cell *matCellDef="let element">{{element.name}}</mat-cell>
        </ng-container>

        <!-- Note the additional ng-container below -->

        <ng-container matColumnDef="description">
            <mat-header-cell *matHeaderCellDef >Description</mat-header-cell>
            <mat-cell *matCellDef="let element">{{element.description}}</mat-cell>
        </ng-container>

    </mat-table>

</div>
0
Haru On

Actually, I had that problem, and I solved it by adding the MatTableModule to my module component, which fixed it for me.

0
Ashrik Ahamed On

In my case i was using *ngIf inside ng-container, i fixed it like this:

if (condition) {
      this.displayedColumns = ['no', 'item', 'unit', 'qty', 'al_deli_qty', 
      'deli_qty', 'uni_cost_price', 'tot_price'];
    } else {
      this.displayedColumns = ['no', 'item', 'unit', 'qty', 'deli_qty', 
    'uni_cost_price', 'tot_price'];
    }

Like this you need to declare displayed columns, if u are hiding an ng-container

0
Luke O'Malley On

Check your spelling! I got this error due to a type-o in my matColumnDef directive. Notice the mismatch between the displayed columns and the matColumnDef in this example:

public displayedColumns = ['name', 'description'];
 <mat-table [dataSource]="myData">
   <ng-container matColumnDef="neme">
     <mat-header-cell *matHeaderCellDef >Name</mat-header-cell>
     <mat-cell *matCellDef="let element">{{element.name}}</mat-cell>
   </ng-container>
 </mat-table>