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

159

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

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

Карл Дж
источник
Возможный дубликат Select на основе enum в Angular2
Гюнтер Цохбауэр
1
Я не думаю, что эти вопросы являются дубликатами; другой спрашивает, как создать опции выбора HTML, основанные на ВСЕХ значениях перечисления (ngFor), тогда как этот - о ngSwitch, основанном на определенном значении перечисления. Хотя тот же самый подход создания внутренней ссылки на перечисление появляется. Спасибо за указание на сходство.
Карл Дж

Ответы:

166

Вы можете создать ссылку на перечисление в вашем классе компонентов (я только что изменил начальный символ на нижний регистр), а затем использовать эту ссылку из шаблона ( 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;
  }
}
Карл Дж
источник
88

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

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;
  }
}
Эрик Лиз
источник
7
Кто-нибудь имел успех, используя этот метод с компилятором AoT?
Дэнни
2
Декораторы @Simon_Weaver - это, по сути, функции, которые принимают функцию в качестве параметра и расширяют поведение этой функции. В случае ES6 / 7 мы имеем дело с расширением / аннотацией классов. Вот статья высокого уровня о том, как они работают . Предложение по реализации в ES7 на GitHub - в настоящее время в стадии 2. В этом предложении, они касаются возможного использования для декораторов. TypeScript, являющийся надмножеством JS, включает эту функцию.
Эрик Лиз
2
@Simon_Weaver В этом случае синтаксический сахар скрывает вызов MyEnumAware(), в EnumAwareComponentкоторый передается экземпляр, и имеет свойство MyEnum, добавленное к его прототипу. Значение свойства устанавливается самим перечислением. Этот метод делает то же самое, что и принятый ответ. Это просто использование синтаксического сахара, предложенного для декораторов и разрешенного в TypeScript. При использовании Angular вы сразу используете синтаксис декоратора. Вот что Component такое расширение пустого класса, с которым базовые классы Angular знают, как взаимодействовать.
Эрик Лиз
5
-1: Это не похоже на работу с aot, в результате чего ERROR in ng:///.../whatever.component.html (13,3): Property 'MyEnum' does not exist on type 'EnumAwareComponent'. Это имеет смысл, поскольку свойство, добавляемое декоратором, никогда не объявляется, поэтому компилятор машинописного текста не знает о его существовании.
меритон
2
Так что я использую это в течение 4 + месяцев. Однако теперь, когда я делаю --prodсборку (Ionic 3 / Angular 4 / Typescript 2.4.2), она больше не работает. Я получаю ошибку "TypeError: Cannot read property 'FirstValue' of undefined". Я использую стандартное числовое перечисление. Он отлично работает с AoT, но не с --prod. Это работает, если я перехожу на использование целых чисел в HTML, но это не главное. Любые идеи?
Расс
47

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

  statusEnum: typeof StatusEnum = StatusEnum;
Аймен Бумаиза
источник
После дней исследований наконец то нашел то что мне нужно. Большое спасибо!
Гсирадзе
@Rahul StatusEnumопределен в одном из .tsклассов. В компоненте Angular вы импортируете его, привязываете его к свойству компонента (здесь statusEnum), и свойства компонента доступны из шаблона.
Том
танки это здорово
Хасан Хадеми
45

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

Решение здесь: https://stackoverflow.com/a/42464835/802196

кредит: @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>
ObjectiveTC
источник
Как вы можете использовать один и тот же enum в другом компоненте?
ForestG
1
Я должен был определить перечисление во внешнем файле, используя «экспортировать перечисление MyEnum {...}». Затем в файле компонента импортируйте «MyEnum» из этого внешнего файла и продолжите решение выше для «MyEnum = MyEnum» и т. Д.
ObjectiveTC
16

по состоянию на 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;
}
born2net
источник
1
Что изменилось?
Карл Дж
заменен на ngSwitchCase
born2net
Ах хорошо. Спасибо!
Карл Ги
14

В качестве альтернативы декоратору @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>
Винсент Селс
источник
Мне также нужно было поменять сервис и написать @Injectable ({обеспечено: root), чтобы оно работало. Спасибо!
Стали
2

Начните с рассмотрения " Должен ли я на самом деле хочу , чтобы это сделать?

У меня нет проблем со ссылками на перечисления непосредственно в 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). Это также может быть улучшено в будущем обновлении.

Simon_Weaver
источник
не уверен, что мне нравятся константы внутри, htmlно я понял вашу точку зрения и начал ее использовать; это делает работу, как старые добрые времена, при компиляции! :)
Genuinefafa
@genuinefafa этот подход на самом деле заключается в извлечении самого перечисления из html, но все же позволяет проверять значения перечисления при компиляции. Я полагаю, вы могли бы сказать, что он отделяет HTML от TS, но это само по себе не дает никаких реальных преимуществ, потому что они всегда используются вместе.
Simon_Weaver
мне нравится проверка типов, особенно в неавтоматически проверенных разработках
Genuinefafa
upvote из-за открытия строки «Начните с рассмотрения« Действительно ли я хочу это сделать? »»
WebDever
2

Мой компонент использовал объект 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>
Heribert
источник
1

Если вы используете подход '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). Если у вас много разных таблиц, это может стать громоздким.

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

Simon_Weaver
источник