How to set ngFor and ngIf on the same element?
In Angular, placing both *ngFor and *ngIf on the same HTML element is invalid and throws a compile-time template error:
Template parse errors: Can't have multiple template bindings on one element. Use only one attribute prefixed with *
This error occurs because Angular expands asterisk (*) syntax into an <ng-template> wrapper. Having two asterisks on a single tag creates an ambiguous nesting hierarchy for the template compiler.
1. Classic Solution: Using <ng-container>
In classic Angular, you resolve this by placing one of the structural directives on an <ng-container> wrapper. The <ng-container> is a logical grouping element that Angular does not render into the final DOM:
<!-- Scenario A: Render the list only if items array exists -->
<ng-container *ngIf="users && users.length > 0; else noUsersTemplate">
<ul>
<li *ngFor="let user of users; trackBy: trackById">
{{ user.name }}
</li>
</ul>
</ng-container>
<ng-template #noUsersTemplate>
<p>No users registered.</p>
</ng-template>
<!-- Scenario B: Filter items inside the loop (only render active users) -->
<ul>
<ng-container *ngFor="let user of users; trackBy: trackById">
<li *ngIf="user.isActive">
{{ user.name }}
</li>
</ng-container>
</ul>
2. Modern Solution: Built-in Control Flow (@if and @for) (Angular 17+)
In modern Angular (v17+), built-in control flow completely eliminates the multiple-structural-directive conflict because @if and @for are native template blocks rather than directive attributes:
<!-- Clean, natural nesting with zero dummy containers -->
@if (users && users.length > 0) {
<ul>
@for (user of users; track user.id) {
@if (user.isActive) {
<li>{{ user.name }}</li>
}
}
</ul>
} @else {
<p>No users registered.</p>
}
3. Best Practice Architectural Recommendation
[!TIP] Filtering in Templates vs. Component Signals: While nesting
@ifinside@forworks, filtering large collections directly in the template can hurt performance. Instead, use a computed Signal in your component to pre-filter items:typescriptactiveUsers = computed(() => this.users().filter(u => u.isActive));In template:
html@for (user of activeUsers(); track user.id) { <li>{{ user.name }}</li> }