如何处理Angular 6中两个可观察对象的数据?

时间:2018-09-26 18:01:29

标签: angular

我已经完成了《英雄之旅》教程(两次),以及Brad Traversy的《从前到后的角度》。我有一点(功能性)Python经验,但是仍然想尽我所能围绕Angular语法以及类如何工作。

作为一种实践,我正在基于ToH制作一个图书馆Web应用程序,在那里我可以使用单独的服务将书和作者存储在单独的组件中,以通过HTTP服务获取它们。

遵循Angular Style Guide,将逻辑放在组件而不是模板中之后,我未成功制作了一个书本/作者-视图组合的组件,显示了作者及其所写的书分别,反之亦然。 (我不想将其放在HTML模板中。)

这是 author.component.ts book.component.ts 是相同的,除了作者被“ book / books”所取代:

import { Component, OnInit } from '@angular/core';
import { Author } from '../../models/author';
import { AuthorService } from '../../services/author.service';

@Component({
  selector: 'app-authors',
  templateUrl: './authors.component.html',
  styleUrls: ['./authors.component.css']
})
export class AuthorsComponent implements OnInit {
  authors: Author[];

  constructor(private authorService: AuthorService) { }

  ngOnInit() {
    this.getAuthors();
  }

  getAuthors(): void {
    this.authorService.getAuthors().subscribe(authors =>  
      this.authors = authors);
  }

Book / AuthorService与Tour of Heroes的“ HeroService”相似,因为它们使用http.get:

/** GET authors from the server */
getAuthors (): Observable<Author[]> {
   return this.http.get<Author[]>(this.authorsUrl);

模型 author.ts book.ts

export class Author {
  id: number;
  firstName: string;
  lastName: string;
  booksAuthored?: number[];
}

export class Book {
  id: number;
  authorId?: number;  // TODO: allow multiple authors
  title: string;
}

我可能已经正确理解,一个可观察对象是一个流对象(而不是JSON类型的对象),并且不能像常规数组那样被切片。

我想要一个可以将author.lastName和author.firstName串联在一起的函数,然后列出属于各自作者的书籍。我已经能够在HTML模板中使用ngFor(让作者的作者)和ngIf(如果book.authorId === author.id)做到这一点,但是现在我想在组件(或服务)中做同样的事情。 )

2 个答案:

答案 0 :(得分:0)

您可以向Author类添加一个函数以允许串联,

export class Author {
  id: number;
  firstName: string;
  lastName: string;
  booksAuthored?: number[];

  public fullName() {
    return this.firstname + ' ' + this.lastname;
  }
}

那么只要您有一个Author对象,就调用author.fullname()

要获取每位作者的书籍,可以在get authors方法中执行以下操作:

  getAuthors(): void {
    this.authorService.getAuthors().pipe(map(eachAuthor => {
       eachAuthor.booksAuthorWrote = this.bookService.getBooks<Book[]>(eachAuthor.id);
    }))
    .subscribe(authors =>  
      this.authors = authors
    );
  }

这将检索由getAuthors()调用返回的每个作者的书籍。如果您想避免进行那么多服务器调用,只需获取所有作者和所有书籍,然后运行与模板中所用类似的循环即可:

for(let author of this.authors) {
  for(let book of this.books) {
    if(book.authorID === author.id) {
      this.authors.booksAuthorWrote.push(book);
    }
  }
}

这假设您向author类添加了一系列书籍,如下所示:

export class Author {
  id: number;
  firstName: string;
  lastName: string;
  booksAuthored?: number[];
  booksAuthorWrote?: Book[];
}

答案 1 :(得分:0)

combineLatest可能是您正在寻找的。

因此,我们将两个流组合在一起:作者和书籍。重要的是要知道,只有在作者流和书籍流都具有事件的情况下,组合流才会开始发送事件。

接下来,我们要操纵结果。因此,我们使用map运算符在其中循环遍历每个作者,并创建一个包含串联名称和书籍的新对象。在books属性内,添加与作者ID匹配的所有书籍。

使用此结果流,您可以创建一个显示作者列表及其相应书籍的组件。

import {combineLatest} from 'rxjs';

combineLatest(authors$, books$).pipe(
    map(([authors, books]) => {
        return authors.map(author => {
            return {
                name: author.firstName + ' ' + author.lastName,
                books: books.filter(book => book.authorId === author.id)
            };
        })
    })
).subscribe(res => console.log(res));

有关combineLatest的更多文档:

http://rxmarbles.com/#combineLatest

http://reactivex.io/documentation/operators/combinelatest.html