尝试将两个可观察对象合并为一个*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}}
答案 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请求将以串行方式执行。
为了同时执行它们并且仍然有language
和user
个变量,需要更多嵌套:
<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,而不使用其他答案中提出的样板。组件代码:
@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> };
};
注意:
strictTemplates
内的 angularCompilerOptions
中启用了 tsconfig.json
。ng-container
中的内容只会在所有 observable 解析后显示。因此,如果有任何依赖于 dom 元素的可观察对象,您将遇到问题。话虽如此,此处发布的所有其他答案都存在同样的问题。为防止出现此问题,我倾向于使用 RouteResolvers 来预取所有 API 数据。