在 Angular 中使用异步管道设置 select 元素的 select 项

Setting selected item of a select element using async pipes in Angular

Angular专家!我试图理解 Angular 中的异步管道,但我被困在一个基本场景中。我在 UI 中有两个 select 元素,一个包含帖子,一个包含相关评论。我想为显示帖子的 select 元素设置一个帖子(最后一个)作为最初 selected 的帖子,我想使用 selected 项目过滤相关评论第二个select。这在我的代码中不起作用,为此我在 Stackblitz 中创建了一个简化版本:

https://stackblitz.com/edit/angular-p6ynuy

你们中的任何人都可以向我解释我做错了什么吗?这是相关的代码片段 HTML:

ngOnInit() {
    this.postList$ = this.getPostList();

    // latestPost$ is not is use yet, but maybe it could be used to set the selected post?
    this.latestPost$ = this.postList$ 
      .pipe(
        map(posts => posts[posts.length - 1])
      );

    this.selectedPost$ = combineLatest([
      this.postList$,
      this.postSelectedAction$
    ])
      .pipe(
        map(([posts, selectedPostId]) => posts.find(post => post.id === selectedPostId))
      );

    this.commentList$ = this.selectedPost$
      .pipe(switchMap(
        post => this.getCommentList(post)
      ));
  }


<select [ngModel]="selectedPost$ | async" (change)="onSelected($event.target.value)">
  <option *ngFor="let post of postList$ | async" [ngValue]="post">
    {{post.id}} {{post.title}}
  </option>
</select>
<select>
  <option *ngFor="let comment of commentList$ | async" [ngValue]="comment">
    {{comment.id}} {{comment.postId}} {{comment.name}}
  </option>
</select>

Angular 默认按引用比较对象

你快到了。问题是您的 select 获得了 option 的列表,其中引用 Post 作为 ngFor 的一部分。现在要找出哪个 option 当前被 select 编辑,Angular 将每个 post 对象与 selectedPost$ | async 的当前值进行比较。

默认情况下,完成的方式是使用 === 运算符。 === 运算符按值比较基元,但按引用比较对象。示例:

console.log('a' === 'a');
const obj = {'a': 'b'};
const obj2 = obj;
console.log(obj === obj2);
console.log(obj === {'a': 'b'});

因此,为了使 postselectedPost$ | async 算作相同的 post,它们必须是实际相同的对象,而不仅仅是看起来相同的对象.

您实际上检索了相同内容的多个副本 Post,而不是一个 Post

现在情况并非如此:当您使用 async 管道时,可能会在更改检测期间从 API 重新加载 post。当您查看浏览器的网络选项卡时,您实际上可以看到有三个请求:

所有请求的response payload是一样的,但是由于Post对象被返回了3次,所以在内存中存储了3次。 JavaScript没办法知道其实是一样的,===比较returnsfalse.

解决方案:提供您自己的 compareWith 函数

你怎么解决这个问题?您可以帮助 Angular 在 select 中正确比较 Post 个对象。您只需要问自己一个问题:我怎么知道两个 Post 对象实际上是同一个对象?在这种情况下,答案是:当它们具有相同的 ID 时。

您现在可以为 Angular 编写自己的指令来比较对象或您的 select:只需将 compareWith 输入添加到 select:

<select [ngModel]="selectedPost$ | async" 
(change)="onSelected($event.target.value)"
[compareWith]="comparePosts"
>

现在 Angular 知道使用一种名为 comparePosts 的方法来比较两个 post。现在该方法看起来如何?例如像这样:

comparePosts(p1: Post, p2: Post) {
    return p1.id === p2.id;
}

现在 Angular 知道如何正确比较两个 Post 对象,你的问题就解决了。

PS:请确保写出比我写的更好的 comparePosts 方法,例如也正确处理 undefinednull 值。