When clicking the button, the service function is not properly updating the view

Upon calling this.getLeaderboard(); in the ngOnInit() function within leaderboard.component.ts, the leaderboard is only displayed on page load or refresh, which is expected. However, I also want to retrieve and display the leaderboard upon clicking a button in app.component.ts.

When I click on the button, the flow directs to leaderboard.component, which calls the leaderboard.service, but the changes are not reflected or updated in leaderboard.component.html. Although values are logged in the console, the DOM remains unchanged...

What could be causing this issue?

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>

Answer №1

If you're looking to access your component references using the ViewChild annotation, you should consider removing them from the constructor as shown below:

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

    @ViewChild(LeaderboardComponent)
    private leaderboard_component: LeaderboardComponent;

    @ViewChild(ClubStatisticsComponent)
    private clubstatistics_component: ClubStatisticsComponent;

    constructor(){}

Additionally, it's recommended to exclude these components from the providers array in your AppComponent. Typically, components are injected with services rather than other components.

Answer №2

Perhaps the issue lies in the leaderboard array only updating its entries and not the list itself. Angular may not recognize changes if elements are added or removed from the same list. One solution is to follow the advice provided in this answer: . Another approach would be to recreate the list when retrieving the leaderboard:

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

By using [].concat to generate a new instance, Angular can correctly identify the change.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the process for eliminating moment locales from an Angular build?

When I build my Angular 5 application with the command: ng build --prod --sm I noticed that the main.js file contains a lot of excess space occupied by moment. It seems all the locales are being loaded when I include: import * as moment from 'momen ...

Tips on how to connect the scope from a controller to a custom directive in Angular

Currently, I am delving into the world of Angular and finding myself immersed in directive lessons. However, as I engage in some practice exercises, I have encountered a stumbling block. Specifically, I have developed a custom directive with the intention ...

Encountering an issue when utilizing a personalized directive with AngularJS

I'm encountering an issue with the "auto-complete" directive that I'm using from jsfiddle. The error message I'm receiving is iElement.autocomplete is not a function. Can someone help me troubleshoot and fix this error? directive.js starte ...

Exploring the capabilities of observables in mapping nested dynamic object keys, specifically focusing on manipulating data within angular-calendar events

Perhaps utilizing something like map<T,R> would be a better approach than my current method. I am hoping to receive some advice on how to resolve this issue. Currently, no events are being mapped due to incorrect mapping resulting in an incorrect pat ...

Sort through the data that is already populated and proceed to paginate within Mongodb

Hey there, I'm currently working on populating some data and then implementing pagination for that data. Take a look at this example: Schema A (Users) { name: 'Demo', postId: 'someObjectId', } Schema B (Posts) { id: 's ...

Is there a way to get Axios to display CSS on the page?

While working on a Web Viewer project with axios for web scraping practice, I encountered an issue where the CSS wasn't loading properly. Here is the code snippet I used: console.log("Tribble-Webviewer is starting!") const express = requir ...

What steps are involved in creating a default toolbar for a material picker in a React application?

Looking for assistance with customizing the toolbar for material picker (v3) by adding a title inside the dialog. I successfully implemented this in the KeyboardDatePicker following a helpful thread (https://github.com/mui-org/material-ui-pickers/issues/11 ...

Adjusting Image Sizes in React using Material-UI: A Guide to Automatically Resizing Images for Various Screen Dimensions

Having trouble making images within my React project responsive across different screen sizes. Using Material-UI, I currently set the maxWidth property with a percentage value which doesn't give me the desired outcome. Seeking advice on a more dynamic ...

Mysterious anomaly observed: Peculiar trajectory detected in canvas image during left

I have a fascinating HTML5 Canvas that showcases the movement of two identical objects to both the right and the left. Although the right side operates flawlessly, I've noticed that the left object leaves behind an intriguing trail with a hint of gree ...

Stop users from refreshing or closing the window while an axios request is being processed

I'm in the process of creating a dynamic Web Application that involves utilizing Axios.get requests. Given that Axios operates asynchronously, my approach includes an async function along with await axios.all: async handleSubmit(){ const ...

Difficulty arises in bookmarking slug paths within Next.js SSG

I am currently managing a next js SSG exported application that is operating in nginx. My objective is to transfer it to S3 in the near future. However, I have encountered an obstacle where the slug paths cannot be bookmarked in a browser. Although the ro ...

Unordered list in Bootstrap Collapse not responding after initial click

I recently created a filetree page on my website and incorporated some bootstrap collapse elements that seem to function as intended. However, I am encountering an issue where the folder content does not collapse upon clicking the folder symbol (button) fo ...

How can I populate dropdown options from an API in a react JS project using typescript and react saga?

Check out my page, where I am trying to fetch brand options from an API. Below is the saga I have implemented: Action.tsx export const getBrandsForDropdown = (request: IPagination) => { return { type: actions, payload: request ...

The information being transferred from a jQuery Ajax request to an MVC ApiController Action is disappearing

Let me first mention that although there are similar questions to this one already posted here, I've tried all the solutions and have not had any success. I have an MVC ApiController Action with the following signature: public void Post([FromBody] E ...

Unchanging Dive Field

I'm having trouble understanding why this field isn't updating with the correct number. It seems that any value less than 1 is being rounded in the alert() function. I believe all numbers are simply getting rounded down, but I'm unsure of an ...

Implement lazy loading of content on scroll in Grails framework

Currently, I am uploading images and displaying them in a gsp view. My goal now is to implement a functionality where the images load as I scroll down on the page. A friend suggested using jQuery to make an ajax call for this. Can someone please provide g ...

Creating a series of images in JavaScript using a for loop

Currently attempting to create an array of images, but with a large number of images I am looking into using a "for loop" for generation. Here is my current code snippet : var images = [ "/images/image0000.png", "/images/image0005.png", "/ima ...

Automatically fill in options for up to 3 dropdown menus depending on the choices made in the previous dropdown menu

I've looked through similar questions but haven't found the solution I need. Here's a sample fiddle with some example data for years, months, and days. Instead of manually adding select option divs for each year, I want the div id to dynami ...

Discovering if the array data is already present in Angular can be accomplished by using specific methods

Here is the snippet of code: data = [ { 'id': 'asdjxv', 'username': 'emma', }, { 'id': 'asqweja', 'username': 'adam', }, { ...

Discover the new features of Next.js 13: Learn how to efficiently extract parameters from URLs using userParams, and seamlessly pre-render components at build time

Currently, I am diving into the latest version of next.js 13.4 and have set up a page with the route /notes/[note]. To extract the note parameter from the URL, I am utilizing import { useParams } from 'next/navigation'. In addition to this, I wa ...