RxJs - What is the best approach for generating an array of Observables within an Observable?

Attempting to understand the correct approach in RxJs, as I am relatively new to it. My goal is to create an Observable array of Observables.

I aim to fetch a list of User's Posts with each Post being an Observable. These individual Post Observables should be stored in an Observable array so that any changes to the array can notify the calling code and update all subscriptions related to the post "list". While this task seems straightforward, I also require each Post to itself be an Observable for subscribing to individual objects like posts[i]. What would be the best practice to achieve this?

My current setup involves Angular 9:

public getPosts(): Observable<Array<Post>> {
    return new Promise((resolve, reject) = {
        let posts: Observable<Array<Post>> = new Observable<Array<Post>>();
        this.get<Array<Post>>('posts').subscribe(r => {
            posts = from(r);
            return resolve(posts);
        });
    });
}

This code provides an

Observable<Array<Post>>
, but how can I generate an
Observable<Array<Observable<Post>>>
? Is this considered an anti-pattern?

Answer №1

The key factor here is convenience - if your server provides you with detailed updates on post changes, then feel free to create an

Observable<Observable<Post>[]>
.


However, in your specific case, there seem to be some issues. Mixing Observables with Promises is not ideal. The method getPosts will only fetch the first post obtained from the API.


While this code snippet offers a potential solution, it may not align precisely with your original intentions...

public getPosts(): Observable<Array<Observable<Post>>> {
  return this.get('posts').pipe(
    switchMap(posts => combineLatest(
      posts.map(post => this.get('post', post.id))
    )),
  );
}

Answer №2

There seems to be some confusion about the goal here, but perhaps a solution closer to this would work better:

@Injectable({providedIn:'root'})
export class PostService {
  // private replay subject will cache one value
  private postSource = new ReplaySubject<Post[]>(1)
  // public list of posts observable
  posts$ = this.postSource.asObservable();
  // function to select item by id out of list
  post$ = (id) => this.posts$.pipe(map(posts => posts.find(p => p.id === id)))

  getPosts() {
    // function to retrieve remote posts
    return this.get<Post[]>('posts');
  }

  loadPosts() {
    // function to fetch posts and update the subject value
    this.getPosts().subscribe(posts => this.postSource.next(posts));
  }
}

You will need to define the get function and call loadPosts whenever you need to refresh the list.

Answer №3

Provided Information:

!Please inform me if any of the statements are incorrect so I can update the answer!

  1. There is a get function that returns an observable containing an array of posts
  2. The get observable always emits when there are changes in the posts
  3. The value inside the observable (Array>) is not observable and remains constant over time
this.get<Array<Post>>('posts')

Possible Functions:

  1. () => getPostById$
// This function provides an observable with the post related to a specific id.
// The observable will only emit if the id is found and if there are changes in the post values
function getPostById$(id: string): Observable<Post> {
  const findId = (id: string) => (posts: Array<Post>): Post | undefined =>
     posts.find(post => post.id === id);

  const existingPost = (post: Post | undefined): boolean => post != null;

  const postComparator = (prevPost: Post, currPost: Post): boolean =>
    prevPost.value === currPost.value && prevPost.name === currPost.name;

  return this.get('posts').pipe(
    map(findId(id)),
    filter(existingPost),
    distinctUntilChanged(postComparator)
  );
}

  1. () => getPosts$
function getPosts$(): Observable<Array<Post>> {
  return this.get('posts');
}
  1. () => getStatePosts$
// This function enables you to manage your own state for posts
function statePosts$(posts$: Observable<Array<Posts>>, clear$: Observable<void>, add$: Observable<Post>): Observable<Array<Post>> {
  const updatePosts = (newPosts: Array<Posts>) => (oldPosts: Array<Posts>) => newPosts;

  const clearPosts = () => (oldPosts: Array<Posts>) => [];

  const addPost = (post: Post) => (oldPosts: Array<Posts>) => [...oldPosts, post];

  return merge(
    posts$.pipe(map(updatePosts)),
    clear$.pipe(map(clearPosts)),
    add$.pipe(map(addPost))
  ).pipe(
    scan((oldPosts, fn) => fn(oldPosts), [])
  )
}

// Usage
private posts$: Observable<Array<Post>> = this.get('posts');
private clear$: Subject<void> = new Subject();
private add$: Subject<Post> = new Subject();

public statePosts$ = getStatePosts(posts$, clear$, add$);

Hint: When reading the functions from the return statement first, it may help clarify what each operation does. If you have any questions, feel free to ask for clarification.

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

Leveraging setters in JavaScript for modifying properties

The MDN documentation for the set function appears to suggest that a setter defined in [ECMAScript 2015] should not exist in an object literal alongside a data entry for the same property. [ECMAScript 2015] setter must not appear in an object literal .. ...

CSS Grid expands the item width on its own when there is still space left

Having some queries regarding the piece of code provided. Currently, I have a collection of 5 cards displayed in rows of 3 cards each using the styling grid-template-columns: repeat(3, minmax(200px, 1fr));. My concern is whether there is a way for the 4th ...

