0

I want to get all comments from comments.json using http and observable which I am doing right now successfully, now I just want to loop these "comments" within this observable such that for each actor id I can call another function and pass this id and fetch user detail. Something similar done in This Question

I know getting user detail separately is not good idea but it is requirement. I want to know do this by async pipe so that information keep updating.

I have a service below:

service.ts

import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http'
import {IComment} from "../../comments";
import {Observable} from "rxjs/observable";

@Injectable()
export class CommentsDataService {

  private _url:string = "../../../../assets/json/comments.json";
  constructor(private http: HttpClient) {  }

  /**
   * Return Sample json for
   * Comment listing.
   * @param Id
   * @returns {json)
   */
  getComments():Observable<IComment[]>
  {
    return this.http.get<IComment[]>(this._url)
  }

  /**
   *
   * @param commentObj
   */

  saveComment(commentObj){

    console.log(commentObj);

  }

}

component

 ngOnInit() {


    //Service call for Comments listing.
    this.commentsDataService.getComments()

    .subscribe(data=>this.commentsData = data)


  }

HTML:

<div class="container">

    <div class="row listCommentsContainer">
        <div class="col-lg-8 col-sm-8" *ngFor="let commentData of commentsData; let i = index">
          <ol style="list-style: none;">
          <li class="listComments">

            <div  style="display: block">
            <div style="display:inline-block;">
              <a class="avatar">

                <img style="" src="">
              </a>
            </div>
            <a class="commentPostByUserName">
              <span class="commentPostByUserName" style=""></span>
            </a>
              <div class="commentTime">{{commentData.time_stamp}}</div>
            </div>
            <div class="commentTextDisplay">{{commentData.object}}</div>

            <br>

            <!-- Click Reply -->
            <div class="addReplyContainer" #commentData.id>
              <a  class="addReplyLink"  (click)="showReplyTextArea($event, commentData.id)">Reply</a>
            </div>

            <!-- Add Reply -->
            <div [attr.commentId]="commentData.id" class="addReplyContainer replyTextAreaContainer" style="display: none" >
              <textarea (keyup)="keyUpCommentTextarea($event, reply, commentData.id)" (keyup.enter)="addReply($event, commentData.root_id)" [(ngModel)]="reply" style="width:100%"
                        class="replyText commentText addReplyTextarea form-control"></textarea>
              <button [attr.commentId]="commentData.id" disabled class="btn btn-success addCommentBtn" (click)="addReply($event, commentData.root_id)">Add</button>
            </div>
            <!-- ----------- -->

            <!-- List Replies -->


            <div class="replies col-lg-8 col-sm-8" *ngFor="let reply of commentData.comment">
              <ol style="list-style: none;">
                <li class="listComments listReplies">

                  <div  style="display: block">
                    <div style="display:inline-block;">
                      <a class="avatar">

                        <img style="" src="">
                      </a>
                    </div>
                    <a class="commentPostByUserName">
                      <span class="commentPostByUserName" style=""></span>
                    </a>

                  </div>
                  <div class="commentTextDisplay replyTextDisplay">{{reply.object}}</div>
                </li>
              </ol>
            </div>

            <!-- --------------- -->
          </li>
          </ol>
        </div>

    </div>

    <!-- Add Comment-->
    <div class="row">
      <div  class="addCommentContainer col-lg-6 col-sm-12">

          <textarea (keyup)="keyUpCommentTextarea($event, comment)"
                    [(ngModel)]="comment" class="commentText form-control"
                    placeholder="Add Comment">
          </textarea>
        <button  (click)="addComment($event)" class="btn addCommentBtn btn-success">Add</button>

      </div>
    </div>
</div>

comments.json

