Angular4 / RxJS Observables获得儿童记录

时间:2017-06-17 05:13:35

标签: angular typescript rxjs observable

我正在为博客建立一个前端。我需要能够从API中获取一系列帖子,然后为每个帖子重新调用API以获取特色图像对象并将其附加到其父Post对象。最后返回完整Post对象的数组。用Observables很难找到解决这个问题的最佳方法。

到目前为止我所拥有的:

export class PostService {
private apiAddress: string = '/wp-json/wp/v2/posts';
private logger: Logger;

constructor (private httpService: HttpService, private loggerService: LoggerService) {
    this.logger = loggerService.newLogger('PostService');
}

getPosts(category: Category, page: number = 1, pageSize: number = 10): Observable<Post[]> {
    this.logger.log(`getPosts(id:${category.id}, ${page}, ${pageSize}) returned`);
    return this.httpService.get(`${this.apiAddress}?categories=${category.id}&page=${page}&per_page=${pageSize}&orderby=date&order=desc`).map<Response, Post[]>((res) => {
        let posts:Post[] = [];

        let body = res.json();
        for (var i = 0; i < body.length; i++) {
            posts.push(new Post(body[i]));
        }

        this.logger.log(`getPosts(id:${category.id}, ${page}, ${pageSize}) returned ${posts.length} posts`);
        return posts;
    });
}
}

了解我喜欢做什么:

export class PostService {
private apiAddress: string = '/wp-json/wp/v2/posts';
private logger: Logger;

constructor (private httpService: HttpService, private loggerService: LoggerService) {
    this.logger = loggerService.newLogger('PostService');
}

getPosts(category: Category, page: number = 1, pageSize: number = 10): Observable<Post[]> {
    this.logger.log(`getPosts(id:${category.id}, ${page}, ${pageSize}) returned`);
    return this.httpService.get(`${this.apiAddress}?categories=${category.id}&page=${page}&per_page=${pageSize}&orderby=date&order=desc`).map<Response, Post[]>((res) => {
        let posts:Post[] = [];

        let body = res.json();
        for (var i = 0; i < body.length; i++) {
            posts.push(new Post(body[i]));
        }

        this.logger.log(`getPosts(id:${category.id}, ${page}, ${pageSize}) returned ${posts.length} posts`);
        return posts;
    }).forEach((post, done) => {
        this.httpService.get(`/wp-json/wp/v2/media/${post.featured_media}`).map<Request, Media>((res) => {
            let body = res.json();
            post.featured_image = new Media(body);
            done(post);
        });
    });
}
}

我可能完全以错误的方式思考Observables。我很感激任何反馈。谢谢!

3 个答案:

答案 0 :(得分:0)

我可以看到你为两个http请求获取帖子而另一个获取特色媒体

在这种情况下,不要发出两个http请求,为什么不调整后端来重新调整数据,如

postdata : [
  {postid:1, postname:"something else, 
      children:[
            {childid :1, childname:first child item},
            {childid :2, childname:second child item}
          ]
    }  //this is a post with children items in it

  {postid :2, postname: "numbsertwo",
         children:[
                   ...return like in the above
                 ]

      }

我希望你能得到我的想法然后你可以通过

轻松地完成它们
 return this.httpService.get(`$urltoget`).map<Response, Post[]>((res) => {
    let posts:Post[] = [];

    let body = res.json();
    for (var i = 0; i < body.length; i++) {
        posts.push(new Post(body[i]));

       //here loop through the children

    }

    return posts;
})

答案 1 :(得分:0)

我会沿着这些方向做点什么

getPosts(category: Category, page: number = 1, pageSize: number = 10) {
    this.logger.log(`getPosts(id:${category.id}, ${page}, ${pageSize}) returned`);
    return this.httpService.get(`${this.apiAddress}?categories=${category.id}&page=${page}&per_page=${pageSize}&orderby=date&order=desc`).map((res) => {
        let posts:Post[] = [];

        let body = res.json();
        for (var i = 0; i < body.length; i++) {
            posts.push(new Post(body[i]));
        }

        this.logger.log(`getPosts(id:${category.id}, ${page}, ${pageSize}) returned ${posts.length} posts`);
        return posts;
    }).
    switchMap(posts => {
        const requestsForImage = new Array<Observable>();
        forEach(post => {
            requestsForImage.push(this.httpService.get(`/wp-json/wp/v2/media/${post.featured_media}`).map(res => {
                   let body = res.json();
                   post.featured_image = new Media(body);
                }
             ));
        })
        return Observable.combineLatest(requestsForImage).map(() => posts);
    })
}

基本思想是发送第一个http请求以检索帖子,然后创建一个其他http请求的数组,每个帖子一个,以检索每个帖子的图像。由于http请求是Observable,因此您创建了一个Observable数组。

您可以将一个Observable数组合并到一个新的Observable中,该Observable在Array的所有Observable都发出时发出。这是通过combineLatest运算符实现的。

最后一个地图运算符用于确保订阅者收到的是由第一个http调用创建的帖子数组。

答案 2 :(得分:0)

您需要将第一个请求的响应转换为流,然后再执行另一个请求。

  

永远不要将rx编程与for循环混合,这是一种不好的做法

您的代码应如下所示:

url = `${this.apiAddress}?categories=${category.id}
        &page=${page}&per_page=${pageSize}
        &orderby=date&order=desc`
mediaUrl = `/wp-json/wp/v2/media/${post.featured_media}`;

getPosts(category: Category, 
         page: number = 1, 
         pageSize: number = 10): Observable<Post[]> {
    return this.httpService.get(url)
      .map(res => res.json())
      .flatMap(posts => Observable.from(posts))
      .map(post => new Post(post))
      .flatMap(post=> 
         this.httpService.get(mediaUrl)
             .map(res=>res.json())
             .map(image=> Object.assign(post, {
                    featured_image: new Media(body) 
                   }) 
              )
      ).toArray();

}    

在我解释我做了什么之前,让我告诉你如何调试它。 如果您不确定两个运营商之间发生了什么,请使用do

...
.map(res => res.json())
.do(data=> console.log(data))
.flatMap(
...

do将只在控制台中打印数据并继续执行。 我在这里一步一步地做什么:

.map(res => res.json()) 

转换后的流,其中包含对res.json数据流的响应

.flatMap(posts => Observable.from(posts))

每个流都包含一个数组,我们不希望这样。我们想逐个采取每个项目。因此,将stream<data[]>转换为stream<data>

.map(post => new Post(post))

将每个原始对象转换为post对象

.flatMap(post=> 

现在对于每个obj,我们想要再做一次http调用。因此,使用flatMap,我正在进行http调用,获取响应并修改响应并将对象添加回流。

.map(image=> Object.assign(post, {
                featured_image: new Media(body) 
               }) 
          )

获取媒体响应并返回post对象,并添加一个新的参数featured_image