Which @angular/* package(s) are the source of the bug?
core
Is this a regression?
Yes
Description
The control flow migration incorrectly interpreting an else template reference whose name begins with then
This causes the migration to:
- take the then + else migration path instead of the else path,
- drop the original contents of the *ngIf host,
- generate an invalid placeholder that later becomes:
<ng-template [ngTemplateOutlet]=""></ng-template>
The migration completes without any warnings, resulting in silent content loss.
Reproduction:
Input:
<ng-container *ngIf="data.teamMember; else thenBlock">
<h2>Hello team member!</h2>
</ng-container>
<ng-template #thenBlock>
<h2>No team member</h2>
</ng-template>
Run the control flow migration.
Expected behavior
The migration should produce:
@if (data.teamMember) {
<h2>Hello team member!</h2>
} @else {
<h2>No team member</h2>
}
Actual behavior:
The @else branch is migrated correctly, but the @if branch loses its original content and is replaced with an empty template outlet similar to:
@if (data.teamMember) {
<ng-template [ngTemplateOutlet]=""></ng-template>
} @else {
<h2>No team member</h2>
}
Root cause:
The migration determines whether an *ngIf uses then/else by regex matching the raw microsyntax string.
In ifs.ts:
const matchThen = etm.attr.value.match(/[^\w\d];?\s*then/gm);
const matchElse = etm.attr.value.match(/[^\w\d];?\s*else/gm);
the regex matches:
- the real else keyword
- the "then" prefix of the template reference thenBlock
As a result, the migration incorrectly assumes both then and else are present and follows the then + else migration path.
Later, getTemplateName() attempts to extract the template names using these match locations. Since the "then" occurs after the real else, it ends up calling slice(start, end) with start > end, producing an empty template name.
This empty placeholder is never resolved and is ultimately emitted as:
<ng-template [ngTemplateOutlet]=""></ng-template>
Please provide a link to a minimal reproduction of the bug
No response
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run ng version)
Angular CLI: 20.3.32
Node: 22.21.0
Package manager: npm 10.9.4
Anything else?
No response
Which @angular/* package(s) are the source of the bug?
core
Is this a regression?
Yes
Description
The control flow migration incorrectly interpreting an else template reference whose name begins with
thenThis causes the migration to:
The migration completes without any warnings, resulting in silent content loss.
Reproduction:
Input:
Run the control flow migration.
Expected behavior
The migration should produce:
Actual behavior:
The
@elsebranch is migrated correctly, but the@ifbranch loses its original content and is replaced with an empty template outlet similar to:Root cause:
The migration determines whether an *ngIf uses then/else by regex matching the raw microsyntax string.
In
ifs.ts:the regex matches:
As a result, the migration incorrectly assumes both then and else are present and follows the then + else migration path.
Later, getTemplateName() attempts to extract the template names using these match locations. Since the "then" occurs after the real else, it ends up calling slice(start, end) with start > end, producing an empty template name.
This empty placeholder is never resolved and is ultimately emitted as:
Please provide a link to a minimal reproduction of the bug
No response
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run
ng version)Anything else?
No response