为多个 'observable consumers' 创建一个订阅
Create one subscription for multiple 'observable consumers'
经理状态有为用户提供的商店服务。在商店中有一个 'select' 方法映射以获取用户 ID:
type User = { userId: number };
@Injectable({ providedIn: "root" })
export class SomeService {
activeUsers$ = new BehaviorSubject<User[]>([]);
selectUserIds = (): Observable<number[]> => {
return this.activeUsers$
.asObservable()
.pipe(map((data) => data.map(({ userId }) => userId)));
};
// ...
}
在我调用该方法以查看哪个用户处于活动状态的组件中:
@Component({
selector: "app-root",
template: `
<div>
<h1>See console output</h1>
<ng-container *ngFor="let user of users">
<div [ngClass]="{'active': (activeUserIds$ | async).includes(user.userId)}">
{{ user.userName }}
</div>
</ng-container>
</div>`,
})
export class AppComponent implements OnInit {
activeUserIds$: Observable<number[]>;
users = [
{ userId: 1, userName: "John" },
{ userId: 2, userName: "Fred" },
{ userId: 3, userName: "Alex" }
];
constructor(private someService: SomeService) {}
ngOnInit(): void {
this.activeUserIds$ = this.someService.selectUserIds().pipe(
tap((data) => {
console.log("data: ", data);
}),
);
}
}
但是在控制台中,它在模板中被调用的次数(三次)发生了:
// console output:
// data: []
// data: []
// data: []
做三遍好像是多余的。但我确实希望它对状态更新做出反应。
我的想法是,通过在 OnInit 中创建 'activeUserIds$',我将可以观察到 1 并多次使用它。但它似乎订阅了多次。
只有 1 个订阅的方法是什么?
沙盒link:https://codesandbox.io/s/red-bird-n7xips?file=/src/app/app.component.ts
当您在 *ngFor
中使用 async
时,您正在创建三个订阅。它不知道 activeUserIds$ | async
会在您每次订阅它时发出相同的值。
一种解决方案是在模板中使用局部变量,并使用来自 NgRx 的 ngrxLet
等指令仅订阅一次。
在原版 Angular 中,最简单的方法(没有 *ngIf
and this https://github.com/angular/angular/issues/15280)可能是将 shareReplay(1)
和 take(1)
附加到链中,以便它立即完成并且不会订阅源 Observable:
this.activeUserIds$ = this.someService.selectUserIds().pipe(
tap((data) => {
console.log("data: ", data);
}),
shareReplay(1),
take(1),
);
现场演示:https://stackblitz.com/edit/angular-ivy-woypne?file=src%2Fapp%2Fapp.component.ts
经理状态有为用户提供的商店服务。在商店中有一个 'select' 方法映射以获取用户 ID:
type User = { userId: number };
@Injectable({ providedIn: "root" })
export class SomeService {
activeUsers$ = new BehaviorSubject<User[]>([]);
selectUserIds = (): Observable<number[]> => {
return this.activeUsers$
.asObservable()
.pipe(map((data) => data.map(({ userId }) => userId)));
};
// ...
}
在我调用该方法以查看哪个用户处于活动状态的组件中:
@Component({
selector: "app-root",
template: `
<div>
<h1>See console output</h1>
<ng-container *ngFor="let user of users">
<div [ngClass]="{'active': (activeUserIds$ | async).includes(user.userId)}">
{{ user.userName }}
</div>
</ng-container>
</div>`,
})
export class AppComponent implements OnInit {
activeUserIds$: Observable<number[]>;
users = [
{ userId: 1, userName: "John" },
{ userId: 2, userName: "Fred" },
{ userId: 3, userName: "Alex" }
];
constructor(private someService: SomeService) {}
ngOnInit(): void {
this.activeUserIds$ = this.someService.selectUserIds().pipe(
tap((data) => {
console.log("data: ", data);
}),
);
}
}
但是在控制台中,它在模板中被调用的次数(三次)发生了:
// console output:
// data: []
// data: []
// data: []
做三遍好像是多余的。但我确实希望它对状态更新做出反应。
我的想法是,通过在 OnInit 中创建 'activeUserIds$',我将可以观察到 1 并多次使用它。但它似乎订阅了多次。
只有 1 个订阅的方法是什么?
沙盒link:https://codesandbox.io/s/red-bird-n7xips?file=/src/app/app.component.ts
当您在 *ngFor
中使用 async
时,您正在创建三个订阅。它不知道 activeUserIds$ | async
会在您每次订阅它时发出相同的值。
一种解决方案是在模板中使用局部变量,并使用来自 NgRx 的 ngrxLet
等指令仅订阅一次。
在原版 Angular 中,最简单的方法(没有 *ngIf
and this https://github.com/angular/angular/issues/15280)可能是将 shareReplay(1)
和 take(1)
附加到链中,以便它立即完成并且不会订阅源 Observable:
this.activeUserIds$ = this.someService.selectUserIds().pipe(
tap((data) => {
console.log("data: ", data);
}),
shareReplay(1),
take(1),
);
现场演示:https://stackblitz.com/edit/angular-ivy-woypne?file=src%2Fapp%2Fapp.component.ts