1

I've spent the last few days learning about observables and have started to embrace them in my Angular application. I have a Web API app that I am producing a client for through NSwagClient in typescript. I have an API action that has this as its signature:

Task<ActionResult<List<Product>>> GetProducts(int skip, int take)

The API client I have in typescript is invoked like this:

_client.GetProductsAsync(0, 10).subscribe(next => {console.log(next)}, err => {}, () => {})

In my mind, when I ask for the next load of products, (so, skip 10, take 10, and then skip 20, take 10), I need to update the parameters of the initial _client.GetProductsAsync call. But seemingly, I can't do this without unsubscribing and re-subscribing to the observable with the new parameters. This seems to work against the concept of observables in my mind as streams of data, as the observable should be created once and then somehow "triggered" to get more results and pipe them to the Observer.

How can I subscribe once to the API client and accomplish this?

Lewis Cianci
  • 926
  • 1
  • 13
  • 38

3 Answers3

2

You will want to have a Subject that contains the current page and make use of one of these type of RxJS operators: concatMap, switchMap, or mergeMap.

The page Subject will trigger the observable chain when a new value is pushed onto it.

page$ = new Subject<number>();

In your html template you will need some content that builds out your pagination links/buttons that will push a new value on your page subject when you click it:

<button (click)="page$.next(pageNum)">{{ pageNum }}</button>

Now, your data calls can make use of the page$ subject to trigger a new request from your server for these partial results. When the page changes, a new call will be made to your API.

data$ = page$.pipe(
    map(page => (page - 1) * 10), // map to 'skip' value
    switchMap(skip => this.apiService.getProducts(skip, 10))
);
Daniel W Strimpel
  • 8,190
  • 2
  • 28
  • 38
1

You don't, each time you retrieve another page of data you are making another http request. Http requests fire once and then the observable completes.

Adrian Brand
  • 20,384
  • 4
  • 39
  • 60
  • In this case, what is the point of it being an observable? If it fires once and completes, wouldn't a Promise be more appropriate? – Lewis Cianci Apr 29 '19 at 22:19
  • 1
    No, you can cancel an observable, you can't cancel a promise. If you really want to make an observable that you can stay subscribed to you could build an observable chain from a behaviour subject that switchMaps to a http request so each time you call next on the behaviour subject that has the paging parameters the observable chain emits. – Adrian Brand Apr 29 '19 at 22:25
0

I have built a library call ngx-rxcache that allows you to build observable state management for Angular.

https://github.com/adriandavidbrand/ngx-rxcache

You could build a service with a product$ observable variable

@Injectable({
  providedIn: 'root'
})
export class ProductService {
  private productCache = this.cache.get<Product[]>({
    id: '[Product] products'
  });

  products$ = this.productCache.value$;

  loading$ = this.productCache.loading$;

  constructor(private http: HttpClient, private cache: RxCacheService) { }

  load(page: number) {
    this.usersCache.load(() => this.http.get<Product[]>(`product/${page}`));
  }
}

Then in your component you can stay subscribed to the products$ observable and update it with the load method.

products$ = this.productService.products$;

goto(page: number) {
  this.productService.load(page);
}

You can read an article I wrote on it here.

https://medium.com/@adrianbrand/angular-state-management-with-rxcache-468a865fc3fb

Adrian Brand
  • 20,384
  • 4
  • 39
  • 60