我有以下容器组件
export class EventsComponent {
data$: Observable<Data[]> = this.store.select(data);
loading$: Observable<boolean> = this.store.select(loading);
}
并通过
| async
将可观察量绑定到表示组件:
<app-presentational
[rowData]="data$ | async"
[loading]="loading$ | async"
...
export class PresentComponent {
@Input()
rowData: Data[];
@Input()
loading: boolean;
}
然而,TS编译器总是抱怨异步管道可能返回null。
更新,这就是我得到的确切错误
Type 'boolean | null' is not assignable to type 'boolean'.
Type 'null' is not assignable to type 'boolean'.ngtsc(2322)
那么我真的必须把我所有的
@Input()
都改成这个吗?
export class PresentComponent {
@Input()
rowData: Data[] | null;
@Input()
loading: boolean | null;
}
正如 Angular 文档所指出的那样这里:
上述问题有两种潜在的解决方法:
在模板中,包含非空断言运算符!在最后 可以为 null 的表达式,例如
. 在此示例中,编译器忽略了类型不兼容的情况 可空性,就像在 TypeScript 代码中一样。在异步的情况下 管道,请注意表达式需要用括号括起来,如 在
. 完全禁用 Angular 模板中的严格 null 检查。
启用 strictTemplates 后,仍然可以禁用 类型检查的某些方面。设置选项 strictNullInputTypes 设置为 false 会禁用严格的 null 检查 有角度的模板。该标志适用于所有组成部分的组件 应用程序。
但是,您可以:
,而不是使用非空断言运算符,甚至禁用 Angular 严格检查??
) - 在 Angular 12+ 中可用:TS:
@Component({
template: ``,
})
export class PresentationalComponent {
@Input() loading: boolean;
@Input() rowData: Data[];
}
HTML:
<app-presentational
[loading]="(loading$ | async) ?? false"
[rowData]="(rowData$ | async) ?? []"
></app-presentational>
ngAcceptInputType_*
:TS:
@Component({
template: ``,
})
export class PresentationalComponent {
static ngAcceptInputType_loading?: boolean | null;
// Note that if you have `@angular/cdk` installed you can use this instead:
// static ngAcceptInputType_loading: BooleanInput;
static ngAcceptInputType_rowData?: Data[] | null;
@Input() loading: boolean;
@Input() rowData: Data[];
}
HTML:
<app-presentational
[rowData]="rowData$ | async"
[loading]="loading$ | async"
></app-presentational>
getter
和setter
的不同类型:TS:
@Component({
template: ``,
})
export class PresentationalComponent {
@Input()
// Note that if you have `@angular/cdk` installed you can use `BooleanInput` instead.
set loading(loading: boolean | null | undefined) {
this._loading = loading ?? false;
}
get loading(): boolean {
return this._loading;
}
private _loading: Data[];
@Input()
set rowData(rowData: Data[] | null | undefined) {
this.rowData = rowData ?? [];
}
get rowData(): Data[] {
return this._rowData;
}
private _rowData: Data[] = [];
}
input setter coercion fields
已被弃用。
修改您的代码片段如下。应该修复空分配错误。
<app-presentational
[rowData]="(data$ | async)!"
[loading]="(loading$ | async)!"
...
仅从您提供的信息中我无法判断,但当 Observable 未提供值时,组件看起来只需要 null 处理
<app-presentational
[rowData]="data$ | async"
[loading]="(loading$ | async) || false"
/>
Angular 文档建议使用非空断言运算符
!
。一个例子是:
<user-detail [user]="(user$ | async)!"></user-detail>
使用非空断言运算符表示您确信某个值将始终存在,这会覆盖 TypeScript 的空检查行为。
但是,这种方法并不总是合适的。当您可以从一开始就保证一个值时,例如使用
of()
定义可观察量时,最好使用它。
另一种方法可以将组件输入转换为可观察值,或者接受
null
值,但现在我们正在使“哑”组件变得更聪明。
以下是我坚持的一些一般准则和示例:
如果您确定从一开始就有一个值,请使用非空断言运算符:
<user-detail [user]="(user$ | async)!"></user-detail>
当您知道 user$ 总是会发出一个值时,这很合适,也许因为它是立即用一个值初始化的。
如果您正在等待一个值,请实现加载状态并在该值可用时渲染组件:
<ng-container *ngIf="user$ | async as user; else loading">
<user-detail [user]="user"></user-detail>
</ng-container>
<ng-template #loading>
<p>Loading...</p>
</ng-template>
在这种情况下,ngIf 检查 user$ 是否已发出值。如果没有,则会显示加载模板。
如果即使在等待值时也需要渲染组件,请提供后备:
<user-detail [user]="(user$ | async) || defaultUser"></user-detail>
这里,defaultUser 用作后备值,直到 user$ 发出值。当您想要确保组件始终呈现时,即使实际数据尚不可用,此方法也很有用。
通过遵循这些规则并使用适当的示例,您可以确保您的应用程序更有效地处理值和加载状态。
您的 Store State 初始值未定义,这就是为什么您得到 null 或者您没有声明 Store State 对象
export class PresentComponent {
@Input()
rowData: Data[] = [];
@Input()
loading = false;
对于 .select,您还可以选择它与字符串一起使用的状态的一部分: 尝试将它们转换为字符串
export class EventsComponent {
data$: Observable<Data[]> = this.store.select('data');
loading$: Observable<boolean> = this.store.select('loading');
当进入处于状态的对象内部时,您也可以使用倍数。
this.store.select('name1', 'name2', ...)
export class EventsComponent implements OnInit {
data: Data[] = [];
loading = false;
constructor(private store: Store<{ data: Data[], loading: boolean}>) {
}
ngOnInit(): void {
// you can do one by one
this.store.select('data')
.subscribe(next => this.data = next);
this.store.select('loading')
.subscribe(next => this.loading = next);
// or do
// all together
this.store.subscribe(next => {
this.data = next.data;
this.loading = next.loading;
});
}
}