Search code examples
angularrxjsobservable

How to add item in rxjs observable array in angular


I have an angular service that consumes rest api.

@Injectable({
  providedIn: 'root'
})
export class ProductService {

  private url: string = "/app/products";

  constructor(private http: HttpClient) {
  }

  get(caregoryId: string) {
    return this.http.get<Product[]>(`${this.url}`)
      .pipe(map(products => products.filter(p => p.caregoryId == caregoryId)))
  }

  add(p: Product) {
    return this.http.post<Product>(this.url, p);
  }
}

And my component is:

export class ProductComponent implements OnInit{

     items: Observable<Array<Product>>;

     ngOnInit() {
        this.route.params.subscribe(
          (params: any) => {
            this.items = this.productService.get(params.id);            
          })
     }

    addWidget() {
        let item: any = { name: "p-1", color: "red" } }

        this.productService.add(item))
        /// how to add created item in my items array ?

    }
}

I can get list of products and list them using async pipe in html. But I could not add the created item in my observable array. How can I do it?


Solution

    1. Instead of reassigning the items$ every time you perform an operation in list (add, remove, update, etc.), you could use rxjs#Subject, rxjs#merge and rxjs#scan to control the data flow;
    2. You don't have to manually subscribe to route param changes, you could just use rxjs#switchMap and do something like this:

    this.activatedRoute.paramMap.pipe( map((params) => params.id), switchMap((id) => this.productsService.getItems(id)) )

    1. You have a typo in a service method: caRegory :)

    With these changes, we have this:

    @Component({
      // ...
    })
    export class AppComponent {
      readonly items$: Observable<readonly Product[]>;
      private readonly insertedItemSource$ = new Subject<Product>();
      private readonly insertedItem$ = this.insertedItemSource$.asObservable();
    
      constructor(
        private readonly activatedRoute: ActivatedRoute, 
        private readonly productsService: ProductsService
      ) {      
        this.items$ = merge(          
          this.activatedRoute.params.pipe(
            map((params) => params.id),
            switchMap((id) => this.productsService.getItems(id))
          ),
          this.insertedItem$
        ).pipe(scan((accumulator, value) => [...accumulator, value]));
      }
    
      addWidget(): void {
        const item: Product = { color: 'purple', name: 'p-3' };
        this.productsService.add(item).subscribe(itemInserted => this.itemInsertedSource$.next(itemInserted));   
      }
    }
    

    STACKBLITZ DEMO