angular - Crud Service RXJS Angular 9 命令查询模式

标签 angular rxjs crud cqrs

我正在尝试使用 rxjs 以 Angular 构建 crud 服务。
我有产品服务可以通过 getall、getbyid、post、pust、delete 方法与后端通信
最重要的是
product-facade-service 充当商店/服务并公开组件的公共(public) 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,
    };
  }
}

现在我正在尝试构建两个组件
用于显示产品的产品列表,用户可以根据需要删除并导航用户添加或编辑产品
product-form 创建或编辑新表单,创建后用户返回产品列表。
产品列表.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
尽管我在这次堆栈 Blitz 中对您的原始版本进行了重大更改,包括将更新更改为单行 map并删除一行filter .

关于angular - Crud Service RXJS Angular 9 命令查询模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64468661/

相关文章:

php - 更新不起作用的 PHP MYSQL CRUD

angular - 在 Angular 中拦截 HTTP header

angular - Angular + Grails:刷新页面或键入URL

javascript - 隐藏多条路线上的某些组件 Angular 5

angular - 什么 rxjs 运算符类似于 concatmap 但在触发下一个请求之前等待每个请求?

javascript - NodeJS CRUD api 删除方法出现问题

html - CSS 溢出滚动条裁剪背景颜色

angular - Angular 4 中的 Websocket 和 RxJS 混淆

javascript - 有没有办法将 rxjs 导入普通的 js web 自定义组件?

java - Spring数据保存不更新持久化上下文中的PK?