Как использовать значение перечисления машинописи в выражении 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».