Angular2 ngSwitchステートメントでtypescript enum値を使用する方法


158

Typescript列挙型は、Angular2のngSwitchディレクティブと自然に一致するようです。しかし、コンポーネントのテンプレートで列挙型を使用しようとすると、「undefined in ...のプロパティ 'xxx'を読み取れません」と表示されます。コンポーネントテンプレートで列挙値を使用するにはどうすればよいですか?

これは、列挙型(ngFor)のすべての値に基づいてHTML選択オプションを作成する方法とは異なることに注意してください。この質問は、列挙型の特定の値に基づくngSwitchに関するものです。列挙型へのクラス内部参照を作成する同じアプローチが表示されますが。



1
これらの質問は重複しているとは思いません。もう1つは、列挙型のすべての値(ngFor)に基づいてHTML選択オプションを作成する方法を尋ねていますが、これは列挙型の特定の値に基づいてngSwitchに関するものです。列挙型へのクラス内部参照を作成する同じアプローチが表示されますが。類似点を指摘していただきありがとうございます。
Carl G

回答:


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

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

7
AoTコンパイラでこのメソッドを使用して成功した人はいますか?
ダニー

2
@Simon_Weaver デコレーターは、基本的には関数をパラメーターとして取り、その関数の動作を拡張する関数です。ES6 / 7の場合、クラスの拡張/注釈を扱っています。ここでは、それらがどのように機能するかについての高レベルの記事を示しますES7での実装提案はgithubにあります-現在ステージ2です。その提案では、デコレーターの可能な使用法に触れています。JSのスーパーセットであるTypeScriptには、この機能が含まれています。
エリックリース

2
@Simon_Weaverこの場合、構文糖衣はMyEnumAware()EnumAwareComponentインスタンスが渡されるへの呼び出しを隠しておりMyEnum、プロトタイプにプロパティが追加されています。プロパティの値は列挙型自体に設定されます。このメソッドは、受け入れられた回答と同じことを行います。これは、デコレーターに提案され、TypeScriptで許可されている構文糖を利用しているだけです。Angularを使用するときは、デコレーター構文をすぐに使用しています。それがa Component とは何か、Angularのコアクラスが相互作用する方法を知っている空のクラスの拡張です。
エリックリース

5
-1:これはaotでは機能しないようで、結果としてになりERROR in ng:///.../whatever.component.html (13,3): Property 'MyEnum' does not exist on type 'EnumAwareComponent'ます。これは理にかなっています。デコレータが追加するプロパティは宣言されておらず、typescriptコンパイラがその存在を認識しないためです。
メリトン2017

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クラスの1つで定義されています。Angularコンポーネントをインポートして、コンポーネントプロパティ(ここではstatusEnum)にバインドすると、テンプレートからコンポーネントプロパティにアクセスできます。
トム

これは素晴らしい戦車です
HSN KH

45

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>

同じ列挙型を別のコンポーネントでどのように再利用できますか?
ForestG、

1
「exum enum MyEnum {...}」を使用して、外部ファイルでenumを定義する必要がありました。その外部ファイルからコンポーネントファイル、インポート「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
変化したこと?
Carl G

ngSwitchCaseに置き換えられました
born2net

あ、そう。ありがとう!
Carl G

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({providedIn: 'root'})を記述して機能させる必要もありました。ありがとう!
Stalli

2

「私は本当に私は本当にこれをしたいですか?」

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といくつかのtypescriptマジックを使用すれば、それを実現できます。これは関数の定義です:

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

FeatureBoxResponsiveLayout[string]which の使用法は、渡された文字列値を列挙型の数値に変換することに注意してください。

無効な値を使用すると、AOTコンパイルでエラーメッセージが表示されます。

タイプ '"H4orizo​​ntal' 'の引数は、タイプ'" Vertical "のパラメーターに割り当てることができません| 「水平」

現在、VSCodeはH4orizontalHTMLエディターで下線を引くほどスマートではありませんが、コンパイル時に警告が表示されます(--prod buildまたは--aotスイッチを使用)。これは、将来のアップデートで改善される可能性もあります。


内部の定数htmlが好きかどうかはわかりませんが、あなたのポイントがわかり、使い始めました。それは、古き良き時代のように、コンパイル時に機能します!:)
本物のfafa

@genuinefafaこのアプローチは、HTMLから列挙型自体を取得することですが、列挙型の値をコンパイルチェックすることもできます。htmlとtsを分離していると言えるかもしれませんが、HTMLは常に一緒に使用されるため、それ自体は実際のメリットはありません。
Simon_Weaver

特に自動テストされていない開発では、タイプチェックが好きです
本物fafa

なぜなら、「『?ド私は本当にこれをしたい』考慮してスタート」ラインを開くupvote
WebDever

2

私のコンポーネントmyClassObjectMyClass、それ自体が使用していたタイプのオブジェクトを使用しました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

'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の回答で示されているようにサービスを作成することもできます)。多くのタイプテーブルがある場合、これは面倒になるかもしれません。

また、短い名前を取得するために、宣言でそれらの名前を常に変更します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.