【问题标题】:Setting selected item of a select element using async pipes in Angular在Angular中使用异步管道设置选择元素的选定项
【发布时间】:2020-08-11 10:26:39
【问题描述】:

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


    【解决方案1】:

    Angular 默认通过引用比较对象

    你快到了。问题是您的select 获取options 的列表,引用Posts 作为ngFor 的一部分。现在要找出当前选择了哪个option,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 被视为同一帖子,它们必须是实际相同的对象,而不仅仅是看起来相同的对象。

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

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

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

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

    你怎么能解决这个问题?您可以帮助 Angular 在您的 select 中正确比较 Post 对象。你只需要问自己一个问题:我怎么知道两个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 值。

    【讨论】:

    • 感谢 fjc,它工作得很好,我学到了一些新东西!请问您是否能够发现为什么当我在第一个选择中选择一个帖子时第二个选择(cmets)没有更新?
    • 不客气。据我所见,评论选择没有更新,因为你没有将它与任何值联系起来。对于您的帖子选择,您有一个 ngModel 集。对于您的评论选择,没有这样的事情。
    • 你是对的,我打算添加一个 ngModel 来获取选定的评论,但现在我只想通过选定的帖子过滤 cmets 列表。正如您在第 38 行看到的那样,我正在尝试使用 switchMap 来执行此操作。我想这不是这样做的方法。你有更好的建议吗?
    • 我自己发现了这个错误。我预计 onSelected 中的输入参数错误。再次感谢您的宝贵帮助!
    • 很好,很高兴你做到了。我还没有机会去看看。不客气,很高兴看到一个写得很好、很清楚的问题,其中有一个实际可重现的问题来进行更改。
    猜你喜欢
    • 1970-01-01
    • 2018-10-22
    • 2017-05-30
    • 1970-01-01
    • 2018-02-24
    • 2010-11-16
    • 1970-01-01
    • 2017-12-31
    • 1970-01-01
    相关资源
    最近更新 更多