【问题标题】:Using nested observables in Angular 4在 Angular 4 中使用嵌套的 observables
【发布时间】:2018-04-11 13:59:02
【问题描述】:

我想使用 http 和 observable 从 cmets.json 获取所有 cmets,我现在正在成功地做这件事,现在我只想在这个 observable 中循环这些“cmets”,这样对于每个演员 ID,我都可以调用另一个函数和传递此 id 并获取用户详细信息。 在This Question中做了类似的事情

我知道单独获取用户详细信息不是一个好主意,但这是必需的。我想知道通过异步管道做到这一点,以便信息不断更新。

我在下面有一个服务:

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);

  }

}

组件

 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>

cmets.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"
  }
]

【问题讨论】:

  • 非常不清楚您想要什么,您是否尝试为返回的 cmets 数组中的每个项目发出另一个 HTTP 请求?
  • 是的,当 cmets 返回时。我想发出另一个 http 请求来获取演员详细信息。
  • @Simer 你检查过这个toddmotto.com/angular-ngif-async-pipe
  • @SuvethanNantha 我发现这更有用blog.danieleghidoli.it/2016/10/22/http-rxjs-observables-angular 感谢您的参考。 :) 我也会读。
  • @SuvethanNantha 我访问了那篇文章但无法理解它,因为我对 angular 2+/angular4 很陌生。如果您提到它,我会尽力理解它。

标签: angular


【解决方案1】:

switchMap 运算符是基于第一个结果进行更多异步调用这一想法的经典解决方案,实现如下:

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
}

我对您的数据架构不够熟悉,无法说这会完美运行,您可能需要进行一些小的修改,但这个想法或多或少应该可行。

【讨论】:

  • 如何布置应用程序取决于您,但我认为这应该是服务级别功能
  • 谢谢@bryan60 我需要测试并理解这一点。再次感谢。
  • 如有任何问题,请随时提出。
  • 所以我必须做同样的事情才能在评论中发表评论。如果您查看 cmets.json。 @bryan60
  • 是的,嵌套 cmets 会稍微复杂一些,但设置通常相似,您需要设置嵌套连接,这可能是 PITA。你真的应该对这些数据进行一些非规范化
【解决方案2】:

你可以试试这样的

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

【讨论】:

  • 有趣的模式。您选择 .finally() 而不是来自 .subscribe()onComplete 回调是否有原因?
  • 不,你也可以这样做,我很高兴你提到它,因为我想编辑我的答案或发表评论:) 虽然它更具可读性
  • 你可以尝试使用你提到的完整的第三个回调
  • switchMap 运算符用于以这种方式组合异步函数,即使用一个结果执行另一个。如果多个组件对 cme​​ts 发出并发请求,此方法可能会遇到严重问题。 switchMap 是最好的方法。
  • finally 也会在错误时执行,因此会在这里导致双重失败。它的唯一目的是清理 @DrNio 。成功处理程序是唯一使用返回值的地方
猜你喜欢
  • 2019-10-27
  • 2019-08-04
  • 1970-01-01
  • 2017-03-20
  • 1970-01-01
  • 2020-03-25
  • 2017-10-29
  • 2019-08-15
  • 1970-01-01
相关资源
最近更新 更多