[{ "id":"123",
  "root_id":"234",
  "target_id": "2",
  "object":"Nice!",
  "actor":"user:123",
  "time_stamp": "2 mins ago",

  "comment":[
    {
      "id": "124",
      "root_id":"234",
      "target_id":"3",
      "object":"Well!!",
      "actor":"user:123",
      "time_stamp": "2 mins ago"
    },
    {
      "id": "125",
      "root_id":"234",
      "target_id":"3",
      "object":"Great!",
      "actor":"user:125",
      "time_stamp":""
    }
  ]
},
  {
    "id":"126",
    "root_id":"234",
    "target_id": "2",
    "object":"Super.",
    "actor":"user:124",
    "time_stamp": "2 mins ago",
    "comment":[
      {
        "id": "234",
        "root_id":"234",
        "target_id":"",
        "object":"Cool.",
        "actor":"user:123"

      },
      {
        "id": "236",
        "root_id":"234",
        "target_id":"3",
        "object":"hiii.",
        "actor":"user:123",

      }
    ]
  },  {
  "id":"345",
  "root_id":"234",
  "target_id": "12",
  "object":"Interesting.",
  "actor":"user:124",
  "time_stamp": "2 mins ago"
},  {
  "id":"444",
  "root_id":"234",
  "target_id": "12",
  "actor":"user:125",
  "object":"Cool.",
  "time_stamp": "2 mins ago"
},
  {
    "id":"567",
    "root_id":"234",
    "target_id": "12",
    "object":"Last Comment..",
    "actor":"user:125",
    "time_stamp": "2 mins ago"
  }
]

user.json

[
  {
    "profile_image":"/../../assets/images/no-user.png",
    "first_name" : "jack",
    "id":"124"
  },
  {
    "profile_image":"/../../assets/images/no-user.png",
    "first_name" : "john",
    "id":"125"
  },
  {
    "profile_image":"/../../assets/images/no-user.png",
    "first_name" : "simon",
    "id":"123"
  }
]

enter image description here

7
  • 1
    it's very unclear what you want, are you trying to make another HTTP request for each item in the returned comments array? Commented Apr 10, 2018 at 13:34
  • Yes, when comments are returned. I want to make another http request to fetch actor details. Commented Apr 10, 2018 at 13:35
  • @Simer have you checked this toddmotto.com/angular-ngif-async-pipe Commented Apr 11, 2018 at 4:39
  • @SuvethanNantha I found this more useful blog.danieleghidoli.it/2016/10/22/http-rxjs-observables-angular Thanks for reference. :) I will read that too. Commented Apr 11, 2018 at 5:40
  • @SuvethanNantha I visited that article but couldn't understand it as I am quite new to angular 2+/angular4. I will try to understand it if you have referred it. Commented Apr 11, 2018 at 5:42

2 Answers 2

2

The switchMap operator is the classic solution to this idea of making more async calls based on the results of the first one, achieved like this:

getUser(id) {
   return this.http.get<User>('/user/ + id'); // don't know your actual user endpoint or interface but this is a sample
}

getCommentsWithUsers() {
    return this.getComments().switchMap(comments => // first we use switchMap to execute a new set of observables
         // map the comments into an array of requests for the user and combine with the forkJoin and map operator and object.assign
         (comments.length) 
            ? Observable.forkJoin(comments.map(comment => this.getUser(comment.actor).map(user => Object.assign(comment, {actor: user})) 
            : Observable.of([])); // empty check needed becuase forkJoin breaks with empty array
}

I'm not familiar enough with your data schema to say this will work perfectly, you may need some minor modifications, but this idea should work more or less.

Sign up to request clarification or add additional context in comments.

5 Comments

how you lay out your application is up to you, but this should be a service level function in my opinion
Thanks @bryan60 I need to test this and understand this. Thanks again.
any questions feel free to ask.
So same thing I have to do for comment inside comment. If you look at comments.json. @bryan60
Yea it'll get a little more complex with nested comments but the set up would be generally similar, you'll need to set up nested joins which could be a PITA. You really should denormalize this data somewhat
0

You can try something like this

this.http.get('/comments')
   .map(res => res.json())
   .finally(() => this.isLoading = false) // here load actors
   .subscribe(
         data => this.orders = data,
         error => console.log(error)
   );

5 Comments

Interesting pattern. Is there a reason you chose a .finally() rather than the onComplete callback from .subscribe()?
no, you can do that as well and i am glad you mentioned it cause i wanted to edit my answer or leave a comment :) it is much more readable though
you can try with the 3rd callback which is the complete as u mentioned
the switchMap operator is for combining asynchronous functions in this manner, ie using the results of one to execute another. This method could run into severe issues if concurrent requests are made for the comments by multiple components. switchMap is the preferable method.
finally will also execute on errors, and hence will lead to double failure here.. Its only purpose is of cleanup @DrNio . The success handler is the only place to utilize the returned value

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.