【问题标题】:How to use a typescript enum value in an Angular2 ngSwitch statement如何在 Angular2 ngSwitch 语句中使用打字稿枚举值
【发布时间】:2016-06-20 13:05:26
【问题描述】:

Typescript 枚举似乎与 Angular2 的 ngSwitch 指令自然匹配。但是当我尝试在我的组件模板中使用枚举时,我得到“无法读取...中未定义的属性'xxx'”。如何在组件模板中使用枚举值?

请注意,这与基于枚举 (ngFor) 的所有值创建 html 选择选项的方法不同。这个问题是关于 ngSwitch 基于枚举的特定值。尽管出现了创建对枚举的类内部引用的相同方法。

【问题讨论】:

  • 我不认为这些问题是重复的;另一个是询问如何根据枚举 (ngFor) 的所有值创建 HTML 选择选项,而这个是关于基于枚举的特定值的 ngSwitch。尽管出现了创建对枚举的类内部引用的相同方法。感谢您指出相似之处。

标签: typescript angular


【解决方案1】:

您可以在组件类中创建对枚举的引用(我只是将初始字符更改为小写),然后使用模板中的该引用 (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;
  }
}

【讨论】:

    【解决方案2】:

    这很简单,就像一个魅力:) 只需像这样声明你的枚举,你就可以在 HTML 模板上使用它

      statusEnum: typeof StatusEnum = StatusEnum;
    

    【讨论】:

    • 经过几天的研究终于找到了我需要的东西。非常感谢!
    • @Rahul StatusEnum.ts 类之一中定义。在您导入的 Angular 组件中,将其绑定到组件属性(此处为 statusEnum),并且可以从模板访问组件属性。
    • 坦克这太棒了
    • 像魅力一样工作。这将解决您在 HTML 文件中可能遇到的以下错误; “类型上不存在属性 xyz”。
    【解决方案3】:

    您可以创建一个自定义装饰器以添加到您的组件中,以使其了解枚举。

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

    【讨论】:

    • 有人在 AoT 编译器中使用此方法成功吗?
    • @Simon_Weaver 装饰器本质上是函数,它们将函数作为参数并扩展该函数的行为。对于 ES6/7,我们正在处理类的扩展/注释。这是high level article about how they workproposal for implementation in ES7 在 github 上——目前处于第 2 阶段。在该提案中,他们谈到了装饰器的可能用途。 TypeScript 是 JS 的超集,包含此功能。
    • @Simon_Weaver 在这种情况下,语法糖隐藏了对MyEnumAware() 的调用,其中传递了EnumAwareComponent 实例,并在其原型中添加了一个属性MyEnum。该属性的值设置为枚举本身。此方法与接受的答案相同。它只是利用了为装饰器提出并在 TypeScript 中允许的语法糖。使用 Angular 时,您立即使用装饰器语法。这就是 Component 的含义,它是 Angular 的核心类知道如何与之交互的空类的扩展。
    • -1:这似乎不适用于 aot,导致 ERROR in ng:///.../whatever.component.html (13,3): Property 'MyEnum' does not exist on type 'EnumAwareComponent'。这是有道理的,因为装饰器添加的属性从未被声明,打字稿编译器不知道它的存在。
    • 所以我已经使用了 4 个多月了。但是,现在我正在执行--prod 构建(Ionic 3 / Angular 4 / Typescript 2.4.2)它不再有效。我收到错误"TypeError: Cannot read property 'FirstValue' of undefined"。我正在使用标准数字枚举。它适用于 AoT,但不适用于 --prod。如果我将其更改为在 HTML 中使用整数,它确实有效,但这不是重点。有什么想法吗?
    【解决方案4】:

    Angular4 - 在 HTML 模板 ngSwitch / ngSwitchCase 中使用枚举

    这里的解决方案:https://stackoverflow.com/a/42464835/802196

    信用:@snorkpete

    在你的组件中,你有

    enum MyEnum{
      First,
      Second
    }
    

    然后在您的组件中,您通过成员“MyEnum”引入 Enum 类型,并为您的枚举变量“myEnumVar”创建另一个成员:

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

    您现在可以在 .html 模板中使用 myEnumVar 和 MyEnum。例如,在 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>
    

    【讨论】:

    • 如何在不同的组件中重用相同的枚举?
    • 我必须使用“export enum MyEnum{...}”在外部文件中定义枚举。然后在组件文件中,从该外部文件中导入“MyEnum”,并继续使用上面的“MyEnum = MyEnum”等解决方案。
    • 男人,你是救命恩人!这极大地增加了可读性。对于像我这样的新手 - 不要忘记 MyEnum = MyEnum;行,没有它 switch case 不起作用!
    【解决方案5】:

    截至 rc.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;
    }
    

    【讨论】:

    • 发生了什么变化?
    • 替换为 ngSwitchCase
    • 啊,好吧。谢谢!
    【解决方案6】:

    作为@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({providedIn: 'root'}) 以使其工作。谢谢!
    【解决方案7】:

    首先考虑“我真的要这样做吗?”

    我在 HTML 中直接引用枚举没有问题,但在某些情况下,有更简洁的替代方案不会失去类型安全性。 例如,如果您选择我的其他答案中显示的方法,您可能已经在组件中声明了 TT,如下所示:

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

    要在您的 HTML 中显示不同的布局,您需要为每种布局类型设置一个 *ngIf,并且您可以直接引用组件 HTML 中的枚举:

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

    此示例使用服务获取当前布局,通过异步管道运行它,然后将其与我们的枚举值进行比较。它非常冗长,令人费解,而且看起来并不有趣。它还公开了枚举的名称,它本身可能过于冗长。

    替代方案,保留 HTML 的类型安全

    或者,您可以执行以下操作,并在组件的 .ts 文件中声明更具可读性的函数:

    *ngIf="isResponsiveLayout('Horizontal')"
    

    更干净!但是如果有人错误地输入了'Horziontal' 怎么办?您想在 HTML 中使用枚举的全部原因是为了保证类型安全,对吧?

    我们仍然可以通过keyof 和一些打字稿魔法来实现这一目标。这是函数的定义:

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

    注意FeatureBoxResponsiveLayout[string]的用法,其中converts将字符串值传递给枚举的数值。

    如果您使用无效值,这将在 AOT 编译时给出错误消息。

    “H4orizo​​ntal”类型的参数不能分配给“垂直”类型的参数 | “水平”

    目前 VSCode 不够智能,无法在 HTML 编辑器中为 H4orizontal 下划线,但您会在编译时收到警告(使用 --prod build 或 --aot 开关)。这也可能在未来的更新中得到改进。

    【讨论】:

    • 不确定我是否喜欢html 中的常量,但我明白你的意思并开始使用它;它在编译时完成了工作,就像过去的美好时光一样! :)
    • @genuinefafa 这种方法实际上是将枚举本身从 html 中取出,但仍允许对枚举值进行编译检查。我想您可以说它将 html 与 ts 分离,但这本身并没有提供任何真正的好处,因为它们总是一起使用。
    • 我喜欢类型检查,特别是在非自动测试的开发中
    • 因为开头行“首先考虑'我真的想这样做吗?'”而投赞成票
    【解决方案8】:

    我的组件使用了MyClass 类型的对象myClassObject,它本身正在使用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>
    

    【讨论】:

      【解决方案9】:

      如果使用“类型表引用”方法(来自@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 的回答所示进行服务)。如果你有很多很多类型表,这可能会变得很麻烦。

      此外,您总是在声明中重命名它们以获得更短的名称。

      【讨论】:

        【解决方案10】:

        您现在可以这样做了:

        例如,枚举是:

        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”的定义绑定到该类

        【讨论】:

          猜你喜欢
          • 2020-01-29
          • 2021-02-09
          • 1970-01-01
          • 1970-01-01
          • 2022-08-03
          • 2018-09-16
          • 2020-04-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多