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
- Used as a binding
languagesUsed()
- Due to the
async
call, returned valuethis.langs
won't be the right value. - 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>