*ngIf and *ngFor are Angular's core structural directives for conditional rendering and looping — each has a few less-obvious variants worth knowing, and combining both on one element needs a specific workaround.
Basic ngIf
Welcome back, {{ user.name }}
ngIf with an else template
Welcome back, {{ user.name }}
Please log in
ngIf with then and else together
Welcome, {{ user.name }}
Please log in
Simulating else-if with nested ngIf/else
Pending
Shipped
Delivered
Unknown status
Angular's template syntax has no native else if keyword — nesting *ngIf/else blocks like this is the standard workaround, though for more than two or three branches, a [ngSwitch] block is generally more readable.
Basic ngFor
{{ product.name }}
ngFor with index and other local variables
{{ i + 1 }}. {{ product.name }}
(first)
ngFor iterating over an object's entries
{{ key }}: {{ settings[key] }}
objectKeys(obj: Record): string[] {
return Object.keys(obj);
}
*ngFor only iterates arrays natively — looping over a plain object's keys needs a helper method (as shown) or a custom pipe, since Angular's template syntax has no built-in equivalent of a JavaScript for...in loop for objects.
Combining ngIf and ngFor via ng-container
0">
{{ product.name }}
No products found.
Angular doesn't allow two structural directives (*ngIf and *ngFor) on the same host element — attempting it is a compile error. Wrapping the *ngFord element in an <ng-container> carrying the *ngIf is the standard fix, since ng-container doesn't render an actual DOM element, avoiding an unnecessary wrapping <div> just to hold the condition.
trackBy, for efficient list re-rendering
{{ product.name }}
trackByProductId(index: number, product: Product): number {
return product.id;
}
Without trackBy, Angular re-renders every item in the list whenever the array reference changes, even if most items are unchanged — trackBy tells Angular to track items by a stable identifier (the product's own ID) instead, so only genuinely added, removed, or changed items are actually re-rendered.