*ngFor and *ngIf are the two most-used built-in Angular structural directives, and understanding their individual behavior — plus the one genuine restriction on combining them directly — covers the majority of conditional and repeated rendering in a typical Angular template.
Basic *ngFor
{{ product.name }}
*ngFor with the index
{{ i + 1 }}. {{ product.name }}
Basic *ngIf
Welcome back!
*ngIf with if/else
Welcome back!
Please log in
*ngIf with if/else-if/else
Loading...
Something went wrong.
Content loaded successfully.
Angular has no native *ngIf/*ngElseIf/*ngElse chain — nesting templates this way is the standard workaround for an if/else-if/else chain using only *ngIf and named ng-template references.
Why you can't put *ngFor and *ngIf on the same element
{{ product.name }}
Two structural directives can't coexist on the same element, since each one desugars to its own wrapping behind the scenes — having both would mean two conflicting template wrappers on one element.
The fix: wrap with ng-container
{{ product.name }}
Following the ng-container pattern covered elsewhere on this site, this splits the two directives across the container and the actual element without adding any extra wrapping DOM node to the rendered output.
Looping over a plain object's keys and values
{{ entry.key }}: {{ entry.value }}
objectEntries(obj: Record) {
return Object.entries(obj).map(([key, value]) => ({ key, value }));
}
*ngFor only iterates arrays and other genuinely iterable values natively — for a plain object, converting it to an array of key/value pairs first (via Object.entries(), as shown here, or Angular's built-in KeyValue pipe) is necessary before *ngFor can loop over it.
Using the built-in KeyValue pipe instead
{{ entry.key }}: {{ entry.value }}
The keyvalue pipe does the same object-to-array conversion directly in the template, without needing a helper method in the component class — generally the more convenient choice for a simple case, while a component method offers more control if the conversion needs custom sorting or filtering logic.
The modern alternative: Angular's built-in control flow syntax (Angular 17+)
@for (product of products; track product.id) {
{{ product.name }}
}
@if (isLoggedIn) {
Welcome back!
} @else {
Please log in
}
Newer Angular versions introduced this built-in @for/@if syntax as a more concise, JavaScript-like alternative to the *ngFor/*ngIf directives — both approaches remain valid and supported, with the newer syntax becoming increasingly common in projects built on Angular 17 and later.