Ошибка миграции потока управления в Angular 18: @switch может иметь только @case или @default в качестве дочернего элемента

Я пытался перейти на формат потока управления в своих проектах Angular 18 в рабочей области nx. Большинство шаблонов работали отлично, но для одного HTML я получаю следующую ошибку:

 [ngSwitch]: Error: Text node: "
        ◬" would result in invalid migrated @switch block structure. @switch can only have @case or @default as children.

Что я сделал:

  1. беги npx nx generate @angular/core:control-flow-migration
  2. Укажите мой путь к компоненту

Вот мой полный 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>
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Введение в CSS
Введение в CSS
CSS является неотъемлемой частью трех основных составляющих front-end веб-разработки.
Как выровнять Div по центру?
Как выровнять Div по центру?
Чтобы выровнять элемент <div>по горизонтали и вертикали с помощью CSS, можно использовать комбинацию свойств и значений CSS. Вот несколько методов,...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Travel Booking Angular Template один из лучших Travel & Tour booking template in the world. 30+ валидированных HTML5 страниц, которые помогут...
1
0
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Как вы можете прочитать в сообщении об ошибке, @switch может иметь только @case или @default в качестве дочерних элементов. Однако в вашем случае во время миграции будет создано что-то вроде следующего:

@switch(type) {
  @for (...) {
    @case () {}
  }
}

Что неверно согласно приведенному выше утверждению. Angular не может автоматически решить эту проблему, поскольку это был допустимый синтаксис с [ngSwitch] и *ngSwitchCase, поэтому миграция не удалась. Чтобы это исправить, вы можете переместить *ngFor перед [ngSwitch]:

<ng-container *ngFor = "let type of buttonTypes">
  <ng-container [ngSwitch] = "type">
    <button *ngSwitchCase = "">
    <!-- ... your other content -->
  </ng-container>
</ng-container>

А затем снова запустите миграцию.

Ответ принят как подходящий

Попробуйте вручную перенести код, это скрипт, он не может обрабатывать все крайние сценарии. Мы можем перенести его, это будет выглядеть примерно так, как показано ниже.

default всегда должен быть внизу оператора переключателя, я думаю, это может быть ошибка. Обратите внимание на внесенное мною изменение, которое отличается от вашего кода: я переместил оператор случая наверх во внутреннем операторе переключения.

import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    @if (!!type; else DeprecatedStructure) {
      @switch(type) {
        @for(type of buttonTypes; track $index) {
          @case (type) {
            <button
              [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)">
                @switch(type) {
                  @case (icon) {
                    <mat-icon class = "fl-icon">{{ icon }}</mat-icon>
                    @if (loading) {
                      <span class = "loading loading-sm"></span>
                    }
                  }
                  @default {
                    <span><ng-template [ngTemplateOutlet] = "ButtonContentRef"></ng-template></span>
                    @if (icon) {
                      <mat-icon
                        class = "fl-icon"
                        [ngClass] = "{
                          right: iconPosition === 'right',
                          left: iconPosition === 'left'
                        }">
                        {{ icon }}
                      </mat-icon>
                    }
                    @if (svgIcon && !icon) {
                      <mat-icon
                        class = "fl-icon"
                        [svgIcon] = "svgIcon"
                        [ngClass] = "{
                          right: iconPosition === 'right',
                          left: iconPosition === 'left'
                        }">
                      </mat-icon>
                    }
                    @if (loading) {
                      <span class = "loading loading-sm"></span>
                    }
                  }
                }
            </button>
          }
        }
      }
    }
    <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)">
        @if (icon && iconPosition !== 'right') {
          <mat-icon class = "fl-icon" [ngClass] = "iconClassName">{{ icon }}</mat-icon>
        }
        @if (!onlyIcon) {
          <span><ng-template [ngTemplateOutlet] = "ButtonContentRef"></ng-template></span>
        }
        @if (icon && iconPosition === 'right') {
          <mat-icon class = "fl-icon right" [ngClass] = "iconClassName">{{
            icon
          }}</mat-icon>
        }
        @if (loading) {
          <span class = "loading loading-sm"></span>
        }
      </button>
    </ng-template>

    <ng-template #ButtonContentRef>
      <ng-content></ng-content>
    </ng-template>
  `,
})
export class App {
  name = 'Angular';
}

bootstrapApplication(App);

Другие вопросы по теме