Як використовувати значення enum typecript в операторі Angular2 ngSwitch


158

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

Зверніть увагу, що це відрізняється від способів створення параметрів вибору html на основі ВСІХ значень enum (ngFor). Це питання стосується ngSwitch, заснованого на певному значенні перерахунку. Хоча з'являється той самий підхід створення внутрішнього посилання класу на enum.



1
Я не думаю, що ці запитання є дублікатами; інший запитує, як створити параметри вибору HTML, засновані на ВСІХ значеннях enum (ngFor), тоді як цей - про ngSwitch, що базується на певному значенні enum. Хоча з'являється той самий підхід створення внутрішнього посилання класу на enum. Дякую, що вказали на подібність.
Карл Г

Відповіді:


166

Ви можете створити посилання на enum у вашому класі компонентів (я просто змінив початковий символ на малі регістри ), а потім використати це посилання з шаблону ( 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;
}

enum -now.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'. Це має сенс, оскільки властивість, яку додає декоратор, ніколи не декларується, залишаючи компілятор машинопису не обізнаним про його існування.
meriton

2
Тому я використовую це протягом 4+ місяців. Однак тепер, коли я роблю --prodзбірку (Ionic 3 / Angular 4 / Typescript 2.4.2), вона більше не працює. Я отримую помилку "TypeError: Cannot read property 'FirstValue' of undefined". Я використовую стандартний числовий перелік. Це добре працює з AoT, але не з --prod. Це спрацьовує, якщо я зміню його на використання цілих чисел у HTML, але це не в цьому суть. Будь-які ідеї?
Russ

47

Це просто і працює як шарм :) просто заявіть про свій перелік таким чином, і ви можете використовувати його в HTML-шаблоні

  statusEnum: typeof StatusEnum = StatusEnum;

Після днів досліджень нарешті знайшли те, що мені потрібно. Велике дякую!
gsiradze

@Rahul StatusEnumвизначено в одному з .tsкласів. У компоненті Angular ви імпортуєте його, прив'яжіть його до властивості компонента (тут statusEnum), а властивості компонента доступні з шаблону.
Том

танки це чудово
hassan khademi

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>

як можна повторно використовувати один і той же перелік в іншому компоненті?
ForestG

1
Мені довелося визначити enum у зовнішньому файлі, використовуючи "export enum MyEnum {...}". Потім імпортуйте у файл компонентів «MyEnum» з цього зовнішнього файлу та продовжуйте рішення з вищевказаного тексту для «MyEnum = MyEnum» тощо.
ObjectiveTC

16

станом на rc.6 / final

...

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;
}

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 ({predviIn: '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, полягала в тому, щоб набрати безпечніше правильно?

Ми все ще можемо досягти цього за допомогою маніпуляції з клавіатурою та деякими машинописними магіями . Це визначення функції:

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

Зауважте, використання FeatureBoxResponsiveLayout[string]якого перетворює передане значення рядка в числове значення перерахунку.

Це дасть повідомлення про помилку при компіляції AOT, якщо ви використовуєте недійсне значення.

Аргумент типу "" H4orizontal "" не призначається параметру типу "" Вертикальний "| "Горизонтальний"

В даний час VSCode недостатньо розумний, щоб підкреслити його H4orizontalв редакторі HTML, але ви отримаєте попередження під час компіляції (за допомогою --prod build або --aot перемикача). Це також може бути покращено в майбутньому оновлення.


не впевнений, чи мені подобаються константи всередині, htmlале я бачу вашу точку і почав її використовувати; він робить роботу, як старі добрі часи, коли збираєш! :)
truefafa

@genuinefafa цей підхід дійсно полягає у виведенні самого enum з html, але все ж дозволяє перевірити значення enum, щоб перевірити. Я гадаю, ви можете сказати, що він від’єднує html від ts, але сам по собі не дає реальних переваг, оскільки вони завжди використовуються разом.
Simon_Weaver

Мені подобається перевірка типу, особливо в неавтоматизованому тестуванні
truefafa

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>

1

Якщо ви використовуєте підхід "посилання на тип" (від @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). Якщо у вас багато багато друкованих текстів, це може стати громіздким.

Також ви завжди перейменовуєте їх у своїй декларації, щоб отримати коротше ім’я.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.