Tips for effectively transmitting data while utilizing a declarative/reactive data access method with RxJS in Angular?

Angular typically follows a classic pattern for data access that looks like this:

Traditional Data Access Pattern

getProducts(): Observable<Product[]> {
  return this.http.get<Product[]>(this.productsUrl)
    .pipe(
      tap(data => console.log(JSON.stringify(data))),
      catchError(this.handleError)
    );

In the above code snippet, a method is used to return an Observable. When data is retrieved from the http endpoint, the Observable emits the data in a format specified by the Product[] generic parameter, where Product is an interface.

For a more reactive application design, developers often opt for a declarative/reactive approach. This involves changing the previous method into a property declaration, as demonstrated below:

Reactive Data Access Pattern

products$ = this.http.get<Product[]>(this.url)
  .pipe(
    tap(data => console.log(JSON.stringify(data))),
    catchError(this.handleError)
  );

It's worth noting that this new approach declares a property for the Observable rather than using a method. The convention of adding a $ suffix to products$ helps differentiate Observables from other types of data structures, such as objects or arrays.

The reactive data access pattern is commonly employed when dealing with interactive UI elements (such as filtering based on user selections), complex data operations (like merging data from multiple sources), and data sharing across different components (for cases where each component needs to react to changes in the shared data).

However, one challenge posed by the reactive data access pattern is how to pass parameters required by the URL when implementing this technique.

For instance, imagine a scenario where an http request necessitates specifying a category to fetch products only within that category:

this.http.get<Product[]>(`${this.url}?cat=${catId}`)

How can we include this category information since there are no methods involved?

Answer №1

Instead of worrying about how to "transfer" data, focus on how to gradually release that value over time. To release data, we create our own Observable using Subject or BehaviorSubject.

private categorySubject = new Subject<number>();
categorySelectedAction$ = this.categorySubject.asObservable();

products$ = this.categorySelectedAction$.pipe(
 switchMap(catId=>this.http.get<Product[]>(`${this.url}?cat=${catId}`))
    .pipe(
      tap(data => console.log(data)),
      catchError(this.handleError)
  ));

selectedCategoryChanged(categoryId: number): void {
  this.categorySubject.next(categoryId);
}

In the code above, our Observable is created using a Subject. The Subject is kept private to prevent external access to it from our service. We then expose the readonly portion of the Subject using asObservable().

Whenever a user chooses a different category, the component triggers selectedCategoryChanged which releases the selected categoryId into the stream defined by our Subject.

For products$, when a value is released, it goes through a series of operators. The switchMap is a higher-order mapping operator that automatically subscribes to the inner Observable (the this.http.get...) and flattens the result. The returned products for the specified category are then released into the products$ stream.

For a more comprehensive solution, check out this github repository: https://github.com/DeborahK/Angular-RxJS/tree/master/APM-Final

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The utilization of the rest parameter in combination with generics

I encountered an issue with my iteration. The error message "Operator '+=' cannot be applied to types 'number' and 'T'" is showing up. I am puzzled as to why this is happening. let a: number = 1, b: number = 2, c: number ...

Uncover the content of a base64 encoded string and convert it into

A JSON response has been linked on the user's request to retrieve an excel document. The structure of the response is as follows: { "format": // file extn ----only xls "docTitle": //file name "document" :// base 64 encoded data } The attem ...

Modifying ngModel will result in the corresponding ngModel in another Angular2 browser being updated

I'm a novice when it comes to Angular 2 and I'm currently working on a project using npm to run the application. At the moment, I have it hosted on localhost with port 3000. The issue I'm facing in my project is that when I open the same ...

Error: The layout was unable to display the template body

I've been working on a web application with express and eta, but I'm running into an issue with including partials in my templates. Despite trying to include a file partial (refer to the Docs), the compiled template doesn't seem to incorpor ...

What could be causing the lack of reflection of Angular changes in the browser post-build process?

I'm having trouble seeing changes reflected in my Angular project after making them. I've tried clearing the cache pages and cookies in Chrome, as well as enabling disable cache in the network tab of developer tools. In addition, I cleared the a ...

How can we initiate an AJAX request in React when a button is clicked?

I'm fairly new to React and I'm experimenting with making an AJAX call triggered by a specific button click. This is how I am currently using the XMLHttpRequest method: getAssessment() { const data = this.data //some request data here co ...

Is there a way to initiate validations for inputs within ReactiveForms?

After creating a form using Reactive Forms, I have implemented functionality for users to set a new password. The process involves entering both the old and new passwords, as well as confirming the new password. Throughout development, I considered the fol ...

Converting a JavaScript function to work in TypeScript: a step-by-step guide

When writing it like this, using the this keyword is not possible. LoadDrawing(drawing_name) { this.glg.LoadWidgetFromURL(drawing_name, null, this.LoadCB,drawing_name); } LoadCB(drawing, drawing_name) { if (drawing == null) { return; ...

Is there a way to ensure that the observer.next(something) received the value before executing observer.complete()?

I have been working on an Angular app where I am using a resolver to preload data in the component. Below is the code for the resolver: resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): void { return Observable.create(observer => { ...

A property in TypeScript with a type that depends on the value of an object

How can we troubleshoot the error not displaying in Typescript and resolve it effectively? Explore Typescript sandbox. enum Animal { BIRD = 'bird', DOG = 'dog', } interface Smth<T extends Animal = Animal> { id: number; a ...

The return type in Typescript is populated with a generic type name that lacks meaningful

When utilizing ts-results-es, I encounter an issue. This library aids in wrapping errors within a class to provide insight into potential function errors. Here is a simplified class definition: interface BaseResult<T, E> {} class Err<E> imple ...

Reversing a Firebase list in Angular 2 after inserting a new item: A step-by-step

I am currently looking for a solution to reverse my Firebase list in real-time. For instance: Let's say I have the following Firebase list: {apple, orange, banana}; == After reversing => {banana, orange, apple} If I were to add a new item (with ...

Challenge with validating a custom form control using Angular's FormArray

Could you please review the following issue? I have developed a custom component that functions as a checklist. I utilized FormArray to represent the rows, making each row a form array item and thus a FormGroup. Below is the form definition for the main ...

How can I showcase an SVG icon received in base64 format using an img tag?

Having trouble displaying SVG/base64 encoded images through the img tag. Here is the issue at hand: Receiving iconData (as a string) from the server. Attempting to display it in my component. No issues with step 1. Encountering a broken image sign with s ...

Dynamic TypeScript class constructor argument typing determined by user input

I am working on creating a dynamic class that can adapt its argument properties based on a certain value. To illustrate this concept, let's consider a simple example: Imagine I have a class called Customizer, and depending on the value of the mode pr ...

A step-by-step guide on accessing the data from an uploaded JSON file in a React application

One exciting feature is the drag and drop component that allows users to add multiple files. However, there seems to be an issue with displaying the content of a JSON file once it's added. Below is the code snippet in question: if (files?.length) ...

Activating the microphone device on the MediaStream results in an echo of one's own voice

I am in the process of creating an Angular application that enables two users to have a video call using the Openvidu calling solution. As part of this application, I have implemented a feature that allows users to switch between different cameras or micr ...

What is the best way to effectively use combinedLatestWith?

https://stackblitz.com/edit/angular-ivy-s2ujmr?file=src/app/country-card/country-card.component.html I am currently working on implementing a search bar in Angular that filters the "countries$" Observable based on user input. My approach involves creatin ...

Loading external libraries in Angular2: A step-by-step guide

I'm currently working on incorporating a Datepicker in Angular 2, but I'm facing an issue where the library is not loading. This is causing a template parsing error stating 'material-datepicker' is not a recognized element: My System.c ...

Is it possible for me to employ T extends Contravariant<any> to effectively restrict a generic type to a union of Contravariants

Let's begin by clarifying some terms: type Contravariant<T> = (t: T) => void; declare let cNum: Contravariant<number>; declare let cStr: Contravariant<string>; Now, suppose I wish to create a function that accepts an array of the ...