1

When i call this.getLeaderboard(); in ngOnInit() in leaderboard.component.ts the leaderboard is shown only on page start or page refresh which is normal. But i also want to fetch and show leaderboard on button click from app.component.ts.

When i click on button the flow goes to leaderboard.component which calls the leaderboard.service but nothing is shown or updated in leaderboard.component.html. If i console.log the values are there but the DOM is not updated...

What am i missing here?

app.component.html

  <div class="col nopadding">
         <button id="bottomButton2" type="button" class="btn btn-bottom" (click)="getLeaderboard()">
             <img id="bottom2" class="navbar-bottom-pics" src="assets\img\podium.svg">
              <img id="bottom22" class="navbar-bottom-pics hide" src="assets\img\podiumSelected.svg">
         </button>
    </div>

app.component.ts

 import { Component, Injectable } from '@angular/core';
    import { MatchesComponent } from './matches/matches.component';
    import { LeaderboardComponent } from './leaderboard/leaderboard.component';
    import { ClubStatisticsComponent } from './club-statistics/club-statistics.component';

    @Component({
     selector: 'app-root',
     templateUrl: './app.component.html',
     styleUrls: ['./app.component.css'],
     providers: [LeaderboardComponent,MatchesComponent,ClubStatisticsComponent]
     })

    export class AppComponent{

    constructor(private match_component:MatchesComponent, private 
    leaderboard_component:LeaderboardComponent, private 
    clubstatistics_component:ClubStatisticsComponent){}

    //CALLS FOR BOTTOM NAVBAR
    getLeaderboard(){
      //POSITION SCREEN TO TOP
      window.scrollTo(0, 0);
      //CALL GET LEADERBOARD
      this.leaderboard_component.getLeaderboard();
    }

leaderboard.component.ts

import { Component, OnInit, Injectable} from '@angular/core';
import { leaderboardInstance } from './leaderboardInstance';
import { LeaderboardService } from './leaderboard.service';

@Component({
  selector: 'app-leaderboard',
  templateUrl: './leaderboard.component.html',
  styleUrls: ['./leaderboard.component.css'],
  providers: [LeaderboardService]
})

@Injectable()
export class LeaderboardComponent implements OnInit {

  public leaderboard: leaderboardInstance[];
  public finalLeaderboard: leaderboardInstance[] = new Array();

  constructor(private leaderboard_service: LeaderboardService) { }

  ngOnInit() {

      this.getLeaderboard();

  }

  //GET ACTUAL SERVER JSON RESPONSE AND SUBSCRIBE IT TO array
  getLeaderboard(){
    console.log("leaderboardComponent");
    this.leaderboard_service.getLeaderboard().subscribe(leaderboard =>{ this.leaderboard = this.formatMatchesSingles(leaderboard)});

  }

  formatMatchesSingles(leaderboard){
    console.log("formatMatches");
    let uniqueNames = [];
    let uniqueSurnames = [];
    let matchesPlayed = [];
    let matchesWon = [];

    for(let i = 0; i< leaderboard.length; i++){   

      //FIND ALL UNIQUE NAMES 
      if(uniqueNames.indexOf(leaderboard[i].name1) === -1){
          uniqueNames.push(leaderboard[i].name1);        
      }
      if(uniqueNames.indexOf(leaderboard[i].name2) === -1){
        uniqueNames.push(leaderboard[i].name2);        
      }

      //FIND ALL UNIQUE SURNAMES
      if(uniqueSurnames.indexOf(leaderboard[i].surname1) === -1){
        uniqueSurnames.push(leaderboard[i].surname1);        
      }
      if(uniqueSurnames.indexOf(leaderboard[i].surname2) === -1){
        uniqueSurnames.push(leaderboard[i].surname2);        
      }

    }

    //CALCULATE MATCHES PLAYED
    for(let i=0;i<uniqueNames.length;i++){

      let played = leaderboard.reduce(function(s, o) {
      if (o.name1 === uniqueNames[i] && o.doubles == "0") s++;
      if (o.name2 === uniqueNames[i] && o.doubles == "0") s++;
      return s;
    }, 0);
    matchesPlayed[i]=played;
    }

    //CALCULATE WINS
        for(let i=0;i<uniqueNames.length;i++){

      let wins = leaderboard.reduce(function(s, o) {
      if (o.name1 === uniqueNames[i] && o.sets_team1 > o.sets_team2 && o.doubles == "0") s++;
      else if(o.name2 === uniqueNames[i] && o.sets_team2 > o.sets_team1 && o.doubles == "0") s++;
            return s;
        }, 0);
        matchesWon[i]=wins;
    }

    //CREATE USER OBJECT, ASSIGN ALL VARIABLES AND ADD IT TO ARRAY
    for (let i = 0; i < uniqueNames.length; i++) { 

      let MatchesNo: number = parseFloat(matchesPlayed[i]);
      let MatchesWonNo: number = parseFloat(matchesWon[i]);
      let MatchesLostNo: number = MatchesNo - MatchesWonNo;  //CALCULATE LOSES
      let WinPercentage: number;

      if (MatchesNo > 0 && MatchesWonNo == 0) WinPercentage = 0;
      else WinPercentage = (MatchesWonNo/MatchesNo)*100; //CALCULATE PERCENTAGE
      let newInstance = new leaderboardInstance();
      newInstance.name = uniqueNames[i];
      newInstance.surname = uniqueSurnames[i]
      newInstance.played = matchesPlayed[i];
      newInstance.wins = matchesWon[i];
      newInstance.loses = MatchesLostNo;
      newInstance.percentage = Math.floor(WinPercentage);
      this.finalLeaderboard.push(newInstance);

    }

    //SORT
    this.finalLeaderboard.sort(function(a, b){

      if(a.wins === b.wins){

        if(a.percentage != b.percentage){ //SORT BY PERCENTAGE
          let x = a.percentage, y = b.percentage;  
          return y < x ? -1 : y > x ? 1 : 0;
        }
        else{ //IF ALSO PERCENTAGES ARE THE SAME THEN SORT BY PLAYED MATCHES
          let x = a.played, y = b.played;
          return y < x ? -1 : y > x ? 1 : 0;
        }
      }
      return b.wins - a.wins //DEFAULT SORT BY WINS
    });


    return this.finalLeaderboard;
  }

}

leaderboard.service.ts

import { Injectable } from '@angular/core';
import { leaderboardInstance } from './leaderboardInstance';
import { Observable, Subject } from 'rxjs';
import { HttpClient, HttpParams } from '@angular/common/http';
import {SHA256} from 'crypto-js';

@Injectable()
export class LeaderboardService {

constructor(private http: HttpClient) { }

getLeaderboard(): Observable<leaderboardInstance[]>  {
console.log("leaderboardservice");
let url="ourAPIurl";

var salt = "1234"
var hash1 = SHA256(salt+"lalalala");

localStorage.setItem('methodName', 'GetMatchesOfGroup');
localStorage.setItem('userId', '2');
localStorage.setItem('token', String(hash1));
localStorage.setItem('id_group', '7');
localStorage.setItem('password', String(hash1));
localStorage.setItem('madCheck', 'bc8fcafb0829db3744d0aad45ebda03882d25367291de3883a8c7f75a9c45fb5');

const params = new HttpParams()
.set('methodName', localStorage.getItem('methodName'))
.set('userId', localStorage.getItem('userId'))
.set('token', localStorage.getItem('token'))
.set('id_group', localStorage.getItem('id_group'))
.set('password', localStorage.getItem('password'))
.set('madCheck', localStorage.getItem('madCheck'));

return this.http.post<leaderboardInstance[]>(url, params, {responseType: 'json'});

}
}

leaderboard.component.html

    <tbody>
    <tr *ngFor="let leaderboardInstance of leaderboard; index as i">
      <td width=25><div id="rank1">{{i+1}}</div></td>
      <td><a class="name-table">{{leaderboardInstance.name | uppercase}} {{leaderboardInstance.surname | uppercase | slice:0:1}}<span>.</span></a></td>
      <td class="played">{{leaderboardInstance.played}}</td>
      <td class="won">{{leaderboardInstance.wins}}</td>
      <td class="loses">{{leaderboardInstance.loses}}</td>
      <td class ="percentage" class="center">{{leaderboardInstance.percentage}}<span>%</span></td>
    </tr>
  </tbody>
  </table>
</div>
dukaric1991
  • 602
  • 7
  • 11

2 Answers2

2

I think you want to get your component references via the ViewChild annotation and remove them from the constructor like that:

export class AppComponent{
    @ViewChild(MatchesComponent)
    private match_component: MatchesComponent;

    @ViewChild(LeaderboardComponent)
    private leaderboard_component: LeaderboardComponent;

    @ViewChild(ClubStatisticsComponent)
    private clubstatistics_component: ClubStatisticsComponent;

    constructor(){}

And also remove them from the providers array in your AppComponent. You usually only want services to be injected in the component like that.

Maximilian Riegler
  • 22,720
  • 4
  • 62
  • 71
0

Maybe the leaderboard-array is only updating its entries, not the list itself? Angular does not detect changes if you add or remove elements from the same list. You can use the answer provided here: https://stackoverflow.com/a/42962723/1471485, or you can recreate the list when you get the leaderboard:

this.leaderboard = [].concat(this.formatMatchesSingles(leaderboard));

using [].concat to create a new instance, and Angular will detect the change.

Maximilian Riegler
  • 22,720
  • 4
  • 62
  • 71
John
  • 10,165
  • 5
  • 55
  • 71
  • It doesn't look like that at all, he is setting the new object here `this.leaderboard = this.formatMatchesSingles(leaderboard)`. – Maximilian Riegler Jul 09 '18 at 10:16
  • 1
    Yes, you are right, it does not look that way. However, I do not know the contents of `formatMatchesSingles()`, but it might be the case that the OP is editing the existing list, and returning it. Something similar to this stackBlitz: https://stackblitz.com/edit/random-list-check – John Jul 09 '18 at 10:21
  • Ah, I understand now what you mean - that would be pretty du-... hilarious, I guess? – Maximilian Riegler Jul 09 '18 at 10:23
  • That would be strange indeed :P – John Jul 09 '18 at 10:23
  • formatMatchesSingles() is where i sort the leaderboard and then return sorted. And whats the non hilarious way then to sort some array after it is fetched from the server? i added the missing code for formatMatchesSingles() – dukaric1991 Jul 09 '18 at 10:49
  • You are changing the reference of `leaderboard` to point to `finalLeaderboard `. You can try to return `[].concat(this.finalLeaderboard)` in your method, or do as I suggested in the answer. Note that all the objects inside the arrays are still pointers, not copies. So if you change some properties inside an entry in `leaderboard`, it will change in `finalLeaderboard` as well, and vice versa. – John Jul 09 '18 at 10:53