【问题标题】:Angular: how to handle Observables and use result in different methodsAngular:如何处理 Observables 并以不同的方法使用结果
【发布时间】:2021-11-19 16:55:14
【问题描述】:

我很难理解应该如何使用 Observables 来满足我的要求。例如,假设我有一个应用程序并且为了国际化,我正在使用带有所有标签和翻译的 REST 服务。某些标签可能会根据页面上执行的操作而更改。而且我不确定如何从服务中检索数据并以不同的方式使用它。

让我们看看这个伪代码:

labels: KeyValuePair[];
userDetails: UserDetails;
displayedColumns: string[];

// services below generated thanks to ng-swagger-gen
constructor(
   labelService: LabelService, 
   userService: UserService
){}

ngOnInit() {
   this.userService.get().subscribe(
      (userData) => {
        this.userDetails = userData; 
        console.log(userData);
      },
      (error) => {
        this.errorMessage = error;
      }
    );

   this.labelService.get(this.userDetails.getLanguage()).subscribe(
      (labelsResponse) => {
        this.labels = labelsResponse; // use the labels in HTML template
        console.log(labelsResponse);
      },
      (error) => {
        this.errorMessage = error;
      }
    );

   this.retrieveTableColumns();
}

retrieveTableColumns() {
   this.displayedColumns = this.labels.filter(/* filter columns */);
}

executeMethods() { // method executed from HTML template
   this.doSomethingWithUserDetails();
   this.doSomethingWithLabels();
}

doSomethingWithUserDetails() {
   // 1. do differnet things and use it in HTML template
   // 2. check one of properties and based on this 
   this.setColumns();
}

setColumns() { // columns used in mat-table
    if (this.userDetails.allowedForAction) {
        this.displayedColumns = this.columnsDef.concat(['action']);
        this.hiddenColumns.push(this.displayedColumns.length);
    } else {
        this.displayedColumns = this.columnsDef;
    }
}

doSomethingWithLabels() {
   // do some magic using retrieved labels
}

为了生成网络客户端服务,我使用了ng-swagger-gen

如何处理Observables 来实现这一点?所以能够在不同的地方使用数据。我听说过用.toPromise 转换它并使用async await,但我也听说这是反模式......所以不知道在这里做什么......如果重要的话,我正在使用Angular 9 .

【问题讨论】:

  • toPromise 绝对是 Angular 的反模式。虽然您的问题有一个很好的答案,但我建议您使用 i18n 库。 Angular 自己的 localize 包为每种语言构建应用程序版本,或者 transloco 从 json 动态获取翻译。翻译公司宁愿使用标准格式文件也不愿调用您的 API

标签: angular rxjs observable


【解决方案1】:

基础知识

您只需将响应保留为可观察的:

labels$ = this.labelService.get(this.userDetails.getLanguage())

在 Html 中你使用异步管道:

*ngFor="let label of labels$ | async"

如果你想在你的打字稿中转换数据,你可以在 observable 上使用 '.pipe()' 并创建一个新的:

newLabels$ = labels$.pipe(
    map(labels => labels.filter(label => label.id > 5)),
    // Or whatever you want to filter, sorry no inspiration
    tap(labels => console.log(labels)),
    // more pipes ...
)

您可以创建自己的函数以在这些管道中使用。

您也可以编写一个函数,将可观察对象内的任何对象作为参数,然后在您的 html 中使用该函数:

*ngFor="let label of labelsSortedByName((labels$ | async)!)"

我使用ngFor 作为示例,因为它被大量使用,但您可以在任何地方使用它。基本上尽量不要订阅你的代码,只使用异步管道。学习高阶可观察对象,将多个可观察对象组合在一起,您将获得很多乐趣。

合并 Observables

例子:

first$ = of("en") // An observable representing the result of your first request

second(lang: string): Observable<string> { // Request requiring a string argument
  return of(lang + " language")
}

result$ = this.first$.pipe( // The resulting observable of the second request
  mergeMap(r => {
    return this.second(r)
  })
)

在模板中:

{{ result$ | async }}  <!-- Result: 'en language' -->

在此示例中,first$second() 只是您的请求的模拟。我意识到你的第二个请求不仅仅是连接字符串,但想法是一样的。它接受第一个 Observable 持有的类型的参数,并返回一个新的 Observable。

这里有一个Stackblitz 供你玩。

【讨论】:

  • 感谢您的回答。但我还是很困惑。 UserDetails 来自 observable 那么我们如何确定getLanguage() 在您传递它以获取标签时会出现?另外,您最后一个代码示例中感叹号的目的是什么?
  • 感叹号只是确保编译器会存在一个对象。它存在是因为严格的类型检查,即使没有内容,您也应该确保返回一个有效的对象(例如空数组)。对于另一个问题,您只需使用诸如 mergeMap、flatMap 之类的管道......我将用一个示例更新我的答案。
  • 感谢您的更新。我今天尝试提供一些更改,但我一直坚持。你能看看我更新的帖子并阐明一些观点,或者甚至用工作示例更新我的 stackblitz 吗?非常感谢您的帮助:)
  • 我觉得你现在在问三个不同的问题。如果第一个问题已回答,则将其标记为已解决并创建新问题,这样它就不会变成一米长的帖子。如果您在评论中链接新问题,我很乐意稍后查看这些问题和您的 stackblitz。
  • 我明白你的意思,你是对的。有时我会忘记它并试图在一个地方获得所有答案,当它们相关时(仅基于流程)。我在这里问了新的:stackoverflow.com/questions/70070009/…
猜你喜欢
  • 2013-07-03
  • 1970-01-01
  • 1970-01-01
  • 2017-02-04
  • 2016-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-01
相关资源
最近更新 更多