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

Always_a_learner
  • 4,585
  • 13
  • 63
  • 112
  • 1
    it's very unclear what you want, are you trying to make another HTTP request for each item in the returned comments array? – bryan60 Apr 10 '18 at 13:34
  • Yes, when comments are returned. I want to make another http request to fetch actor details. – Always_a_learner Apr 10 '18 at 13:35
  • @Simer have you checked this https://toddmotto.com/angular-ngif-async-pipe – Suvethan Nantha Apr 11 '18 at 04:39
  • @SuvethanNantha I found this more useful http://blog.danieleghidoli.it/2016/10/22/http-rxjs-observables-angular/ Thanks for reference. :) I will read that too. – Always_a_learner Apr 11 '18 at 05: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. – Always_a_learner Apr 11 '18 at 05:42
  • @Simer what is exactly you want and what have you finished up to now? – Suvethan Nantha Apr 11 '18 at 05:48
  • I want to fetch comments, each comment will have user id, using that id I want to fetch user information. But each comment has a target id using that target id I need to fetch child comments(i.e replies) and then using their user id need to fetch their user information. @SuvethanNantha . – Always_a_learner Apr 11 '18 at 05:55

2 Answers2

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.

bryan60
  • 28,215
  • 4
  • 48
  • 65
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)
   );
DrNio
  • 1,936
  • 1
  • 19
  • 25
  • Interesting pattern. Is there a reason you chose a `.finally()` rather than the _onComplete_ callback from `.subscribe()`? – msanford Apr 10 '18 at 13:42
  • 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 – DrNio Apr 10 '18 at 13:43
  • you can try with the 3rd callback which is the complete as u mentioned – DrNio Apr 10 '18 at 13:43
  • 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. – bryan60 Apr 10 '18 at 13:51
  • 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 – NitinSingh Apr 11 '18 at 18:45