在 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'});
因此,为了使 post
与 selectedPost$ | 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
方法,例如也正确处理 undefined
和 null
值。
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'});
因此,为了使 post
与 selectedPost$ | 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
方法,例如也正确处理 undefined
和 null
值。