angular - Crud Service RXJS Angular 9 命令查询模式
问题描述
我正在尝试使用 rxjs 以角度构建 crud 服务。我有产品服务可以通过 getall、getbyid、post、pust、delete 方法与后端进行通信,最重要的是
product-facade-service 充当商店/服务并公开组件的公共 api,如下所示:
import { CrudAction, CrudOperation } from 'src/app/shared/facade-base';
@Injectable({
providedIn: 'root'
})
export class ProductFacadeService {
constructor(private productService: ProductClient) { }
// All products
getProducts$ = this.productService.get()
.pipe(
tap(data => console.log('Products', JSON.stringify(data))),
//shareReplay({bufferSize:1, refCount:1,}),
//shareReplay(1),
);
private productCrudActionSubject = new Subject<CrudAction<ProductResource>>();
productsWithUpdates$ = merge(
this.getProducts$,
this.productCrudActionSubject.asObservable(),
)
.pipe(
scan((acc: ProductResource[], action: CrudAction<ProductResource>) => {
if(action.operation === CrudOperation.Add){
return [...acc,action.entity];
}
else if(action.operation === CrudOperation.Update){
let updatedentity = acc.find(p => p['id'] == action.entity['id']);
updatedentity = action.entity;
return [...acc];
}
else if(action.operation === CrudOperation.Delete){
let deletedEntity = acc.find(p => p['id'] == action.entity['id']);
const index = acc.indexOf(deletedEntity);
if(index > - 1){
acc.splice(index,1)
}
}
return [...acc];
}),
catchError(err => {
console.error(err);
return throwError(err);
})
);
private addProductSubject = new Subject<ProductResource>();
addProductAction$ = this.addProductSubject.pipe(
mergeMap(productToBeAdded =>this.productService.post(productToBeAdded)),
tap(newProduct => this.productCrudActionSubject.next({operation :CrudOperation.Add,entity:newProduct}))
);
private updateProductSubject = new Subject<ProductResource>();
updateProductAction$ = this.updateProductSubject.pipe(
mergeMap(productTobeUpdated =>this.productService.put(productTobeUpdated.id,productTobeUpdated)),
tap(updatedProduct => this.productCrudActionSubject.next({operation :CrudOperation.Update,entity:updatedProduct}))
);
private deleteProductSubject = new Subject<ProductResource>();
deleteProductAction$ = this.deleteProductSubject.pipe(
mergeMap(productToBeDeleted => this.productService.delete(productToBeDeleted.id)),
tap(deletedProduct => this.productCrudActionSubject.next({operation :CrudOperation.Delete,entity:deletedProduct}))
);
private productSelectedSubject = new BehaviorSubject<number>(0);
selectedProduct$ = combineLatest([
this.productsWithUpdates$,
this.productSelectedSubject.asObservable()
]).pipe(
concatMap(([products, selectedProductId]) => {
if(selectedProductId === 0){
return of(this.intialize())
}
var found = products ? products.find(product => product.id == selectedProductId) : null;
if(found){
return of(found);
}
else
return this.productService.getById(selectedProductId);
}),
);
//Public api for component to invoke command....
save(product:ProductResource){
product.id === 0 ?
this.addProductSubject.next(product)
: this.updateProductSubject.next(product);
}
deleteProduct(product:ProductResource): void {
this.deleteProductSubject.next(product);
}
selectProduct(selectedProductId: number): void {
this.productSelectedSubject.next(+selectedProductId);
}
private intialize(): ProductResource {
return {
id: 0,
name: 'New',
unit : 'New',
pricePerUnitTaxInclusive :0,
};
}
}
现在我正在尝试构建两个组件产品列表来显示产品,用户可以根据需要删除并导航用户添加或编辑产品产品表单以创建或编辑新表单,并在创建用户时返回产品列表。
产品列表.ts
export class ProductListComponent implements OnInit{
products$ = this.productService.productsWithUpdates$;
constructor(
private productService: ProductFacadeService,private toastr: ToastrService
) { }
ngOnInit(){
//Code need improvement
this.productService.deleteProductAction$.pipe(
tap(deletedProduct=> this.toastr.success("Product Deleted :" + deletedProduct.name))
).subscribe();
}
onDelete(productToDelete){
if (confirm(`Are you sure you want to delete Product : ${productToDelete.name}`)) {
this.productService.deleteProduct(productToDelete);
}
}
}
产品形式.ts
export class ProductFormComponent implements OnInit,OnDestroy {
form: FormGroup = this.fb.group({
name: ['', Validators.required],
unit: ['', Validators.required],
pricePerUnitTaxInclusive: [, Validators.required],
});;
product$= this.productClient.selectedProduct$.pipe(
tap(res =>{
this.form.patchValue({
name: res.name,
unit: res.unit,
pricePerUnitTaxInclusive: res.pricePerUnitTaxInclusive,
})
})
);
//Code need improvement
onSave$ = combineLatest([this.productClient.addProductAction$.pipe(tap(product => this.toastr.success("New Produt Added : " + product.name))),
this.productClient.updateProductAction$.pipe(tap(product => this.toastr.success("Product Updated : " + product.name)))]
)
.subscribe(() => this.onSaveComplete());
ngOnInit() {
this.route.params.subscribe(param => {
this.productClient.selectProduct(param['id']);
});
}
ngOnDestroy(){
// this.onSave$.unsubscribe();
}
save(product:ProductResource): void {
console.log("Save invoked")
this.productClient.save(Object.assign({},product,this.form.value));
}
private onSaveComplete(): void {
this.form.reset();
this.router.navigate(['../'], { relativeTo: this.route });
}
}
代码的行为不同,因为它发出更多的删除放置或发布命令......不知道我在哪里犯错......因为我是 rxjs 的新手。也欢迎任何关于如何避免订阅 ts 的建议。我已经用注释标记了它们(//代码需要改进。)
解决方案
这是更新后的代码shareReplay(1)
。正如我上面提到的,它是在scan
. 否则,由 管理的数组scan
不会在操作之间适当地重用。
productsWithUpdates$ = merge(
this.getProducts$,
this.productCrudActionSubject.asObservable(),
)
.pipe(
scan((acc: PostResource[], action: CrudAction<PostResource>) => {
if(action.operation === CrudOperation.Add){
return [...acc,action.entity];
}
else if(action.operation === CrudOperation.Update){
let updatedentity = acc.find(p => p['id'] == action.entity['id']);
updatedentity = action.entity;
return [...acc];
}
else if(action.operation === CrudOperation.Delete){
let deletedEntity = acc.find(p => p['id'] == action.entity['id']);
const index = acc.indexOf(deletedEntity);
if(index > - 1){
acc.splice(index,1)
}
}
return [...acc];
}),
shareReplay(1), // <----------- HERE
catchError(err => {
console.error(err);
return throwError(err);
})
);
我还对 Stackblitz 进行了更新,您可以在此处找到:https ://stackblitz.com/edit/angular-crud-deborahk
尽管我在这次 stackblitz 中对您的原始版本进行了重大更改,包括将 update 更改为 one linemap
并将 delete 更改为 one line filter
。
推荐阅读
- c# - 测试时如何启动两个项目互相调用?
- java - 如何将数据从 Cassandra 复制到弹性搜索 6.7
- r - 根据排序不同的字符向量替换数据框中的列名
- dart - 简化的颤振块模式
- c++ - 使用相同的原型通过引用和值传递一次变量
- c# - 使用 Activator.CreateInstance 创建类的实例并将接口注入构造函数
- list - 为什么不提供函数时 list monad 工作
- selenium - 如何在 selenium-ide 中上传文件 3-7-4
- hadoop - 无需名称节点备份即可恢复 hdfs 元数据
- python - 形状为 b*n*3 的张量流张量 T1。形状为 b*n 的 T2 -> 一个布尔张量,指示要在 T1 中接收哪些行