Angular - How to get posts, tags and comment count from Wordpress REST API using switchMap and then combine the resulting values using JavaScript?

I experienced a console error I don't fully understand: Cannot read properties of undefined (reading 'map') I need to fix this in order to continue the work.

The reason for the error is that from within 2nd switchMap, you are only returning comments array.

// here result is the response of /wp/v2/comments?post=3,4,9
.pipe(map((result) => ({ result })));


.subscribe(({ result }) => {
  // here result would be comments array and hence result.questions is undefined
  // result.questions.map results in trying to read 'map' of undefined
}

You can overcome the error by returning something as below from map:

.pipe(map((result) => ({ ...object, comments: result })));

and now within subscribe you can get the result:

.subscribe(result => {
  // here result will be an object -> {questions: Array[3], tags: Array[3], comments: Array[3]}
  console.log(result);
  ...
}


Since the call to get tags and comments are independent of each other, you can make use of forkJoin RxJS operator, which basically waits for all the Observables to complete and then combines the result.

Another approach:

export class AppComponent {
  constructor(private questionService: QuestionService) {}

  // Instead of 'any' you can define your own type as per your use case
  mergedArrays$: Observable<any>;

  // This entire method logic can actually be moved within the service class
  getQuestions(): Observable<any> {
    return this.questionService.getQuestions().pipe(
      switchMap((questions: Question[]) => {
        const tagIDs = questions.map((question) => question.tags).join(',');
        const postIDs = questions.map((question) => question.id).join(',');

        return forkJoin({
          questions: of(questions),
          tags: this.questionService.getTags(tagIDs),
          comments: this.questionService.getAnswers(postIDs),
        });
      }),
      map(({ questions, tags, comments }) => {
        const mergedArrays = questions.map((question) => {
          return {
            ...question,
            tag_names: question.tags
              .map((tagId) => tags.find((tag) => tag.id == tagId)?.name)
              .filter((exists) => !!exists),
            comments: comments.filter((comment) => comment.post === question.id),
          };
        });
        return mergedArrays;
      })
    );
  }

  ngOnInit(): void {
    this.mergedArrays$ = this.getQuestions();
  }
}
<div class="container m-5">
  <h1>All Questions</h1>
  <hr />
  <ul class="list-group list-group-flush">
    <!-- Using async pipe in *ngFor -->
    <li class="list-group-item" *ngFor="let question of mergedArrays$ | async">
      <p>
        <b>{{ question.title.rendered }}</b>
      </p>
      <span>tags: </span>
      <span *ngFor="let tag_name of question.tag_names">
        <a href="#" class="badge bg-secondary">{{ tag_name }}</a>
      </span>
      <!--  Display comments -->
      <div>Comments ({{ question?.comments?.length }}):</div>
      <div *ngFor="let comment of question.comments">
        <span class="" [innerHTML]="comment?.content?.rendered"></span>
      </div>
    </li>
  </ul>
</div>

You can read more about the two concepts used in the above code:

  • forkJoin RxJS operator, which basically waits for all the Observables to complete and then combines the result.
  • async pipe, which subscribes to an Observable or Promise and returns the latest value it has emitted