Using Behavior Subject for pagination in Angular with RxJS

Currently, I am creating a filtering system for a product list based on category IDs using the RXJS operator BehaviorSubject.

However, I have encountered an issue with implementing infinite scrolling with Behavior Subject because I am unable to access the previous array of data (referred to as products) since it always reflects the latest updates.

Here is my Service setup:

private productsSource = new BehaviorSubject<Product[]>(null);
public products$: Observable<Product[]> = this.productsSource.asObservable();
async getProductsByCategory(category: string, page: string = '1'): Promise<void> {
  const products = await this.http.get<Product[]>(`${environment.host + this.wooPath}products`, {
    params: {
      ...environment.configtations,
      category,
      page,
      lang: this.language,
      per_page: '6',
      not_tags: 'component'
    }
  })
    .toPromise();

  this.productsSource.next(products);
}

For handling pagination in loadMore function:

async loadData(event) {
 // How should I proceed next?
}

Answer №1

Modify the line

this.productsSource.next(products);
to:

this.products.next({
   previous: this.products.value.current
   current: products
});

Here is the updated declaration:

private productsSource = new BehaviorSubject<any>(null);
// You could define a custom type like:
// { previous: Product[], current: Product[] }

This way, you will have access to both the previous set and the current set.

If your intention is to implement infinite scrolling, I recommend storing all values in a specific array like so:

products$.subscribe((value) => {
   this.someProductsArray = [...this.someProductsArray, ...value];
});

Answer №2

I have discovered a solution that perfectly fits my current situation.

Here is the code snippet for it:

My approach: I simply return a list of products as a promise from the getProductsByCategory method:

/**
 * @name getProductsByCategory
 * @param category 
 * @param page 
 */
async getProductsByCategory(category: string, page: string = '1'): Promise<Product[]> {

  // Fetching products
  const products = await this.http.get<Product[]>(`${environment.host + this.wooPath}products`, {
    params: {
      ...environment.configtations,
      category,
      page,
      lang: this.language,
      not_tags: 'component'
    }
  })
    .toPromise();

  return products;
}

Additionally, I have implemented a new method to return the productsSource:

filteringProduct() {
  return this.productSource;
}

Within Component: Initializing in ngOnInit:

async ngOnInit() {
  // Obtain the category id
  const id = this.route.snapshot.paramMap.get('id');
  this.categoriesIds = id;

  // Retrieve category and its subcategories or products by category id
  [this.category, this.subCategories] = await Promise.all([
    this.wooService.getCategoryById(id),
    this.wooService.getSubCategories(id)]);

  // Setting products
  this.wooService
    .filteringProduct()
    .next(
      await this.wooService.getProductsByCategory(this.categoriesIds)
    );

  this.wooService.products$
  .pipe(
    takeUntil(this.unsubscribe)
  )
  .subscribe(data => this.products = data);

}

My loadMore Method:

async loadData(event) {

  // Incrementing page number
  this.currentPage++;

  // Fetching content of next page
  const nextPage = await this.wooService.getProductsByCategory(
    this.categoriesIds,
    this.currentPage.toString()
  );

  // Concatenating all data together
  this.products = this.products.concat(nextPage);

  // Completing the loading process
  event.target.complete();

  // Checking if all data has been loaded from the server
  if (nextPage.length === 0) {
    this.infinite = false;
  }

}

Answer №3

To keep track of the page number, you can utilize the count() operator along with a regular subject for the task at hand.

currentPage = Subject();
getProduct = (page) =>
  this.http.get<Product[]>(`${environment.host + this.wooPath}products`, {
    params: {
      ...environment.configurations,
      category,
      page,
      lang: this.language,
      per_page: '6',
      not_tags: 'component'
    }
  });


data = loadMore.pipe(
  count(),
  mergeMap(page => getProduct(page)),
  scan((acc, curr) => {
    return acc.concat(curr.res)
  }, [])
);

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

Creating a query string using a jQuery array that contains multiple values for a query variable

After writing some code to convert an array into a filter string, I noticed that the generated string was not in the format I wanted. product_size=123&product_size=456 Instead of this, I needed it to be product_size=123+456. To achieve this, I reali ...

Expanding the number of buttons for <ul> in creating a responsive navigation system using Angular

My navigation consists of 8 items (li), and as the resolution shrinks, the items are pushed onto a new line. I am looking to implement a feature where if an item doesn't fit in the navigation anymore, a "MORE" dropdown button appears on the right side ...

Include the clicked link into the text input area using Ajax or Jquery

Hey there, I'm just starting out with jquery and ajax so please be patient with me. Below is a snippet of my script that fetches branch names from the database asynchronously: $(document).ready(function () { $("#pickup").on('keyup' ...

