M0ttii
M0ttii

Reputation: 31

Angular ngFor running in infinite loop when displaying array

i'm currently trying to make a Logbox for my webapp. It gets an Array of logs and should display them row by row in a div. Im using ngFor and looping trough my Array of logs and then displaying them.

My problem is now that the logs get displayed infinite times, not just 5 times (5 Array entries in list) like it should.

Does somebody have a hint what im missing?

logs.component.html

<div class="logContent">
    <div class="row">
      <div class="col-12" *ngFor="let log of this.logService.getLogs()">
        <app-singlelog [when]="log.when" [type]="log.type" [data]="log.data"></app-singlelog>
      </div>
    </div>
  </div>

log.service.ts

export class LogService {
  private logArray = [];
  /* private logObject = {} as Log; */

  constructor(private httpservice: HttpserviceService) { 
    
  }

  public getLogs(): Array<Log> {
    this.httpservice.getLogs().subscribe(data => {
      data.forEach(index => {
        let logObject = {} as Log;
        logObject.when = index.when;
        logObject.type = index.type;
        logObject.data = index.data;
        this.logArray.push(logObject);
      })
    }
    )
    return this.logArray;
  }
}

Thanks :)

Upvotes: 3

Views: 1415

Answers (2)

Lor&#225;nd P&#233;ter
Lor&#225;nd P&#233;ter

Reputation: 314

Don't use function calls from html template to display data.
Instead, call the getLogs() function from the Angular ngOnInit() function, and store the response in a variable. Then loop on that variable:

export class LogService implements OnInit {
// ...

logs = [];

ngOnInit() {
   this.getLogs();
}

getLogs(): Array<Log> {
    this.httpservice.getLogs().subscribe(data => {
      data.forEach(index => {
        let logObject = {} as Log;
        logObject.when = index.when;
        logObject.type = index.type;
        logObject.data = index.data;
        this.logArray.push(logObject);
      });
      // assign the variable here:
      this.logs = data;

    });
}

In the template:

 <div class="col-12" *ngFor="let log of logs">
   <app-singlelog [when]="log.when" [type]="log.type" [data]="log.data"></app-singlelog>
</div>

The reason behind this is the fact that Angular calls your getLogs function on every page rendering cycle. But you should call the http request only once, when initalising the component.

Don't forget to unsubscribe from your Observable. ;) - corrected from the comment below.

Upvotes: 6

Fabian Strathaus
Fabian Strathaus

Reputation: 3570

You can also use the reactive approach and do the following:

Define the observable once in your component:

logs$ = this.httpservice
    .getLogs()
    .pipe(
      shareReplay(),
      map((data) => data.map(({ when, type, data }) => ({ when, type, data })))
    );

Include in your component HTML the following:

<div class="col-12" *ngFor="let log of logs$ | async">
   <app-singlelog [when]="log.when" [type]="log.type" [data]="log.data"></app-singlelog>
</div>

Upvotes: 2

Related Questions