I tried to migrate to control flow format in my Angular 18 projects in nx
workspace. For most of the templates worked perfectly but for one html I’m getting the following error:
[ngSwitch]: Error: Text node: "
◬" would result in invalid migrated @switch block structure. @switch can only have @case or @default as children.
What I did:
- run
npx nx generate @angular/core:control-flow-migration
- Specify my path of component
Here is my full html:
<ng-container *ngIf="!!type; else DeprecatedStructure">
<ng-container [ngSwitch]="type">
<ng-container *ngFor="let type of buttonTypes">
<button
*ngSwitchCase="type"
[style.display]="'flex'"
[style.flexDirection]="iconPosition === 'right' ? 'row' : 'row-reverse'"
[ngClass]="buttonClasses[type]"
[disabled]="disabled"
[type]="htmlType"
[id]="id"
[attr.data-test-id]="dataTestId"
(click)="handleClick($event)">
<ng-container [ngSwitch]="type">
<ng-container *ngSwitchDefault>
<span><ng-template [ngTemplateOutlet]="ButtonContentRef"></ng-template></span>
<mat-icon
*ngIf="!!icon"
class="fl-icon"
[ngClass]="{
right: iconPosition === 'right',
left: iconPosition === 'left'
}">
{{ icon }}
</mat-icon>
<mat-icon
*ngIf="!!svgIcon && !icon"
class="fl-icon"
[svgIcon]="svgIcon"
[ngClass]="{
right: iconPosition === 'right',
left: iconPosition === 'left'
}">
</mat-icon>
<span *ngIf="loading" class="loading loading-sm"></span>
</ng-container>
<ng-container *ngSwitchCase="icon">
<mat-icon class="fl-icon">{{ icon }}</mat-icon>
<span *ngIf="loading" class="loading loading-sm"></span>
</ng-container>
</ng-container>
</button>
</ng-container>
</ng-container>
</ng-container>
<ng-template #DeprecatedStructure>
<button
[class.btn]="!onlyIcon"
[class.btn-primary]="!onlyIcon && !textButton && !linkButton && !cardButton"
[class.btn-secondary]="secondary && !textButton && !linkButton && !cardButton"
[class.btn-destructive]="destructive && !textButton && !linkButton && !cardButton"
[class.btn-sm]="size === 'small'"
[class.btn-lg]="size === 'large'"
[class.btn-floating-primary]="onlyIcon"
[class.btn-floating-secondary]="onlyIcon && !!secondary"
[class.btn-floating-destructive]="onlyIcon && !!destructive"
[class.btn-flat-primary]="textButton"
[class.btn-flat-secondary]="textButton && secondary"
[class.btn-flat-destructive]="textButton && destructive"
[class.btn-plain-primary]="linkButton"
[class.btn-plain-secondary]="linkButton && secondary"
[class.btn-plain-destructive]="linkButton && destructive"
[class.btn-card]="cardButton"
[class.btn-loading]="loading"
[ngClass]="className"
[disabled]="!!disabled"
[type]="htmlType"
[id]="id"
[attr.data-test-id]="dataTestId"
(click)="handleClick($event)">
<mat-icon *ngIf="icon && iconPosition !== 'right'" class="fl-icon" [ngClass]="iconClassName">{{ icon }}</mat-icon>
<span *ngIf="!onlyIcon"><ng-template [ngTemplateOutlet]="ButtonContentRef"></ng-template></span>
<mat-icon *ngIf="icon && iconPosition === 'right'" class="fl-icon right" [ngClass]="iconClassName">{{
icon
}}</mat-icon>
<span *ngIf="loading" class="loading loading-sm"></span>
</button>
</ng-template>
<ng-template #ButtonContentRef>
<ng-content></ng-content>
</ng-template>
2
Answers
As you can read in the error message,
@switch
can only have@case
or@default
as children. However in your case something like the following would be generated during migration:Which is invalid as per the above statement. Angular cannot automatically fix this issue, as it was legal syntax with
[ngSwitch]
and*ngSwitchCase
, that’s why the migration fails. To fix it, you can move the*ngFor
before the[ngSwitch]
:And afterwards run the migration again.
Try manually migrating the code, its a script, it can’t handle all edge case scenarios, we can migrate it, it would look something like below.
default should always be at the bottom of the switch statement, I think it might be a bug, please notice this change I made which deviates from your code, I promoted the case statement to the top in the inner switch statement.