To begin, let's delve into the reasoning behind using an exclamation point in the following code snippet:
@if (index() !== null && index() !== undefined) {
<div appMyComponent [index]="index()!"></div>
}
It is necessary to include the exclamation point (!
).
The input is defined as follows:
index = input<number|null>();
When consuming an input, you must call it since it functions as a function. This aligns with your current approach.
Is the outcome always consistent when calling an impure function? No, it may vary. Hence, the compiler requires more than just two checks to determine that the index
passed as an input in your @if
statement is not null.
This scenario can be easily replicated in Typescript to examine the feedback from the compiler:
https://i.sstatic.net/7hlS7.png
As indicated here, the value
can be either a number
, null
, or even undefined (attributed to it being a non-mandatory input, which leads to an undefined value if left unassigned).
So, how do we address this situation?
Let's first consider the solution in Typescript and then explore its adaptation within an Angular template context.
When invoking a function that returns number | null | undefined
, storing the result in a variable allows for subsequent type-specific validations, enabling the compiler to ascertain the type as number
post-validation:
https://i.sstatic.net/cDMEz.png
Next step: How can we implement this solution in an Angular template?
A neat trick, previously applicable with the old syntax (*ngIf
), involves storing observables in variables to prevent falsy values such as 0
, effectively ensuring the truthy evaluation of *ngIf
:
<div
*ngIf="{
value1: value1$ | async,
value2: value2$ | async,
value3: value3$ | async
} as data"
>
{{ data.value1 }} {{ data.value2 }} {{ data.value3 }}
</div>
One interesting aspect is that *ngIf
will always be true since it receives an object. This technique facilitates the storage of observable values in variables, handling integers to evade falsy interpretations by *ngIf
, among other things.
The new @if
syntax operates similarly. Here's how you can achieve your objective based on your scenario:
@if({ index: index() }; as data){
@if(data.index != null){
<sub-sub-component [index]="data.index"></sub-sub-component>
}
}
By calling the index
function once, storing it in a variable, and asserting the variable, Typescript is able to narrow down the type accurately.
I've created a Stackblitz repro for you to experiment with, featuring both your current setup and the alternative approach I just outlined. Your current code triggers a compiler error, whereas the modified version works seamlessly: https://stackblitz.com/edit/stackblitz-starters-wfvrva?file=src%2Fmain.ts