TKtheLazyGuy
TKtheLazyGuy

Reputation: 166

Error in *ngIf on one element not letting *ngFor to work properly on another

Initialization of variable in one element and error in console that I had been ignoring all along, created a problem elsewhere in another element. Why does angular behave this way?

When I do not initialize another variable, which I am using with *ngIf, javascript throws up "undefined" while testing its length (expected). But this also causes *ngFor to not work properly for the dropdown (no data gets loaded - unexpected). Btw I realized, this is not just happening with datalist; *ngFor fails on any component if *ngIf has error in this code.

So, why does *ngFor fail for dropdown (first element) when *ngIf condition can't be validated on table (another element)? Should I think about this in a different way that Angular is a framework and may behave erratically if I don't do a good job in managing errors on its constructs?

Below is the code to reproduce error. Uncomment the suggested line and error will go away...

HTML

<div class="row">
    <div class="col">
        <input
          type="text" class="form-control" list="tktnum"
          placeholder="Ticket Number..." [(ngModel)]="tktNum"
        >
        <datalist id="tktnum">
            <option *ngFor="let a of tktVals">{{a.TicketNo}}</option>
        </datalist>
    </div>
</div>
<br><br>

<div class="row">
    <div class="col">
        <table class="table table-hover table-striped table-dark table-bordered"
            *ngIf="reportElements.length > 0">
            <thead>
                <tr>
                    <th scope="col">Ticket Number</th>
                </tr>
            </thead>
            <tbody>
                <tr *ngFor="let _ of reportElements">
                    <td>{{ _.F1 }}</td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

Typescript

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

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  reportElements: [{F1: 3}];

  tktVals: Array<{ TicketNo: string }> = [{ TicketNo: "1" }, { TicketNo: "2" }];
  tktNum: any;

  constructor() {}

  ngOnInit(): void {
    this.tktVals = [{ TicketNo: "1" }, { TicketNo: "2" }];
    
    //Uncomment the following line of code and it will start working as expected.
    //this.reportElements = [{F1: 3}];

    console.log(this.reportElements.length);
  }
}

Upvotes: 1

Views: 819

Answers (1)

Misha Borisov
Misha Borisov

Reputation: 385

The problem is that reportElements is undefined initially. You cannot actually use the length of "undefined because it doesn't have a property of length, and you cannot also iterrate on it because undefined is not iterrable. There're multiple ways to solve it:

  1. Give an initial value of an empty array to reportElements. But in this case you need to care about removing/changing its type notation:

     reportElements: [{F1: 3}] = [];
    
  2. Wrap the whole table html into ng-container:

     <ng-container *ngIf="reportElements">
         <table></table>
     </ng-container>
    
  3. Change the existing *ngIf of the table to the following one:

     *ngIf="reportElements && reportElements?.length > 0"
    

When an error in template expression happens, it makes all other expressions in the same template to fail. That's why both your table and select are broken.

Upvotes: 1

Related Questions