Angular - Value change of object not registering through change detection

941 Views Asked by At

i'm relatively new to learning Angular and have followed some instructions from a PluralSight demo on setting up a basic service, component and HTML page.

When I run my service and debug, I can see that the service is successful in retrieving the data I need. However, part of the HTML doesn't seem to go through change detection. The date attribute I display does this successfully, however my array just gets ignored.

I've tried explicitly creating a separate array and assigning to a different variable, which also didn't work. Is there something around the ngModel and change detection I am missing?

Service:

@Injectable({
  providedIn: 'root'
})
export class RankingService {
  private rankingUrl = 'https://localhost:44381/api/Ranking'

  constructor(private http: HttpClient) { }

  getRanking(): Observable<IRanking> {
    return this.http.get<IRanking>(this.rankingUrl)
      .pipe(
        tap(data => console.log('All: ' + JSON.stringify(data))),
        catchError(this.handleError)
      );
  }

HTML:

<div class='row'>
  {{'Last Updated: ' + ranking?.startDateTime}}
</div>
<table class='table' *ngIf='ranking'>
  <thead>
    <tr>
      <th>Rank</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor='let position of ranking.rankpositions?.slice(0,30)'>
      <td>
        {{ position.Rank }}
      </td>
    </tr>
  </tbody>
</table>

Component:

export class RankingListComponent implements OnInit {
  errorMessage = '';
  ranking: IRanking;

  constructor(private rankingService: RankingService) { }

  ngOnInit(): void {
    this.rankingService.getRanking().subscribe({
      next: ranking => {
        this.ranking = ranking;
      },
      error: err => this.errorMessage = err
    });
  }

}

IRanking is an interface, with an array of rankPositions (Which represents another Interface).

As an output I get this (I've removed some HTML elements, to make the code segment smaller):

enter image description here

In the console, I clearly have the rankpositions array populated: enter image description here

2

There are 2 best solutions below

1
On BEST ANSWER

Please change rankpositions to rankPositions

1
On

you've got a typo, so this should be closed.

but beyond that, running functions like that in template is pretty much always going to cause you change detection problems. use the slice pipe instead here for a quick fix:

<tr *ngFor='let position of ranking.rankPositions | slice:0:30'>

a better fix IMO would be switching to async pipe and using rx operators to run your slice.

a general angular best practice is to not run functions in templates, use pipes when appropriate, but generally your functions should be running in your component controller.