The classic pattern for data access in Angular suggests code like this:
Classic Data Access Pattern
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(this.productsUrl)
.pipe(
tap(data => console.log(JSON.stringify(data))),
catchError(this.handleError)
);
Above we have a method the returns an Observable. When the data is returned from the http endpoint, that Observable emits the data in a shape defined by the Product[]
generic parameter where Product
is an interface.
For a more reactive application, the declarative/reactive approach is often suggested. It changes the above method to a property declaration like this:
Reactive Data Access Pattern
products$ = this.http.get<Product[]>(this.url)
.pipe(
tap(data => console.log(JSON.stringify(data))),
catchError(this.handleError)
);
Notice that this declares a property in the service for the Observable instead of a method. The $
suffix on products$
is a convention used to distinguish properties that are Observables vs other types of data structures such as general objects or arrays.
This pattern is often used when there is UI interactivity (such as filtering based on a selection), when working with complex data (such as combining data from multiple end points) and when sharing data across components (such as when each component needs to react when the data changes).
The reactive data access pattern presents a common problem. How do we "pass" data required by the URL when using this technique?
For example, say that the http request requires a category to only pull down products for that category:
this.http.get<Product[]>(`${this.url}?cat=${catId}`)
How can this category be "passed in" since there is no method?