这是一个简单的例子。我有一个可观察的
users$
存储从 API 获取的用户。我还有另一个可观察的 usersWithAction$
,当 CRUD 操作发生时,它使用 scan
运算符封装状态。
我对我的
usersWithAction$
可观察值有疑问。尽管我使用 merge
运算符的方法工作正常,但我觉得这个解决方案很糟糕,必须有更好的方法来实现。如果有任何建议或相关信息的链接,我将不胜感激。谢谢。
这里是data.service.ts:
interface User {
id: number;
name: string;
phone: string;
}
type ActionDelete = { action: 'delete'; id: number };
type ActionAdd = { action: 'add'; user: User };
@Injectable({ providedIn: 'root' })
export class DataService {
constructor(private http: HttpClient) {}
private actionSubject = new BehaviorSubject<'' | ActionAdd | ActionDelete>(
''
);
action$ = this.actionSubject.asObservable();
users$ = this.http
.get<User[]>('https://jsonplaceholder.typicode.com/users')
.pipe(shareReplay(1));
usersWithAction$ = merge(this.action$, this.users$).pipe(
scan((acc, value) => {
if (Array.isArray(value)) return [...value];
else {
if (value && value.action === 'delete') {
return acc.filter((user) => user.id !== value.id);
}
if (value && value.action === 'add') {
return [...acc, value.user];
}
}
}, []),
tap((data) => console.log(data, 'data')),
shareReplay(1)
);
deleteUser(id: number) {
// Do some API related requests
this.actionSubject.next({ action: 'delete', id: id });
}
addUser() {
// Do some API related requests
this.actionSubject.next({
action: 'add',
user: { id: 123, name: 'John Doe', phone: '234234' },
});
}
}
这里是app.component.ts:
export class AppComponent {
name = 'Angular ' + VERSION.major;
constructor(private dataService: DataService) {}
users$ = this.dataService.usersWithAction$;
deleteUser(id: number) {
this.dataService.deleteUser(id);
}
addUser() {
this.dataService.addUser()
}
}
这里是app.component.html:
<div *ngFor="let user of users$ | async" class="user-container">
<p>{{ user.name }}</p>
<p>{{ user.phone }}</p>
<button type="button" (click)="deleteUser(user.id)">Delete</button>
</div>
<button type="button" (click)="addUser()">Add</button>
以下是我认为可以简化代码的一些更改:
Subject
代替 BehaviorSubject
进行操作
merge
,使用获取的用户列表开始流,然后切换到发生修改的操作流的结果
private action$ = new Subject<ActionAdd|ActionDelete>();
users$ = this.http.get<User[]>('https://jsonplaceholder.typicode.com/users').pipe(
switchMap(initialUsersList => this.action$.pipe(
scan((users, action) => {
if (action.type === 'add') {
return users.concat(action.user);
}
if (action.type === 'delete') {
return users.filter(user => user.id !== action.id);
}
}, initialUsersList),
startWith(initialUsersList),
shareReplay(1),
)),
);
注意使用
startWith()
来最初发出获取的列表。我们还使用 initialUsersList
而不是空数组来初始化扫描状态。
这是一个 StackBlitz 演示。
如果愿意,您可以将状态更新逻辑提取到自己的纯函数中以提高可读性:
users$ = this.http.get<User[]>('https://jsonplaceholder.typicode.com/users').pipe(
switchMap(initialUsersList => this.action$.pipe(
scan(usersListReducer, initialUsersList),
startWith(initialUsersList),
shareReplay(1)
)),
);
这是一个有效的 StackBlitz 演示。