Reader Stacks

*ngIf and *ngFor in Angular: Conditionals, Loops, and Combining Both

Angular doesn't allow *ngIf and *ngFor on the same element directly — a wrapping ng-container is the standard workaround, avoiding an extra real DOM element just to hold the second directive.

*ngIf and *ngFor in Angular: Conditionals, Loops, and Combining Both

*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

    
        
  • {{ 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.