Is it necessary to have Node.js or Express in order to launch my Angular 2 application?

Currently, I am in the process of developing a food purchasing web application. This app has already been successfully launched on mobile for Android devices. Our next step is to create a web version of the app. The backend of this application was created ...

Steps to invoke a function in a PHP file from an external JavaScript file

Can anyone assist me with calling the function below in my PHP file? function update_hidden_input(saved_tokens, hidden_input) { var token_values = $.map(saved_tokens, function (el) { //alert(el[settings.tokenValue]); return el[ ...

When using jQuery, adding a class does not always trigger CSS animation

Currently facing a peculiar issue. I am utilizing jQuery to load articles from JSON and would like to dynamically add a class of 'animate' to each loaded element. $.each(jsonArticles, function (i, article) { var $articleHTML = $( ' ...

transition effect of appearing and disappearing div

Having trouble creating a fade out followed by a fade in effect on a div element. The fade out happens too quickly and the fade in interrupts it abruptly. Here is the JavaScript code: $('#fillBg').stop(true,false).fadeTo(3000, 0); $("#fillBg"). ...

Chart of commitments and potential outcomes

I am in the early stages of learning about promises and I am struggling to understand how to write code correctly. Here is an overview of what the program should do: Retrieve a list of item types (obtained through a promise) Loop through each item type to ...

Receiving a 500 status code upon converting a SqlDataReader into Json format

Getting a status 500 error and not sure where I am going wrong. When I click on the 'Getcustomers' button, the 'GetCustomers' method is called which returns JSON. Script: <script> var MyApp = angular.module("MyApp", []); ...

Error: Undefined Property in Angular 2 ViewChild Declaration

Exploring a simple example where the childMethod is called within the child component from the parent component using the @ViewChild() decorator. However, encountering an issue where the ViewChild variable remains undefined. Sample Child Component Code: ...

Using the goBack function in React Router does not add the previous location to the stack

In React Router v4, I have a list page and a details page in my web application. I want to implement a 'Save and close' button on the details page that redirects the user back to the list page when clicked. However, I noticed that after the user ...

Submitting an mvc partial view form to send data from the parent view

I am currently working on a MVC 5 App where I have a Parent View that includes a Partial View, allowing users to load images. Upon submitting, the Parent view calls a .Ajax function defined within it, which in turn calls a Method/Controller. My requireme ...

Customizing Material UI Stepper styles using CSS API

I am trying to customize the text color (represented by an SVG Icon) in Material UI StepIcon for active and completed steps only. Currently, I have successfully changed the icon color for those steps. This is how my custom MuiTheme appears: export default ...

Troubleshooting tips for when JavaScript fails to load

I have encountered an issue with my two websites that are using the same theme. The sites in question are and . Both of them are WP multisite subsites, and are utilizing the exact same child theme with identical template files. However, I have noticed th ...

Searching for the way to access the values of a nested object's ref in Vue JS?

Within my Vue data object, I store a reference to a structure called Plot. It has properties for length, width, and acreage. interface Plot { length: number, width: number, acreage: number } const model = { plot: ref<Plot[]>([]), }) When fe ...

When attempting to install font-awesome with meteor npm, the module 'fontawesome'" was not found

Currently working with meteor version 1.4.1.1 which has NPM support enabled. I encountered an issue after installing the npm package "font-awesome" where the console displayed an error message stating "Uncaught Error: Cannot find module 'fontawesome&a ...

Ways to adjust timestamps (DayJs) by increments of 1 minute, 5 minutes, 15 minutes, 30 minutes, and more

Currently, I am exploring time functionality within React Native by utilizing DayJs. I have noticed a slight inconsistency when comparing 2 different points in time to calculate the time difference. Typically, everything works smoothly such as with 10:00 ...

Navigating between primary and named routes in Angular using RouterLink

After struggling for a while, I finally managed to get this code working: this.router.navigateByUrl('/live-chat(subnav:subnav)'); However, I am facing difficulty in replicating the same behavior using a [routerLink]='' directive. The m ...

Refresh a div using jQuery and include PHP files for dynamic content updating

This is the code I am using to dynamically update divs containing PHP files: $(document).ready(function() { setInterval(function() { $('#ContentLeft').load('live_stats1.php').fadeIn("slow"); $('#ContentRight').load( ...

Confirming the manipulation of Node.js callbacks

I have been working on a scheduling program in Node.js to retrieve JSON data about courses. As I'm relatively new to Node.js, I'm looking for ways to improve my code and avoid callback hell. I have already implemented the getJSON method. /*getJS ...