0

角度专家!我试图理解 Angular 中的异步管道,但我陷入了一个基本场景。我在 UI 中有两个选择元素,一个包含帖子,一个包含相关评论。我想为显示帖子的选择元素设置一个帖子(最后一个)作为最初选择的一个,并且我想使用所选项目来过滤第二个选择中的相关评论。这在我的代码中不起作用,为此我在 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>
4

1 回答 1

2

Angular 默认通过引用比较对象

你快到了。问题是你select得到一个option引用Posts 的 s 列表作为ngFor. 现在要找出option当前选择post的对象,Angular 将每个对象与selectedPost$ | async.

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

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

因此,为了post算作与 相同的帖子selectedPost$ | async,它们必须是实际相同的对象,而不仅仅是看起来相同的对象。

您实际上检索了同一篇文章的多个副本,而不仅仅是一篇文章

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

在此处输入图像描述

所有请求的响应载荷都是相同的,但是由于Post对象被返回了 3 次,所以它们在内存中存储了 3 次。JavaScript 无法知道它们实际上是否相同,并且===比较返回false.

解决方案:提供您自己的compareWith功能

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

您现在可以为 Angular 编写自己的指令来比较对象或您的选择:只需在compareWith选择中添加一个输入:

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

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

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

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

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

于 2020-04-27T18:57:22.530 回答