Angular - 如何使用 switchMap 从 Wordpress REST API 获取帖子、标签和评论计数,然后使用 JavaScript 组合结果值?

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

我正在开发一个 Angular 应用程序,它需要从 Wordpress REST API 获取帖子、它们各自的标签名称和评论数。使用 API 三个 GET 请求需要发送到服务器。第一个请求正在发送到 /wp/v2/posts。服务器的响应如下所示:

摘录posts request

[
    {
        "id": 9,
        "title": {
            "rendered": "Hello World! 3"
        },
        "tags": [
            2,
            3,
            4
        ]
    },
    {
        "id": 4,
        "title": {
            "rendered": "Hello World! 2"
        },
        "tags": [
            2,
            3
        ]
    }
    ...
]

tags 数组中的数字不是实际的人类可读标签名称,它们只是标签的标识符。在我们收到服务器的第一个响应后,我们需要向服务器发送另一个 GET 请求到 /wp/v2/tags?include=2,3,4,同时提供标签标识符。我们不需要数据库中可用的每个标签,只需要在对第一个请求的响应中引用的标签。换句话说,第二个请求取决于第一个请求的结果。来自服务器的第二个请求的响应如下所示:

摘录tags request

[
    {
        "id": 8,
        "name": "test"
    },
    {
        "id": 9,
        "name": "test2"
    },
    {
        "id": 30087,
        "name": "test3"
    }
]

第三个也是最后一个请求需要发送到 /wp/v2/comments?post=3,4,9。服务器的响应如下所示:

摘录comments request

[
   {
      "id":3,
      "post":4,
      "content":{
         "rendered":"<p>This is an example comment number 3.<\/p>\n"
      }
   },
   {
      "id":2,
      "post":9,
      "content":{
         "rendered":"<p>This is an example comment number 2.<\/p>\n"
      }
   }
]

经过研究,我发现 Angular RxJS 的 switchMap 运算符似乎是可行的方法。我的要求如下:

然后 .subscribe() 进入整体流程并获得帖子、标签和评论的组合对象,然后可以使用 NgForOf 在 Angular 模板文件中对其进行迭代。

到目前为止,我在 Stackblitz 中的代码看起来 like this。对我的代码的分叉将不胜感激。在编写解决方案时,我遇到了一个我不完全理解的控制台错误:Cannot read properties of undefined (reading 'map')我需要解决这个问题才能继续工作。

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.

错误的原因是在第 2 个 switchMap 中,您只返回 comments 数组。

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

您可以通过从 map:

返回如下内容来克服错误
.pipe(map((result) => ({ ...object, comments: result })));

现在在 subscribe 内您可以获得结果:

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


因为获取tagscomments的调用是相互独立的,你可以利用forkJoinRxJS运算符,它基本上等待所有 Observable 完成,然后合并结果。

另一种方法:

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>

您可以阅读更多关于上述代码中使用的两个概念:

  • forkJoin RxJS 运算符,它基本上等待所有 Observable 完成,然后合并结果。
  • async 管道,订阅 Observable 或 Promise 和 returns 它发出的最新值