Home > OS >  Angular Nested *ngFor is giving Infinite results on being used in a HTML Template
Angular Nested *ngFor is giving Infinite results on being used in a HTML Template

Time:08-13

I'm using Github API to built a Repositories Viewer. I'm taking repo.name as an input for another variable and passing it as a parameter to a function which indeed calls to get the Languages used as in a repository.

The problem is that it not displaying individual set of languages for a repository, it's looping them all and running infinitely.

HTML File

 <div >
    <ul id="repos" *ngFor="let repo of repos | paginate:{ id:'listing_repos',itemsPerPage:10, currentPage:page, totalItems:totalRecords}">
      <li>
        <h4>{{ repo.name | uppercase}}</h4>
        <p>{{ repo.description }}</p>
          <ul *ngFor="let lang of languagesUsed(repo.name)">
              <li>{{lang}}</li>
          </ul>
      </li>
    </ul>
  </div>

Component.ts file for the same

export class UserComponentComponent implements OnInit {

  @Input() username:any;
  
  constructor(private githubUser:UserDataService) { }
  repos:Array<any> = [];
  avatar:any;
  name:String = '';
  location:String = '';
  bio:String = '';
  twitter:String = ''; 
  github:String = '';
  totalRecords = 0;
  page = 1;
  langs:Array<any> = [];
  

  getUser(){
    this.githubUser.getData(this.username).subscribe((data)=>{
        //console.log(data);

        this.avatar = data.avatar_url;
        this.name = data.name;
        this.location = data.location;
        this.bio = data.bio;
        this.twitter = `https://twitter.com/${data.twitter_username}`;
        this.github = `https://github.com/${data.login}`;
    })
  }

  getRepos(){
    this.githubUser.getRepositories(this.username).subscribe((repos)=>{
        this.repos = repos;
        this.totalRecords = repos?.length;
        // this.languagesUsed = Object.keys(repos.languages_url);
    })
  }

  languagesUsed(repoName:any){
      this.githubUser.getLanguages(this.username, repoName).subscribe((languages)=>{
        // console.log(Object.keys(languages));
        this.langs = Array.from(Object.keys(languages));
      })
      return this.langs;
    }

    ngOnInit(): any{
      this.getUser();
      this.getRepos();
    }

  }

Service.ts for fetching the API's

export class UserDataService {

  constructor(private http:HttpClient) {}

  getData(username:String):Observable<any>{

    let user = `https://api.github.com/users/${username}`;
    return this.http.get(user);
  }

  getRepositories(username:String):Observable<any>{
    let repos = `https://api.github.com/users/${username}/repos`;
    return this.http.get(repos);
  }

  getLanguages(username:String, repo:String):Observable<any>{
    let languages = `https://api.github.com/repos/${username}/${repo}/languages`;
    return this.http.get(languages);
  }
}

CodePudding user response:

This issue is caused because of languagesUsed function, couple of problems with it

  1. Used as a binding languagesUsed()
  2. Due to the async call, returned value this.langs won't be the right value.
  3. On each iteration, it will make an ajax call. This is causing an infinite loop

To fix this, you move the fetch languages logic as a serial operation (switchMap) on the observable level. As soon as you received a specific repo, make another call to bring the languages for that user. Thereafter extend the repo object with the languages array received.

Something like below

// after receiving the repos, 
const allRepoWithLanguages = repos.map(repo => 
  // loop over them and make a `getLanguages` ajax call
  this.githubUser.getLanguages(this.username, repo.name)
  .pipe(
     // extend repo with languages
     map(languages => ({...repo, languages }))
  )
);

Let's extend the logic just we've seen into actual implementation.

TS

getRepos(){
    this.githubUser.getRepositories(this.username).pipe(
      switchMap(repos => {
         const allRepoWithLanguages = repos.map(repo => 
            this.githubUser.getLanguages(this.username, repo.name)
            .pipe(
               // amending repo with languages
               map(languages => ({...repo, languages }))
            )
         );
         return forkJoin(allRepoWithLanguages);
      }),
    ).subscribe((repos)=>{
        this.repos = repos;
        this.totalRecords = repos?.length;
    })
}

HTML

<div >
   <ul id="repos" *ngFor="let repo of repos | paginate:{ id:'listing_repos',itemsPerPage:10, currentPage:page, totalItems:totalRecords}">
      <li>
        <h4>{{ repo.name | uppercase}}</h4>
        <p>{{ repo.description }}</p>
          <ul *ngFor="let lang of repo.languages">
              <li>{{lang}}</li>
          </ul>
      </li>
   </ul>
</div>
  • Related