【问题标题】:How do I manipulate data from two observables in Angular 6?如何在 Angular 6 中操作来自两个可观察对象的数据?
【发布时间】:2019-03-02 13:59:41
【问题描述】:

我已经完成了英雄之旅教程(两次)和 Brad Traversy 的 Angular-Front-to-Back。我有一点(函数式)Python 经验,但仍在尝试围绕 Angular 语法和类的工作原理展开思考。

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

按照Angular Style Guide 关于将逻辑放在组件中而不是在模板中,我不成功制作了一个组合的书/作者视图组件,显示作者和他们分别写了哪些书,反之亦然。 (我不想把它放在 HTML 模板中。)

这里是 author.component.tsbook.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.tsbook.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;
}

我可能已经正确理解了 observable 是一个流对象(而不是 JSON 类型的对象),不能像普通数组一样被切片。

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

【问题讨论】:

  • 其中一个答案解决了您的问题吗?
  • 是的,谢谢,我认为您的回答最接近我的需要。然而,K Adrian 的回答让我对这个库 webapp 实验的其他部分有了一些了解。我对两者都投了赞成票,但我的声望只有 6 个,所以它没有显示。 :-)

标签: angular


【解决方案1】:

combineLatest observable 可能是您正在寻找的。​​p>

所以我们将两个流结合在一起:作者和书籍。重要的是要知道,只有当作者流和书籍流本身都有事件时,组合流才会开始发送事件。

接下来我们要处理结果。因此,我们使用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

【讨论】:

    【解决方案2】:

    您可以向 Author 类添加一个函数以允许连接,

    export class Author {
      id: number;
      firstName: string;
      lastName: string;
      booksAuthored?: number[];
    
      public fullName() {
        return this.firstname + ' ' + this.lastname;
      }
    }
    

    那么任何时候你有一个作者对象调用 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);
        }
      }
    }
    

    这假设您将一系列书籍添加到作者类,如下所示:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-21
      • 2016-09-12
      • 2021-02-13
      • 1970-01-01
      • 2019-02-18
      • 2020-06-03
      相关资源
      最近更新 更多