0

I'm building an Angular app to show my website's articles and votes and I want to show them sorted based on their vote. I can retrieve votes making http calls, in particular:

  1. I have to make a http call to retrieve the list of categories and the list of their IDs (I often add categories in my website so I need to update my app)
  2. I have to use these IDs to make one http call for each ID to get the list of articles of that particular category.
  3. Every article has an ID, I have to store every ID to make another one http call for each article ID.
  4. Finally every n.3) response object has a 'users_vote' property. I need then to build an array to merge all the votes and then sort the array. Then I'll show the array on HTML code.

I don't understand how to chain promises so the code is just to clarify what I mean. I also know that it's really 'tricky' but I can't change anything server side at the moment and I have to deal with this.

 ngOnInit() {
    this.getCategories()
    .then((res) => {
      this.getIds(res)
      .then((res) => {
        this.getArticlesList(res)
        .then((res) => {
          this.mergeArticlesList(res)
        })
      })
    })
  }


// 1) getCategories() to get the list 

  getCategories() {
      var promise = new Promise((resolve) => {
        this.myService.getCategoriesList()
        .subscribe((res) => {
          this.categories = res;
          resolve(this.categories);
        })
      })
      return promise;
    }

// 2) getCategoriesIDs(res) to get their IDs and store them in this.ids

  getCategoryIDs(res) {
    var promise = new Promise((resolve) => {
      for (let i=0; i < Object.keys(res).length; i++) {
        this.ids[i] = res[i+1]['id']
      }
      resolve(this.ids)
    })
    return promise;
  }

// 3) getArticlesList(res) to get the list of articles of each category

  getArticlesList(res) {
    var promise = new Promise((resolve) => {
      for (let i=0; i < Object.keys(res).length; i++) {
        this.myService.getArticlesOfCategory(res[i])
        .subscribe((res) => {
          this.allarticleslist[i] = res;
        })
      }
      resolve(this.allarticleslist)
    })
    return promise;
  }

// 4) mergeArticleIds(res) to create another array of ids (this time is // the list of articles' IDs

  mergeArticleIds(res) {
    console.log("Start merging with:")
    console.log(res)
    console.log(res.length)
    console.log(Object.keys(res).length)
    for (let i=1; i < Object.keys(res).length -1; i++) {
      for (let _i=0; _i < res[i]['reviews'].length; _i++) {
        this.mergedArticles = res[i]['reviews'][_i]['id']      }
    }
    return this.mergedArticles
  }

// 5) getArticle(res) to get the article with that particular ID
// With this http call I finally can get 'article_vote' and I have to 
// save this value in order to sort the array. 
// It has to return the array sorted

  getArticle(res) {
    // ??
 }

}

consolelog

It's not working because console.log(res.length) returns 0, so for loop doesn't even start. I know I have to deal with asynchronous operations timing too, but I don't know how to do it.

EDIT: adding getCategoriesList() from myService:

getCategoriesList(): Observable<any[]> {
    let url = 'myurl';
    return this.httpClient.get(url)
}

EDIT2: adding getCategoriesList()'s http.get response:

{"5":{"description":"","title":"SOCCER""term_id":280,"room_name":"SOCCER"},
"4":{"description":"","title":"NFL","term_id":281,"room_name":"NFL"},
"3":{"description":"","title":"MLB","term_id":282,"room_name":"MLB"},
"2":{"description":"","title":"NHL","term_id":283,"room_name":"NHL"},
"6":{"description":"","title":"TENNIS","term_id":284,"room_name":"TENNIS"},
"1":{"description":"","title":"F1","term_id":285,"room_name":"F1"}}
6
  • Are you not using Observables? By default, Angular HTTPClient returns an observable. If you want then we can provide a solution using Observable which you can convert it to promises. Let us know Commented Jun 17, 2019 at 15:16
  • You have no console.log in the code you're showing here, and all of the functions use the variable name res somewhere in them, so it's not clear which one is showing undefined... As @user2216584 mentioned, HttpClient returns an Observable, which has a toPromise() method on it, so you never have to create new promises like you are. You can also just use Observable to do what you need. Commented Jun 17, 2019 at 15:19
  • If you are going to use promises, there are several posts on Stack Overflow about how to chain them, including Dynamic Chaining in Javascript Promises, Javascript Angular: how to chain unknown number of promises Commented Jun 17, 2019 at 15:21
  • I've added some logs to let you understand what I mean...the http calls returns observable, it was my mistake, I'm sorry Commented Jun 17, 2019 at 15:52
  • @user2216584 it's okay, I can use Observables too Commented Jun 17, 2019 at 16:09

2 Answers 2

2

Please try following code -

// assuming that this.myService.getCategoriesList() returns an observable which has all categories
    this.myService.getCategoriesList()
        .pipe(
          switchMap(categories => {
            // NOTE - You should use category Id; I dont know the property which represents categoryId;
            // Replace cat.categoryId with your ID of a category
            const observables = categories.map(cat => this.myService.getArticlesOfCategory(cat.categoryId));
            return forkJoin(observables);
          }),
          switchMap(arrayOfArticlesByCategoryId => {
            // NOTE - You should use article Id; I dont know the property which represents articleId;
            // Replace article.articleId with your ID of an article
            const observables = arrayOfArticlesByCategoryId.map(article => this.myService.getArticle(article.articleId));
            return forkJoin(observables);
          }),
          map(arrayOfallArticles => {
            //You can also use 'pluck' operator - https://www.learnrxjs.io/operators/transformation/pluck.html
            return arrayOfallArticles.map(article => article.users_vote);
          }),
          map(arrayOfUsersVote => {
            console.log(arrayOfUsersVote);
            // Do whatever you want to do with arrayOfUsersVote i.e. sort them
          })
        ).subscribe();
Sign up to request clarification or add additional context in comments.

9 Comments

Thanks a lot! However it throws error "categories.map is not a function"...I've read it could be because of the type of categories and in fact it is the only one of type 'any'...any idea? I imported map and switchMap from rxjs/operators, is it right?
@Federico I have assumed that this.myService.getCategoriesList() returns Observable<Categories[]> [i.e. Array of Category] wrapped in an observable. If its not correct then you have to adjust the code as per return type of this.myService.getCategoriesList().
I've updated the question with my service's method...it's the first time I deal with Observables so I'm trying to understand how they work. Should I create something like a class Categories?
@Federico Your updated code looks fine. Just make sure that your backend returns the list of categories. If it returns an array then the code which I suggested should work.
you were right, getCategoriesList() returns an Object but I can't work with backend for a while, so should I convert that Object into an array?
|
2

You can easily chain promises by creating an array of promises like that

let promiseArray: Promise<any>[] = [];

Then you can add Promise function by using push

var promise = new Promise();
promiseArray.push(promise);

You can use Promise.all(promiseArray).then((responses) => { ... }) it will resolved all promises in your array

I hope it will help you !

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.