Как использовать значение перечисления машинописи в выражении Angular2 ngSwitch

Перечисление Typescript кажется естественным совпадением с директивой Angular2 ngSwitch. Но когда я пытаюсь использовать enum в шаблоне моего компонента, я получаю "Не удается прочитать свойство" xxx "из undefined in...". Как я могу использовать значения enum в шаблоне моего компонента?

Обратите внимание, что это отличается от того, как создавать опции выбора html на основе ВСЕХ значений перечисления (ngFor). Этот вопрос о ngSwitch, основанном на определенном значении перечисления. Хотя тот же самый подход создания внутренней ссылки на перечисление появляется.

10 ответов

Решение

Вы можете создать ссылку на перечисление в вашем классе компонентов (я только что изменил начальный символ на нижний регистр), а затем использовать эту ссылку из шаблона ( plunker):

import {Component} from 'angular2/core';

enum CellType {Text, Placeholder}
class Cell {
  constructor(public text: string, public type: CellType) {}
}
@Component({
  selector: 'my-app',
  template: `
    <div [ngSwitch]="cell.type">
      <div *ngSwitchCase="cellType.Text">
        {{cell.text}}
      </div>
      <div *ngSwitchCase="cellType.Placeholder">
        Placeholder
      </div>
    </div>
    <button (click)="setType(cellType.Text)">Text</button>
    <button (click)="setType(cellType.Placeholder)">Placeholder</button>
  `,
})
export default class AppComponent {

  // Store a reference to the enum
  cellType = CellType;
  public cell: Cell;

  constructor() {
    this.cell = new Cell("Hello", CellType.Text)
  }

  setType(type: CellType) {
    this.cell.type = type;
  }
}

Это просто и работает как шарм:) просто объявите свой enum вот так, и вы можете использовать его в шаблоне HTML

  statusEnum: typeof SatusEnum = SatusEnum;

Вы можете создать собственный декоратор для добавления в ваш компонент, чтобы он знал о перечислениях.

myenum.enum.ts:

export enum MyEnum {
    FirstValue,
    SecondValue
}

myenumaware.decorator.ts

import { MyEnum } from './myenum.enum';

export function MyEnumAware(constructor: Function) {
    constructor.prototype.MyEnum = MyEnum;
}

перечисляемые-aware.component.ts

import { Component } from '@angular2/core';
import { MyEnum } from './myenum.enum';
import { MyEnumAware } from './myenumaware.decorator';

@Component({
  selector: 'enum-aware',
  template: `
    <div [ngSwitch]="myEnumValue">
      <div *ngSwitchCase="MyEnum.FirstValue">
        First Value
      </div>
      <div *ngSwitchCase="MyEnum.SecondValue">
        Second Value
      </div>
    </div>
    <button (click)="toggleValue()">Toggle Value</button>
  `,
})
@MyEnumAware // <---------------!!!
export default class EnumAwareComponent {
  myEnumValue: MyEnum = MyEnum.FirstValue;

  toggleValue() {
    this.myEnumValue = this.myEnumValue === MyEnum.FirstValue
        ? MyEnum.SecondValue : MyEnum.FirstValue;
  }
}

Angular4 - Использование Enum в шаблоне HTML ngSwitch / ngSwitchCase

Решение здесь: /questions/42788760/angular2-ispolzovat-znachenie-perechisleniya-v-atribute-znacheniya-html/42788773#42788773

кредит: @snorkpete

В вашем компоненте у вас есть

enum MyEnum{
  First,
  Second
}

Затем в вашем компоненте вы вводите тип Enum через член MyEnum и создаете другого члена для вашей переменной enum myEnumVar:

export class MyComponent{
  MyEnum = MyEnum;
  myEnumVar:MyEnum = MyEnum.Second
  ...
}

Теперь вы можете использовать myEnumVar и MyEnum в своем.html шаблоне. Например, используя Enums в ngSwitch:

<div [ngSwitch]="myEnumVar">
  <div *ngSwitchCase="MyEnum.First"><app-first-component></app-first-component></div>
  <div *ngSwitchCase="MyEnum.Second"><app-second-component></app-second-component></div>
  <div *ngSwitchDefault>MyEnumVar {{myEnumVar}} is not handled.</div>
</div>

В качестве альтернативы декоратору @Eric Lease, который, к сожалению, не работает, используя --aot (и поэтому --prod), я прибег к использованию сервиса, который предоставляет все перечисления моего приложения. Просто нужно публично внедрить это в каждый компонент, который требует его, под простым именем, после чего вы можете получить доступ к перечислениям в ваших представлениях. Например:

обслуживание

import { Injectable } from '@angular/core';
import { MyEnumType } from './app.enums';

@Injectable()
export class EnumsService {
  MyEnumType = MyEnumType;
  // ...
}

Не забудьте включить его в список поставщиков вашего модуля.

Класс компонента

export class MyComponent {
  constructor(public enums: EnumsService) {}
  @Input() public someProperty: MyEnumType;

  // ...
}

Компонент HTML

<div *ngIf="someProperty === enums.MyEnumType.SomeValue">Match!</div>

По состоянию на 6 декабря / окончательный

...

export enum AdnetNetworkPropSelector {
    CONTENT,
    PACKAGE,
    RESOURCE
}

<div style="height: 100%">
          <div [ngSwitch]="propSelector">
                 <div *ngSwitchCase="adnetNetworkPropSelector.CONTENT">
                      <AdnetNetworkPackageContentProps [setAdnetContentModels]="adnetNetworkPackageContent.selectedAdnetContentModel">
                                    </AdnetNetworkPackageContentProps>
                  </div>
                 <div *ngSwitchCase="adnetNetworkPropSelector.PACKAGE">
                </div>
            </div>              
        </div>


