I have the following container component
export class EventsComponent {
data$: Observable<Data[]> = this.store.select(data);
loading$: Observable<boolean> = this.store.select(loading);
}
And bind the observables via | async
to the presentational component:
<app-presentational
[rowData]="data$ | async"
[loading]="loading$ | async"
...
export class PresentComponent {
@Input()
rowData: Data[];
@Input()
loading: boolean;
}
However, the TS compiler always complains that the async pipe may return null.
Update, this is the exact error i get
Type 'boolean | null' is not assignable to type 'boolean'.
Type 'null' is not assignable to type 'boolean'.ngtsc(2322)
So do I really have to change all my @Input()
to this?
export class PresentComponent {
@Input()
rowData: Data[] | null;
@Input()
loading: boolean | null;
}
As Angular docs points here:
There are two potential workarounds to the above issues:
In the template, include the non-null assertion operator ! at the end of a nullable expression, such as <user-detail [user]="user!" />.
In this example, the compiler disregards type incompatibilities in nullability, just as in TypeScript code. In the case of the async pipe, note that the expression needs to be wrapped in parentheses, as in <user-detail [user]="(user$ | async)!" />.
Disable strict null checks in Angular templates completely.
When strictTemplates is enabled, it is still possible to disable certain aspects of type checking. Setting the option strictNullInputTypes to false disables strict null checks within Angular templates. This flag applies for all components that are part of the application.
But, instead of using the non-null assertion operator or even disabling the Angular strict checks, you could either:
??
) - available in 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
and 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[] = [];
}
Note that nowadays you should prefer to use option 3 rather than the 2, as input setter coercion fields
are being deprecated.