3

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 :)

2 Answers 2

6

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your fast response! Works now
Actually you don't need to unsubscribe, when angular's HttpClient is used. This will either emit a value and complete or error out. Both of these actions inherently call .unsubscribe on all subscriptions :)
2

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.