我正在重写一些代码以使用RXJS和角度异步管道。代码非常简单,我们将要添加和删除数组中的项目。是否有比使用轻按器集将状态存储在BehaviorSubject中更好的方法来完成对共享数组的操作?
this.listSubject = new BehaviorSubject(['a','b','c']);
this.addSubject = new Subject<string>();
this.addAction$ = this.addSubject.asObservable().pipe(
withLatestForm(this.listSubject),
map(([itemToAdd, list]) => {list.push(itemToAdd); return list;}),
tap((data) => this.listSubject.next(data))
);
this.removeSubject = new Subject<number>();
this.removeAction$ = this.removeSubject.asObservable().pipe(
withLatestForm(this.listSubject),
map(([indexToRemove, list]) => {list.splice(indexToRemove, 1); return list;}),
tap((data) => this.listSubject.next(data))
);
this.list$ = merge(this.addAction$, this.removeAction$);
编辑:UI代码使用异步管道
list$ | async
答案 0 :(得分:3)
类似的作品:
export class HelloComponent {
actionSubject = new Subject<Action>();
action$ = this.actionSubject.asObservable();
originalList$ = of(["a", "b", "c"]);
list$ = merge(this.originalList$, this.action$).pipe(
scan((acc: string[], action: any) => {
if (action.isDelete) {
return acc.filter(item => item !== action.item);
} else {
return [...acc, action.item];
}
})
);
onAdd() {
this.actionSubject.next({ item: "z", isDelete: false });
}
onRemove() {
this.actionSubject.next({ item: "b", isDelete: true });
}
}
export interface Action {
item: string;
// This could instead be an Enum of operations
isDelete: Boolean;
}
注意:我在这里对您的代码进行了快速整理:https://stackblitz.com/edit/angular-array-processing-deborahk
我创建了一个动作流,该动作流发出了一个动作对象,其中包含要添加/删除的项以及该动作是否为删除项。 (您可以将其更改为用于添加和删除的枚举)
然后我使用scan
来保留一段时间内的项目集,并简单地添加到该项目列表中或从中删除。
您的代码在app.component.ts
文件中
修改后的代码位于hello.component.ts
文件中。
这对您的情况有用吗?
注意:对于那些说您需要订阅的人……如果您使用异步管道,则不需要。我的用户界面如下所示:
<div>{{ list$ | async}}</div>
答案 1 :(得分:2)
通常来说,tap
的大多数用法将与RxJS硬衬里有关。
我最大的担心是,您确实应该使用订阅而不是轻按来修改应用程序状态(行为主题)。您目前没有显示list$
的任何订阅者。如果没有人订阅,那么您的tap
将永远不会运行。另一方面,如果list$
最终有两个订阅者,则每个事件的抽头操作将运行两次!您可以在某个地方放置publish
来缓解这种情况,但我认为如果您......会更干净
this.listSubject = new BehaviorSubject(['a','b','c']);
this.addSubject = new Subject<string>();
this.addSubject.subscribe(itemToAdd => {
const currentValue = this.listSubject.value;
currentValue.push(itemToAdd);
this.listSubject.next(currentValue);
});
this.removeSubject = new Subject<number>();
this.removeSubject.subscribe(indexToRemove => {
const currentValue = this.listSubject.value;
currentValue.splice(indexToRemove, 1);
this.listSubject.next(currentValue);
});
我要考虑的第二个问题(稍次)是您要从多个主题中重新提交列表。您现在拥有三个主题,每个主题都可以发出相同的列表。这意味着您有三个潜在的真理来源。您还违反了CQS(命令查询分隔)。当某些人调用addItem
时,不需要获取新的列表值。取而代之的是,无论消耗列表的什么,都会简单地获取更新,因为您的列表主题已更新。
额外阅读:在您的应用中,您具有状态(列表)和可以修改状态的事件。或者,更一般而言,可以修改您的状态的“动作”。在ngrx
之类的工具中,您会看到诸如“商店”(您的状态)和“动作”(您的事件)之类的概念来描述此模型。行为主题是ngrx
存储之类的轻量级替代品。随着您的应用程序变得越来越复杂,您可以通过阅读ngrx store之类的工具获得很多好处。
答案 2 :(得分:1)
为什么不使用函数?
list$ = new BehaviorSubject(['a','b','c']);
add(val) {
this.list$.next([...this.list$.value, val]);
}
remove(val) {
this.list$.next(...this.list$.value.filter(v => v !== val));
}
您的代码不清楚,所有这些可观察对象的订阅范围是什么。您将需要订阅可观察到的动作以触发任何功能。您正在变异对象。
答案 3 :(得分:0)
看起来非常不错的代码,我唯一想添加的就是不变性。这样,您每次在其中添加或删除项目时都会得到一个新列表:
this.addAction$ = this.addSubject.asObservable().pipe(
withLatestForm(this.listSubject),
map(([itemToAdd, list]) => [...list, itemToAdd]),
tap((data) => this.listSubject.next(data))
);
this.removeAction$ = this.removeSubject.asObservable().pipe(
withLatestForm(this.listSubject),
map(([indexToRemove, list]) =>
[...list.slice(0, indexToRemove), ...list.slice(indexToRemove + 1)]),
tap((data) => this.listSubject.next(data))
);