* ngIf包含多个异步管道变量

时间:2017-11-20 12:10:46

标签: angular

尝试将两个可观察对象合并为一个*ngIf,并在两者都发出时显示用户界面。

采取:

<div *ngIf="{ language: language$ | async, user: user$ | async } as userLanguage">
    <b>{{userLanguage.language}}</b> and <b>{{userLanguage.user}}</b>
</div>

来自:Putting two async subscriptions in one Angular *ngIf statement

这可以编译,但在我的情况下,language$user$将来自两个HTTP请求,似乎user$会引发运行时错误,如TypeError: _v.context.ngIf.user is undefined。< / p>

基本上我真正想要的是(这不起作用):

<div *ngIf="language$ | async as language && user$ | async as user">
    <b>{{language}}</b> and <b>{{user}}</b>
</div>

是最佳解决方案:

  • 在组件内订阅并写入变量
  • 将组件内的两个可观察对象与withLatestFrom
  • 组合在一起
  • 添加空检查{{userLanguage?.user}}

4 个答案:

答案 0 :(得分:34)

此条件应使用嵌套的ngIf指令处理:

<ng-container *ngIf="language$ | async as language">
  <div *ngIf="user$ | async as user">
    <b>{{language}}</b> and <b>{{user}}</b>
  </div>
<ng-container>

缺点是HTTP请求将以串行方式执行。

为了同时执行它们并且仍然有languageuser个变量,需要更多嵌套:

<ng-container *ngIf="{ language: language$ | async, user: user$ | async } as userLanguage">
  <ng-container *ngIf="userLanguage.language as language">
    <ng-container *ngIf="userLanguage.user as user">
      <div><b>{{language}}</b> and <b>{{user}}</b></div>
    </ng-container>
  </ng-container>
</ng-container>

更有效的方法是在此时将逻辑从模板移动到组件类,并创建单个可观察对象,例如,与withLatestFrom

答案 1 :(得分:14)

您还可以使用以下技巧。您将需要一个额外的嵌套。

<ng-container *ngIf="{a: stream1$ | async, b: stream2$ | async, c: stream3$ | async} as o">
  <ng-container *ngIf="o.a && o.b && o.c">
    {{o.a}} {{o.b}} {{o.c}}
  </ng-container>
</ng-container>

对象o永远是真实的,因此第一个* ngIf很容易用于保存流值。在内部,必须使用o为变量命名。

答案 2 :(得分:4)

这取决于你想要什么,但我认为forkJoin运算符带有加载标志,可能是个好主意。

https://www.learnrxjs.io/operators/combination/forkjoin.html

forkJoin等待所有Observable完成后返回其订阅中的值

Observable.forkJoin(
  Observable.of("my language").delay(1000),
  Observable.of("my user").delay(1000),
).subscribe(results => {
  this.language = results[0]
  this.user = results[1]
})

您可以将错误捕获到订阅的onError并显示它。

答案 3 :(得分:0)

我使用了一个自定义函数,它结合了所有类可观察对象,具有以下优点:

  • async 管道数减少到 1,而不使用其他答案中提出的样板
  • 允许您同步读取组件代码中的所有 observable
  • 允许您在 Angular devtools extension查看当前观察值

组件代码

@Component({
  template: `
    selector: 'app-component',
    <ng-container *ngIf="observables$ | async; let observe;">
      <div *ngIf="observe.language$ && observe.user$">
        <b>{{observe.language$}}</b> and <b>{{observe.user$}}</b>
      </div>
    </ng-container>
  `
})
export class MyComponent {

  language$ = of('English');
  user$ = of('John');
  observables$ = combineComponentObservables<MyComponent>(this);

  ngAfterViewInit() {
    // We can read values synchronously from this field
    const value = this.observables$.value.language;
  }
}

实用程序代码

import { EventEmitter } from '@angular/core';
import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

type FunctionParameter<T> = T extends (arg: infer H) => any ? H : never;
type ClassObservables<T> = {
  [I in keyof T]: T[I] extends Observable<any> ? FunctionParameter<Parameters<T[I]['subscribe']>[0]> : never;
};
type SubType<Base, Condition> = Pick<Base, {
  [Key in keyof Base]: Base[Key] extends Condition ? Key : never
}[keyof Base]>;
export type Observables<T> = ClassObservables<SubType<Omit<T, 'templateObservables$' | '$observables'>, Observable<any>>>;

export const combineComponentObservables = <T>(component: T): Observable<Observables<T>> & { value: Observables<T> } => {
  const keysOfObservableMembers = Object.keys(component)
    .filter(key => component[key] instanceof Observable && !(component[key] instanceof EventEmitter));
  const res = combineLatest(
    keysOfObservableMembers.map(key => component[key] as Observable<any>)
  ).pipe(
    map(observers => {
      const result = {};
      observers.forEach((obs, idx) => result[keysOfObservableMembers[idx]] = obs);
      (component as any).$observables = result;
      (res as any).value = result;
      return result as Observables<T>;
    })
  );
  return res as Observable<Observables<T>> & { value: Observables<T> };
};

注意:

  • 要进行模板类型检查,您需要使用启用了 Ivy 的最新版本的 Angular。还要确保您启用了语言服务,并在您的 strictTemplates 内的 angularCompilerOptions 中启用了 tsconfig.json
  • ng-container 中的内容只会在所有 observable 解析后显示。因此,如果有任何依赖于 dom 元素的可观察对象,您将遇到问题。话虽如此,此处发布的所有其他答案都存在同样的问题。为防止出现此问题,我倾向于使用 RouteResolvers 来预取所有 API 数据。