Angular inbäddad *ngFor ger oändliga resultat när den används i en HTML-mall


Jag använder Github API för att bygga en Repositories Viewer. Jag tar repo.name som en input för en annan variabel och skickar det som en parameter till en funktion som faktiskt anropas för att hämta språken som används i ett repository.

Problemet är att det inte visar enskilda språkset för ett arkiv, det loopar igenom dem alla och kör oändligt.

HTML-fil

 <div class="user-repos">
    <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-fil för samma

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 för att hämta API:erna

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);
  }
}

Svar #1

Detta problem orsakas på grund av languagesUsed-funktionen, ett par problem med den

  1. Används som en bindande languagesUsed()
  2. På grund av async-anropet kommer det returnerade värdet this.langs inte att vara rätt värde.
  3. Vid varje iteration kommer det att göra ett ajax-anrop. Detta orsakar en oändlig loop

För att åtgärda detta flyttar du hämtlogiken för språk som en serieoperation (switchMap) på observabelnivån. Så snart du har mottagit ett specifikt repo, gör ett nytt anrop för att hämta languages för den user. Därefter utökar du repo-objektet med den mottagna languages-arrayen.

Något liknande nedan

// 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 }))
  )
);

Låt oss utvidga den logik vi just har sett till faktisk implementering.

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 class="user-repos">
   <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>