What is the best method for combining two observables into one?

My goal is to initialize a list with 12 users using the URL

${this.url}/users?offset=${offset}&limit=12
. As users scroll, the offset should increase by 8. I plan to implement infinite scrolling for this purpose. However, I am facing an issue with appending the new list of 8 members to the existing one.

I am currently using observables(userList) and struggling to find a solution. Most tutorials online recommend using concat(), but that method is designed for arrays. I tried incorporating the logic to add the list + 8 offset when loadMore is true without success.

Here's a snippet of my code:

service.ts

// Fetch a list of users
getList(offset = 0): Observable<any> {
    return this.http.get(`${this.url}/users?offset=${offset}&limit=12`);
}

page.ts

@ViewChild(IonInfiniteScroll) infiniteScroll: IonInfiniteScroll;
userList: Observable<any>;
offset = 0;
...

getAllUsers(loadMore = false, event?) {
    if (loadMore) {
        this.userList = this.userService.getList(this.offset += 8) // Get new 8 users
            .pipe(map(response => response.results));
    }
    
    this.userList = this.userService.getList(this.offset) // Initialize 12 users
        .pipe(map(response => response.results));
        
    if (event) {
        event.target.complete();
        console.log(event);
        console.log(loadMore);
    }
}

page.html

...
</ion-item>

</ion-list>
<ion-infinite-scroll threshold="100px" (ionInfinite)="getAllUsers(true, $event)">
    <ion-infinite-scroll-content
         loadingSpinner="crescing"
         loadingText="Loading more data...">
    </ion-infinite-scroll-content>
</ion-infinite-scroll>

</ion-slide>

<ion-slide>

Answer №1

Utilize the Merge function to combine multiple observables into one:

getAllUsers(loadMore = false, event?) {
    if (loadMore) {
      const newUserList$ = this.userService.getList(this.offset += 8) //new 8 users
    .pipe(map(response => response.results));
    this.userList = merge(this.userList, newUserList$); // merge observables

    }
    this.userList = this.userService.getList(this.offset) // initials 12 users
    .pipe(map(response => response.results));
    if (event) {
      event.target.complete();
      console.log(event);
      console.log(loadMore);
    }
  }

Update

Based on your URL, it may be advisable to remove the limit parameter:

  getList(offset= 0): Observable<any> {
    return this.http.get(`${this.url}/users?offset=${offset}`);
  }

Answer №2

One approach to effectively handle data accumulation while the user scrolls is utilizing the scan operator.

To continuously add and accumulate data as the user scrolls, consider implementing a BehaviorSubject that emits values during each scroll event. This type of subject is chosen for its ability to provide an initial value.

const loadUsersSubject = new BehaviorSubject<number>(12);
let userList$/* : Observable<any>; */ // Uncomment this if used inside the template along with the async pipe
let internalCnt = 0;

const generateUsers = (n: number) => {
  return of(
    Array.from({ length: n }, ((_, i) => ({ user: `user${++internalCnt}` })))
  );
}

userList$ = loadUsersSubject
  .pipe(
    flatMap(numOfUsers => generateUsers(numOfUsers)),
    scan((acc, crt) => [...acc, ...crt])
  )
  .subscribe(console.log)


// Simulating scrolling after 1s..
timer(1000)
  .subscribe(() => {
    loadUsersSubject.next(8);
  });


// Simulating scrolling after 3s..
timer(3000)
  .subscribe(() => {
    loadUsersSubject.next(8);
  });

Click here for a live demo on StackBlitz

Answer №3

Learn how to utilize the scan operator to enhance the state with subsequent requests.

Click here for a demo

import { of, Observable } from 'rxjs'; 
import { map, scan } from 'rxjs/operators';


const source = new Observable((observer) => {
  observer.next(['Welcome', 'to Coding']);

  setTimeout(() => {
    observer.next(['This', 'is', 'amazing']);
  }, 1000)

   setTimeout(() => {
    observer.next(['Learning', 'is', 'fun']);
  }, 2000)
}).pipe(
    scan(
      (acc, val) => acc.concat(val),
      []
    )
);

source.subscribe(x => console.log(x));

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

CSS switch status toggle

Here's my code for a toggle switch from . I'm trying to change the label before the switch/checkbox to display "checked" or "not checked" based on the toggle state. When I click on the label, it changes the switch but not the text. JavaScript: ...

Postpone the NgModule declaration by importing the module asynchronously

I am facing a challenge in dynamically importing a third-party module and then checking if it exists. The decision to declare it in the NgModule depends on its existence (true/false). Below is an example of my code. However, the issue arises because the ...

Can a specific element be chosen based on its background color?

