Donvino
Donvino

Reputation: 2575

ngIf in ng-container inside mat-table

I am new to Angular. I have a following problem: I need to display on a table only rows with status == "FINISHED". I understand why my code doesn't work the way I want, but can't get the right solution for me.

Here is the .ts file:

import { Component, OnInit } from '@angular/core';

 import { TestCase } from '../test-cases/models/test-case.model';
 import { TestCaseService } from '../test-case.service';

@Component({
  selector: 'app-test-cases-view',
  templateUrl: './test-cases-view.component.html',
  styleUrls: ['./test-cases-view.component.css']
})
export class TestCasesViewComponent implements OnInit {
  testCases: TestCase[];
  displayedColumns: string[] = ['testCaseName', 'testCaseDescription','id','created','status', 'xls'];
  constructor(private testCaseService: TestCaseService) { }

  ngOnInit() {
    this.getTestCases();
  }

  postRequest(testCaseId: string):void {
    this.testCaseService.postTestResult(testCaseId);
  }

  getTestCases(): void {
    this.testCaseService.getTestCases()
      .subscribe(testCases => this.testCases = testCases);
  }

}

test-case.model.ts file:

import { TestCaseParams } from './test-case-params.model';

export class TestCase {
  public testCaseName:string;
  public testCaseDescription:string;
  public parameters:TestCaseParams;
  public id:string;

  constructor () {
    this.parameters= new TestCaseParams();
  }
}

test-case-params.model.ts

import { EditedVar } from './replacement.model';
import { FilterVar } from './filter.model';
import { OutputVar } from './output.model';

export class TestCaseParams {
  public appsCount: number;
  public algId: number;
  public product: string;
  public invokeConsolidation: boolean;
  public invokeProdStrategy: boolean;
  public filterVars: FilterVar[];
  public editedVars: EditedVar[];
  public outputVars: OutputVar[];
  public fromDate: Date;
  public toDate:Date;
}

replacement.model.ts:

export class EditedVar {
  public path:string;
  public value:string;
}

filter.model.ts:

export class FilterVar{
  public filter:string;
  public filterType:string;
  public filterValue:string;
  public varch:boolean;
}

output.model.ts:

export class OutputVar {
  public path:string;
  public alias:string;
  public type:string;
}

Here is the html file that works:

<div id="view-component">
  <h2>Test Cases</h2>

  *some code just don't fit in the question*

    <ng-container matColumnDef="xls">
      <th mat-header-cell *matHeaderCellDef> Xls report </th>
      <td mat-cell *matCellDef="let testCase">
        <button *ngIf = "testCase.status == 'FINISHED'" (click)="postRequest(testCase.id)">Make Excel report</button>
      </td>
    </ng-container>

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

Here is what I try to do:

<table mat-table [dataSource]="testCases" class="mat-elevation-z8">

    <ng-container *ngIf = "testCase.status == 'FINISHED'" matColumnDef="testCaseName">
      <th mat-header-cell *matHeaderCellDef> testCaseName </th>
      <td mat-cell *matCellDef="let testCase"> {{testCase.testCaseName}} </td>
    </ng-container>

    <ng-container *ngIf = "testCase.status == 'FINISHED'" matColumnDef="testCaseDescription">
      <th mat-header-cell *matHeaderCellDef> testCaseDescription </th>
      <td mat-cell *matCellDef="let testCase"> {{testCase.testCaseDescription}} </td>
    </ng-container>

    <ng-container *ngIf = "testCase.status == 'FINISHED'" matColumnDef="id">
      <th mat-header-cell *matHeaderCellDef> Report Id </th>
      <td mat-cell *matCellDef="let testCase"> {{testCase.id}} </td>
    </ng-container>

    <ng-container *ngIf = "testCase.status == 'FINISHED'" matColumnDef="created">
      <th mat-header-cell *matHeaderCellDef> Created </th>
      <td mat-cell *matCellDef="let testCase"> {{testCase.created | date: 'dd/MM/yyyy hh:mm:ss'}} </td>
    </ng-container>

    <ng-container *ngIf = "testCase.status == 'FINISHED'" matColumnDef="status">
      <th mat-header-cell *matHeaderCellDef> Status </th>
      <td mat-cell *matCellDef="let testCase"> {{testCase.status}} </td>
    </ng-container>

    <ng-container *ngIf = "testCase.status == 'FINISHED'" matColumnDef="xls">
      <th mat-header-cell *matHeaderCellDef> Xls report </th>
      <td mat-cell *matCellDef="let testCase">
        <button (click)="postRequest(testCase.id)">Make Excel report</button>
      </td>
    </ng-container>

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

As you can see I don't want to display rows with any status that isn't equeal "FINISHED". For this code I get a following error:

ERROR TypeError: Cannot read property 'status' of undefined at Object.eval [as updateDirectives] (TestCasesViewComponent.html:6)

I understand that I need to place let testCase somewhere else, so it would be defined on the ng-container level, but I can't figure out where.

Upvotes: 1

Views: 9283

Answers (2)

Explosion Pills
Explosion Pills

Reputation: 191729

Rather than do the filtering at the mat-table level, you can filter the array that you get from the Observable and assign the filtered array as the data instead:

getTestCases(): void {
  this.testCaseService.getTestCases()
    .subscribe(testCases => this.testCases = testCases.filter(({ status }) =>
      status === 'FINISHED',
  ));
}

Upvotes: 2

Joel Vicente
Joel Vicente

Reputation: 66

That error is because testCase is undefined, and in javascript accessing properties (like testCase.status) in an undefined object will always throw that error.

But the reason testCase is undefined in the first place is because *ngIf will be run before the method getTestCases()

You have two possible solutions:

  1. Instantiate the object;

  2. Use another *ngIf to check if testCase is undefined.

For example:

    <div *ngIf="testCase">
       <div *ngIf="testCase.status == 'FINISHED'">

       </div>
    </div>

In other words, you just need to be sure that testCase in not undefined when you access its properties in the *ngIf.

Upvotes: 1

Related Questions