[英]*ngIf with multiple async pipe variables
Been trying to combine two observables into one *ngIf
and show the user interface when both have emitted.一直试图将两个 observable 组合成一个
*ngIf
并在两者都发出时显示用户界面。
Take:拿:
<div *ngIf="{ language: language$ | async, user: user$ | async } as userLanguage">
<b>{{userLanguage.language}}</b> and <b>{{userLanguage.user}}</b>
</div>
From: Putting two async subscriptions in one Angular *ngIf statement来自: 将两个异步订阅放在一个 Angular *ngIf 语句中
This works as far as it compiles however in my case language$
and user$
would be from two HTTP requests and it seems user$
throws runtime errors like TypeError: _v.context.ngIf.user is undefined
.这在编译时有效,但是在我的情况下,
language$
和user$
将来自两个 HTTP 请求,并且似乎user$
抛出运行时错误,如TypeError: _v.context.ngIf.user is undefined
。
Essentially what I really want is (this doesn't work):基本上我真正想要的是(这不起作用):
<div *ngIf="language$ | async as language && user$ | async as user">
<b>{{language}}</b> and <b>{{user}}</b>
</div>
Is the best solution:是最好的解决方案:
withLatestFrom
withLatestFrom
结合起来{{userLanguage?.user}}
{{userLanguage?.user}}
This condition should be handled with nested ngIf
directives:这种情况应该用嵌套的
ngIf
指令处理:
<ng-container *ngIf="language$ | async as language">
<div *ngIf="user$ | async as user">
<b>{{language}}</b> and <b>{{user}}</b>
</div>
<ng-container>
The downside is that HTTP requests will be performed in series.缺点是 HTTP 请求将按顺序执行。
In order to perform them concurrently and still have language
and user
variables, more nesting is required:为了同时执行它们并且仍然有
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>
More efficient way way to do this is to move logic from template to component class at this point and create a single observable, eg with withLatestFrom
更有效的方法是此时将逻辑从模板移动到组件类并创建单个可观察对象,例如
withLatestFrom
You can also use the following trick.您还可以使用以下技巧。 You will need one additional nesting.
您将需要一个额外的嵌套。
<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>
The object o is ever truthy, therefore the first *ngIf is simple used to save the stream values.对象 o 永远是真实的,因此第一个 *ngIf 很简单,用于保存流值。 inside you have to namespace your variables with o.
在里面你必须用 o 命名你的变量。
That's depend what do you want but I think forkJoin operator with a loaded flag, could be a good idea.这取决于您想要什么,但我认为带有加载标志的 forkJoin 运算符可能是一个好主意。
https://www.learnrxjs.io/operators/combination/forkjoin.htmlhttps://www.learnrxjs.io/operators/combination/forkjoin.html
The forkJoin wait that all Observable are completed to return their values in its subscribe 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]
})
You can catch errors into onError of the subscribe and display it.您可以将错误捕获到订阅的 onError 中并显示它。
I use a custom function that combines all class observables providing the following advantages:我使用了一个自定义函数,它结合了所有类可观察对象,具有以下优点:
async
pipe count to 1 without the boilerplate proposed in other answers .async
管道数减少到 1,而无需其他答案中提出的样板。 COMPONENT CODE :组件代码:
@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;
}
}
UTILITY CODE :实用程序代码:
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> };
};
NOTES:笔记:
strictTemplates
in the angularCompilerOptions
within your tsconfig.json
.angularCompilerOptions
strictTemplates
中启用了tsconfig.json
。ng-container
will only display once all observables have resolved. ng-container
的内容只会在所有 observables 解析后显示。 Therefore you will experience issues if any observables that depend on dom elements.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.