Is it possible to change the background color of a child element within a div that has a specific background color using CSS? See my explanation below. For example: .container[background-color=some color] .content { background-color: some other color ...

Verifying whether every value in the X array is present in the Y array or not

Currently, I am working with two arrays: const arrA = [1, 2, 3, 4, 5]; const arrB = [1, 2, 3, 4, 5, 6, 7, 8, 9]; My goal is to determine if all elements in array A exist in array B. Here are a few scenarios for better clarity: const arrA = [1, 2, 3, 4, ...

Struggling to add phantom-js to my Angular project

I'm looking to incorporate the phantomJS library into my Angular 4 project for continuous integration with Jenkins. No matter what method I try, I keep encountering the same (or similar) error. For instance, when following this tutorial and attemptin ...

Tips for effectively utilizing the Ngrx navigation function

While exploring NgRx, I stumbled upon navigation. According to the documentation, it seems like this effect should trigger when the component loads. However, I'm facing an issue where this effect is not getting triggered. Other effects that I've ...

What's the process for converting offsetX and offsetY pixel coordinates to percentages?

Currently, I am working on a project where I need the offsetX and offsetY coordinates to be displayed in percentage (%) format while hovering over a div element. By default, these coordinates are shown in pixels. Here is an example of the structure: < ...

Utilizing Jquery to add a delay and animate the sliding up effect on a recently created element

I am looking to apply a slide-up effect to a newly created element after a specified delay. $("div[data-error='true']").delay(5000).slideUp(500, function () { $("#error-alert").remove(); }); $("div[data-success='true']").delay(5000 ...

Trigger the opening of a bootstrap modal from an external source on the current page

One common question is how to load a modal from another page onto the current page, or how to open a modal on the current page when it loads. My idea is a little different: I want to click on an image hotspot (like a person in a team photo) on the /home p ...

Displaying elapsed time in JavaScript (Node.js)

Looking for a way to convert a date time or time-stamp format like 2015-12-18 07:10:54 into a relative time, such as "2 hours ago." I attempted the code provided, but it seems to consistently show an incorrect estimation of "8 days ago." function convert ...

Is there a way to transform time into a percentage with the help of the moment

I am looking to convert a specific time range into a percentage, but I'm unsure if moment.js is capable of handling this task. For example: let start = 08:00:00 // until let end = 09:00:00 In theory, this equates to 100%, however, my frontend data ...

Tips for transferring data from a table row to a bootstrap modal in Angular 2

Attempting to remove a record from a table involves the user clicking on the delete button, which triggers a confirmation box. Once the user confirms deletion by clicking another delete button in the modal, the record should be deleted. The desired action ...

angular.js watch() method is not functioning properly during a JSON call

I am trying to trigger a method whenever the value of my $http.selectedSong (model value) changes, but for some reason it is not working. Any ideas on why this could be happening?: app.controller('songController', ['$http', function($h ...

Using a functional wrapper component to reset the modal field in Reactstrap upon closing and reopening

In the main component that displays a list of to-do tasks, we have the ability to add or edit existing tasks. To facilitate this functionality, a separate wrapper was created. import React, { useEffect, useState } from 'react'; import { Label ...

Why are two vertical scrolls appearing on the screen simultaneously?

I utilized this method to hide the body scrollbar initially and then display it upon clicking a link: $('body').css('overflow', 'hidden'); $('#site').click(function(e) { $('#wrapper').remove(); $(& ...

Create a cookie using the JavaScript option

Can you specify cookie settings in JavaScript like CookieOptions in C#? var options = new CookieOptions { IsEssential = true }; I am aware that you can set a cookie in JavaScript using document.cookie = cookieString, but is there a way to include opti ...

Guide to updating current rjs files to incorporate jQuery and json in Rails 3

Currently, in my Rails 3 application, I am using rjs to render partials in my controllers. An example of this is when saving a new item to a table, the table gets refreshed: respond_to do |format| format.js { render :update do |page| ...

JavaScript - undefined results when trying to map an array of objects

In the process of passing an object from a function containing an array named arrCombined, I encountered a challenge with converting strings into integers. The goal is to map and remove these strings from an object titled results so they can be converted i ...

Discovering the ins and outs of utilizing Validator.pattern in Angular 9

BGroup = new FormGroup({ test: new FormControl(null, [Validators.required, Validators.pattern("[0-9]*$")]) }); Greetings! I am currently using Angular 9 and I have a question. How can I define a pattern that only accepts decimal numbers? Speci ...

Attempting to launch my node project through the terminal in my VS Code environment

node:internal/modules/cjs/loader:988 throw err; ^ Error: Module 'C:\Users\user\desktop\dev-folder\node\node.js' not found ←[90m at Module._resolveFilename (node:internal/modules/cjs/loader:985:15)←[39m ...