首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用可观测数据合成视图模型

用可观测数据合成视图模型
EN

Stack Overflow用户
提问于 2019-11-20 04:44:53
回答 2查看 591关注 0票数 2

下面的代码简单地说明了我试图用两个模型做什么。假设我们有一个Book模型和一个Author模型。首先,我们使用返回类型Books的服务获取一个Observable<Book>列表。然后,我们使用*ngFor迭代每一次,但是我们也希望在书的旁边显示Author,所以在我们的迭代中我们有如下所示的HTML:

代码语言:javascript
复制
<span>{{ getAuthorName(book) | async }}</span>

getAuthorName有自己的请求,可以在AuthorService上通过ID获取作者。问题是,当我们在这里返回一个Observable<string>时,浏览器会完全崩溃,因为更改检测会疯狂运行,因为在每个摘要周期上都会返回一个“新的”可观察到的结果,这会导致更改检测再次运行,对吗?

下面的代码可以很容易地放入StackBlitz并复制(我没有链接到它,因为它会炸掉您的浏览器CPU)

代码语言:javascript
复制
<ul>
  <li *ngFor="let item of getData() | async">
    {{ getOtherData(item.id) | async }}
  </li>
</ul>
代码语言:javascript
复制
import { Component } from '@angular/core';
import { of } from 'rxjs';
import { filter, map } from 'rxjs/operators';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular';

  getData() {
    return of([{
      id: 1
    },{
      id: 2
    },{
      id: 3
    }]);
  }

  getOtherData(id: number) {
    const data = of([{
      id: 1,
      name: 'Matt'
    },{
      id: 2,
      name: 'Steve'
    },{
      id: 3,
      name: 'Alice'
    }]);

    return data.pipe(
      map(x => x.find(d => d.id == id))
    )
  }
}

显然,这里可以进行一些优化,比如作者的缓存等,但是一般来说,在RXJS的世界中应该如何处理这个场景呢?我知道有处理像秋田和ngrx这样的状态的完整框架,但是如果我不想处理所有这些,只想用原始的rxjs构建我的应用程序呢?这个问题的解决方案是什么?您是否使用其他rxjs操作符来阻止这种情况发生?(我找不到任何帮助它的方法,因为每次都会返回一个新的Obserable )您是否只需要使用视图模型而不是方法?那样的话,eis总是仅仅是对可观察到的事物的一次引用?(例如,BookAuthorViewModel,它有一个$author属性,在我们将视图迭代到单个可观察到的视图之前设置该属性?)

我还没有找到一个“感觉正确”的解决方案,我觉得我不得不使用这些状态管理框架来做这样简单的事情。

EN

回答 2

Stack Overflow用户

发布于 2019-11-20 05:05:01

您不应该在视图中调用方法。如果您有一个返回可观测值的方法,那么每次更改保留检查值时,都会创建一个新的可观测值。

代码语言:javascript
复制
data$ = of([{
  id: 1
},{
  id: 2
},{
  id: 3
}]);

otherData$ = of([{
  id: 1,
  name: 'Matt'
},{
  id: 2,
  name: 'Steve'
},{
  id: 3,
  name: 'Alice'
}]);

然后绑定到可观察到的

代码语言:javascript
复制
<li *ngFor="let item of data$ | async">
  {{ otherData$ | async | find: item.id }}
</li>

把找到的管子

代码语言:javascript
复制
@Pipe({
  name: 'find'
})
export class FindPipe implements PipeTransform {
  transform(options, id): string {
    return options.find(option => option.id === id);
  }
}

StackBlitz https://stackblitz.com/edit/angular-6vgosc

票数 3
EN

Stack Overflow用户

发布于 2019-11-20 07:06:49

另一种选择是组合服务中的流。

对于每一本书,你都需要作者的名字。在我的示例中,对于每个ToDo,它都会获得用户名。

代码语言:javascript
复制
  todos$ = this.http
    .get<ToDo[]>(this.todoUrl)
    .pipe(catchError(err => throwError("Error occurred")));

  todosWithUser$ = this.todos$.pipe(
    mergeMap(todos =>
      forkJoin(
        todos.map(todo =>
          this.http.get<User>(`${this.userUrl}/${todo.userId}`).pipe(
            map(user =>({
                  ...todo,
                  usersName: user.name
                } as ToDo)
            )
          )
        )
      )
    )
  );

https://stackblitz.com/edit/angular-users-todos-deborahk

todos$流获取所有待办事项(或您的示例中的书籍)。

它使用mergeMap映射和合并列表中的每个ToDo。这是自动订阅“内部”可观察到的检索用户。

forkJoin允许我们处理ToDo的数组并为每个数组找到用户。然后将它们重新加入到一个数组中。

最后一个mapusersName属性添加到每个Todo。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58946846

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档