Home > Blockchain >  normalising data with combineLatest not working as expected on Angular, RxJS
normalising data with combineLatest not working as expected on Angular, RxJS

Time:11-14

While trying to normalize data before combining it in a new stream, changes did not occur.

I want to check if the post is liked by the user through a Promise function before returning it to the Posts Observable.

this.data$ = combineLatest([
      // stream 1 - posts
      this.posts_service.posts$ as Observable<Post[]>,
      // stream 2 - user
      this.user_service.user$ as Observable<_User>,
    ]).pipe(
      map(([posts, user]) => {
        // if there is a user, check if they have liked the post
        if (user) {
          // check if user has liked post
          posts = posts.map((post) => {
            // send post and user id to a promise function that will return a boolean
            this.posts_service.hasLikedPost(post, user.uid).then((has_like) => {
              console.count('hasLikedPost');
              post.has_like = has_like;
            });
            return post;
          });
          user = user;
          console.table(posts);
          return { posts, user };
        } else {
          return { posts };
        }
      })
    );
  }

CodePudding user response:

Here is my refactor. I extracted some functionality to separate function or method getPosts just for cleaner code.

// Typing the property for better readability and type checking
const data$: Observable<{ posts: Post[], user: _User; }> = combineLatest([
    // stream 1 - posts
    this.postsService.posts$ as Observable<Post[]>,
    // stream 2 - user
    // assuming that user$ emits at least once, even there is no user
    this.userService.user$ as Observable<_User>,
]).pipe(
    // if there is a user, check if they have liked the post
    switchMap(([posts, user]) => user ? getLikes(posts, user) : of({ posts })),
);


function getLikes(posts: Post[], user: _User): Observable<{ posts: Post[], user: _User; }> {
    // create event from each post because we need to check asynchronously each one post for "has_like" flag
    return from(posts).pipe(
        // check each post if it is liked by user and add property to one
        concatMap(post => from(this.postsService.hasLikedPost(post, user.uid))
            .pipe(
                map(hasLike => ({ ...post, hasLike }))
            )
        ),
        // merge back all the posts into array
        toArray(),
        // DEBUG
        tap(posts => console.table(posts)),
        // add user to replay
        map(posts => ({ posts, user })),
    );
}
  • Related