Utilize the power of Javascript/AJAX or jQuery to submit multiple forms simultaneously

I am currently working on a project which involves having 3 forms on a single page. The reason behind this is that one of the forms is displayed in a modal dialog. My goal is to allow users to submit all 3 forms with a single button click, and I also wan ...

An issue arose when attempting to load the page using jQuery

Currently, I am implementing the slidedeck jquery plugin on my webpage to display slides. While everything is functioning properly, I am facing an issue with the CSS loading process. After these slides, I have an import statement for another page that retr ...

Issue with vue-router redirection when pushing a route

I have set up my router configuration as follows: const router = new Router({ mode: 'history', routes: [ // root { path: '/', component: ComponentPage, redirect: routePrefix.public, children: [ ...

The functionality of submitting AJAX chat messages is limited to $(document).keypress() only

My ajax chat has an input field that needs to send data when the enter key is pressed. $("#chatfield").keypress(function(e) { if(e.which == 13) { chatsend($('#chatfield').val()); } }); The current code does not work as expected, so I had to ...

Unable to retrieve a singular data point from a set

I am currently working with a collection named notification and my goal is to retrieve a single value using the findOne() method. var allnotices = Notifications.findOne({eventownernumber:"2"},{sort: {noticedate: -1, limit: 1}}).noticemessage; The desire ...

Achieve Custom Styling in Material UI Stepper Label with ReactJS: Adjust Font Size and Margin Top

Is there a way to adjust the fontsize of the stepper label and the margin between the label and circle? The default marginTop is set to 16px, but I would like to reduce it. Any suggestions on how to achieve this? Check out the Codesandbox code using Mater ...

Guidelines on extracting all choices from a select element and organizing them into a list

Is there a way to retrieve the HTML content of all the options in a list using jQuery (or native js)? The desired output should be something similar to: var options = ['<option value="" selected="">---------</option>', <option val ...

React - Inserting a break in the line;

Can someone help me with my code that is supposed to add a line break every 60 characters? I'm confused about why the \n character isn't working. function countStr (str1){ let str2=""; let charCount=0; for(let i=0; i< ...

CSS Toggle Navigation for Responsive Screen Re-Size in Both Horizontal and Vertical Orientations

I'm currently working on making a navigation menu responsive. Take a look at my progress so far here: https://jsfiddle.net/a16qwd20/4/ Unfortunately, I am facing an issue where the Javascript doesn't seem to be functioning properly in JS Fiddle, ...

Unable to render Blender model in threeJS scene due to undefined condition

I've tried following solutions from Stack Overflow to resolve this issue, but I'm still unable to load external objects (Blender). Essentially, I exported it as a ThreeJS JSON file, here is the content of my JSON file: { "textures":[], ...

The specified type '{}' cannot be assigned to type 'ReactNode'

Can someone help me figure out why I am getting this error in Vercel but not locally? Error: 'FontAwesomeIcon' cannot be used as a JSX component. ./components/Services/ServiceContainer.tsx:25:6 12:01:54.967 Type error: 'FontAwesomeIcon&a ...

Error: The EJS compiler encountered a SyntaxError due to an unexpected token || in the show component file located at /var/www/html

I'm currently working on a project inspired by Colt Steele's YelpCamp creation during his Udemy Web Dev Bootcamp. Everything was going smoothly until I tried to refactor some code towards the end of the course using YouTube tutorials. Now, whenev ...

Mistakes in design when transforming inline SVG to PNG format

One of my main objectives is to convert a <div> element that contains multiple inline svg images into a png file. The entire process must be carried out within the client's browser using JavaScript. I have experimented with various techniques: ...

Develop a new ASP.NET Core RESTful API utilizing JavaScript that handles DateTime conversion to UTC time, rather than

I am facing an issue with sending data from my React application using new Date() function in JavaScript, where the date is Sat Oct 30 2021 00:00:00 GMT+0800 (Singapore Standard Time). However, when I receive this data in my ASP.NET Core Rest Web API app ...

The issue of JQuery $(this) malfunctioning when used within a function parameter

Encountering an issue with the code snippet below: $(".countdown").circularCountdown({ startDate:$(this).attr('data-start'), endDate:$(this).attr('data-end'), timeZone:$(this).attr("timezone") }); On the contrary, the co ...

In React version 16 and d3 version 4, if you try to use the mouse functionality from d3-selection, you may encounter a TypeError stating that it cannot read the property 'sourceEvent' of

Exploring the integration of d3 with React by incorporating the mouse feature from d3-selection module import { selectAll, select, mouse } from 'd3-selection'; Encountering an issue while attempting to utilize : mouse(e.target) or mouse(select( ...

Synchronizing information between different controllers using a service

After reading the discussion on this stackoverflow post, it seems like using services is the recommended way to transfer data between controllers. However, in my own testing on JSFiddle here, I encountered difficulties in detecting changes to my service w ...

Incorporate an additional javascript document into a VueJS component

What is the most efficient method to include Javascript files in Vue.js components? ...