export class AdnetNetwork {       
    private adnetNetworkPropSelector = AdnetNetworkPropSelector;
    private propSelector = AdnetNetworkPropSelector.CONTENT;
}

Начните с вопроса "Действительно ли я хочу это сделать?"

У меня нет проблем со ссылками на перечисления непосредственно в HTML, но в некоторых случаях есть более чистые альтернативы, которые не теряют безопасность типов. Например, если вы выберете подход, показанный в моем другом ответе, возможно, вы объявили TT в своем компоненте примерно так:

public TT = 
{
    // Enum defines (Horizontal | Vertical)
    FeatureBoxResponsiveLayout: FeatureBoxResponsiveLayout   
}

Чтобы показать другой макет в вашем HTML, вы должны иметь *ngIf для каждого типа макета, и вы можете обратиться непосредственно к перечислению в HTML вашего компонента:

*ngIf="(featureBoxResponsiveService.layout | async) == TT.FeatureBoxResponsiveLayout.Horizontal"

В этом примере используется сервис для получения текущего макета, запуска его через асинхронный канал и сравнения с нашим значением перечисления. Это довольно многословно, запутанно и не очень интересно смотреть. Это также выставляет имя enum, которое само по себе может быть слишком многословным.

Альтернатива, которая сохраняет безопасность типов из HTML

В качестве альтернативы вы можете сделать следующее и объявить более читаемую функцию в файле.ts вашего компонента:

*ngIf="isResponsiveLayout('Horizontal')"

Гораздо чище! Но что, если кто-то наберет 'Horziontal' по ошибке? Вся причина, по которой вы хотели использовать enum в HTML, была в том, чтобы быть безопасным, не так ли?

Мы все еще можем достичь этого с помощью keyof и некоторой магии машинописи. Это определение функции:

isResponsiveLayout(value: keyof typeof FeatureBoxResponsiveLayout)
{
    return FeatureBoxResponsiveLayout[value] == this.featureBoxResponsiveService.layout.value;
}

Обратите внимание на использование FeatureBoxResponsiveLayout[string] который преобразует передаваемое строковое значение в числовое значение перечисления.

Это даст сообщение об ошибке с компиляцией AOT, если вы используете недопустимое значение.

Аргумент типа "H4orizontal" не может быть присвоен параметру типа "Vertical" | "Горизонтальный"

В настоящее время VSCode недостаточно умен, чтобы подчеркнуть H4orizontal в редакторе HTML, но вы получите предупреждение во время компиляции (с ключом --prod build или --aot). Это также может быть улучшено в будущем обновлении.

Мой компонент использовал объект myClassObject типа MyClass, который сам использовал MyEnum. Это приводит к той же проблеме, которая описана выше. Решил это, выполнив:

export enum MyEnum {
    Option1,
    Option2,
    Option3
}
export class MyClass {
    myEnum: typeof MyEnum;
    myEnumField: MyEnum;
    someOtherField: string;
}

а затем используя это в шаблоне как

<div [ngSwitch]="myClassObject.myEnumField">
  <div *ngSwitchCase="myClassObject.myEnum.Option1">
    Do something for Option1
  </div>
  <div *ngSwitchCase="myClassObject.myEnum.Option2">
    Do something for Option2
  </div>
  <div *ngSwitchCase="myClassObject.myEnum.Option3">
    Do something for Opiton3
  </div>
</div>

Если вы используете подход 'typetable reference' (из @Carl G) и вы используете несколько таблиц типов, вы можете рассмотреть этот способ:

export default class AppComponent {

  // Store a reference to the enums (must be public for --AOT to work)
  public TT = { 
       CellType: CellType, 
       CatType: CatType, 
       DogType: DogType 
  };

  ...

  dog = DogType.GoldenRetriever; 

Затем доступ в ваш HTML-файл с

{{ TT.DogType[dog] }}   => "GoldenRetriever"

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

Вы также можете поставить глобальный TT где-нибудь и добавьте перечисления к нему по мере необходимости (если вы хотите это, вы можете также сделать сервис, как показано в ответе @VincentSels). Если у вас много разных таблиц, это может стать громоздким.

Также вы всегда переименовываете их в своем объявлении, чтобы получить более короткое имя.

Теперь вы можете сделать это:

например, перечисление:

      export enum MessagePriority {
    REGULAR= 1,
    WARNING,
    IMPORTANT,
}

сообщение о состоянии, которое выглядит следующим образом:

      export default class StatusMessage{
    message: string;
    priority: MessagePriority;

    constructor(message: string, priority: MessagePriority){
        this.message = message;
        this.priority = priority;
    }
}

то в .ts файле компонента можно сделать так:

          import StatusMessage from '../../src/entities/building/ranch/administration/statusMessage';
    import { MessagePriority } from '../../enums/message-priority';
            
    export class InfoCardComponent implements OnInit {
     messagePriority: typeof MessagePriority;
                
     constructor() { 
     this.messagePriority = MessagePriority;
    }
                
    @Input() statusMessage: StatusMessage;
    ngOnInit(): void {}
}

и, наконец, HTML компонента выглядит так:

      <div class="info-card" [ngSwitch]="statusMessage.priority">
    <h2 *ngSwitchCase="this.messagePriority.REGULAR" class="info-card__regular-message">{{statusMessage.message}}</h2>
    <h2 *ngSwitchCase="this.messagePriority.WARNING" class="info-card__warning-message">{{statusMessage.message}}</h2>
    <h2 *ngSwitchCase="this.messagePriority.IMPORTANT" class="info-card__important-message">{{statusMessage.message}}</h2>
</div>

Обратите внимание, что перечисление сначала объявляется в классе с типом «typeof MessagePriority», а затем привязывается к классу, вызывая определение с «this.messagePriority= MessagePriority».

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