Cara menggunakan nilai skrip enum dalam pernyataan Angular2 ngSwitch

158

Encript Script tampaknya cocok alami dengan arahan ngSwitch Angular2. Tetapi ketika saya mencoba menggunakan enum dalam templat komponen saya, saya mendapatkan "Tidak dapat membaca properti 'xxx' dari yang tidak terdefinisi di ...". Bagaimana saya bisa menggunakan nilai enum dalam templat komponen saya?

Harap dicatat bahwa ini berbeda dari cara membuat opsi pilih html berdasarkan SEMUA nilai enum (ngFor). Pertanyaan ini adalah tentang ngSwitch berdasarkan nilai enum tertentu. Meskipun pendekatan yang sama untuk membuat referensi kelas-internal ke enum muncul.

Carl G
sumber
Kemungkinan duplikat Select berdasarkan enum di Angular2
Günter Zöchbauer
1
Saya tidak berpikir bahwa pertanyaan ini adalah duplikat; yang lain bertanya bagaimana cara membuat opsi pilih HTML berdasarkan SEMUA nilai enum (ngFor), sedangkan yang ini adalah tentang ngSwitch berdasarkan nilai enum tertentu. Meskipun pendekatan yang sama untuk membuat referensi kelas-internal ke enum muncul. Terima kasih telah menunjukkan kesamaannya.
Carl G

Jawaban:

166

Anda dapat membuat referensi ke enum di kelas komponen Anda (saya baru saja mengubah karakter awal menjadi huruf kecil) dan kemudian menggunakan referensi itu dari templat ( 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;
  }
}
Carl G
sumber
88

Anda dapat membuat dekorator khusus untuk ditambahkan ke komponen Anda agar membuatnya sadar akan enum.

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;
  }
}
Eric Lease
sumber
7
Adakah yang berhasil menggunakan metode ini dengan kompiler AoT?
Danny
2
@Simon_Weaver dekorator pada dasarnya adalah fungsi yang mengambil fungsi sebagai parameter dan memperluas perilaku fungsi itu. Dalam kasus ES6 / 7, kita sedang berhadapan dengan ekstensi / anotasi kelas. Inilah artikel tingkat tinggi tentang cara kerjanya . The proposal penerapan di ES7 adalah pada github - sedang dalam tahap 2. Dalam proposal itu, mereka menyentuh pada kemungkinan penggunaan untuk dekorator. TypeScript, menjadi superset dari JS, termasuk fitur ini.
Eric Lease
2
@Simon_Weaver Dalam hal ini, gula sintaksis menyembunyikan panggilan ke MyEnumAware(), di mana EnumAwareComponentinstance dilewatkan, dan memiliki properti MyEnum, ditambahkan ke prototipe. Nilai properti diatur enum itu sendiri. Metode ini melakukan hal yang sama dengan jawaban yang diterima. Ini hanya mengambil keuntungan dari gula sintaksis yang diusulkan untuk dekorator dan diizinkan dalam TypeScript. Saat menggunakan Angular, Anda menggunakan sintaksis dekorator langsung. Itulah apa Component itu , ekstensi dari kelas kosong yang kelas inti Angular tahu bagaimana berinteraksi.
Eric Lease
5
-1: Ini sepertinya tidak berfungsi dengan aot, menghasilkan ERROR in ng:///.../whatever.component.html (13,3): Property 'MyEnum' does not exist on type 'EnumAwareComponent'. Ini masuk akal, karena properti yang ditambahkan oleh dekorator tidak pernah dideklarasikan, membuat kompiler naskah tidak mengetahui keberadaannya.
meriton
2
Jadi saya sudah menggunakan ini selama 4+ bulan. Namun, sekarang saya melakukan --prodbuild (Ionic 3 / Angular 4 / Typescript 2.4.2) tidak lagi berfungsi. Saya mendapatkan kesalahan "TypeError: Cannot read property 'FirstValue' of undefined". Saya menggunakan enum numerik standar. Ini berfungsi baik dengan AoT tetapi tidak dengan --prod. Itu berfungsi jika saya mengubahnya menggunakan integer dalam HTML, tapi bukan itu intinya. Ada ide?
Russ
47

Ini sederhana dan berfungsi seperti pesona :) cukup nyatakan enum Anda seperti ini dan Anda dapat menggunakannya pada templat HTML

  statusEnum: typeof StatusEnum = StatusEnum;
Aymen Boumaiza
sumber
Setelah hari penelitian akhirnya menemukan apa yang saya butuhkan. Terimakasih banyak!
gsiradze
@Rahul StatusEnumdidefinisikan di salah satu .tskelas. Di komponen Angular yang Anda impor, ikat ke properti komponen (di sini statusEnum) dan properti komponen dapat diakses dari templat.
tom
tank ini hebat
HSN KH
45

Angular4 - Menggunakan Enum di Template HTML ngSwitch / ngSwitchCase

Solusi di sini: https://stackoverflow.com/a/42464835/802196

kredit: @snorkpete

Di komponen Anda, Anda punya

enum MyEnum{
  First,
  Second
}

Kemudian di komponen Anda, Anda membawa tipe Enum melalui anggota 'MyEnum', dan membuat anggota lain untuk variabel enum Anda 'myEnumVar':

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

Sekarang Anda dapat menggunakan myEnumVar dan MyEnum di templat .html Anda. Misalnya, Menggunakan Enums di 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
sumber
bagaimana Anda dapat menggunakan kembali enum yang sama dalam komponen yang berbeda?
ForestG
1
Saya harus mendefinisikan enum dalam file eksternal menggunakan "export enum MyEnum {...}". Kemudian dalam file komponen, impor 'MyEnum' dari file eksternal itu, dan lanjutkan dengan solusi di atas untuk 'MyEnum = MyEnum "dll.
ObjectiveTC
16

per 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;
}
born2net
sumber
1
Apa yang sudah berubah?
Carl G
diganti dengan ngSwitchCase
born2net
Ah, baiklah. Terima kasih!
Carl G
14

Sebagai alternatif dari dekorator @Eric Lease, yang sayangnya tidak berfungsi menggunakan --aot(dan dengan demikian --prod) dibangun, saya menggunakan layanan yang memaparkan semua enums aplikasi saya. Hanya perlu menyuntikkan secara publik ke setiap komponen yang memerlukannya, dengan nama yang mudah, setelah itu Anda dapat mengakses enum dalam pandangan Anda. Misalnya:

Layanan

import { Injectable } from '@angular/core';
import { MyEnumType } from './app.enums';

@Injectable()
export class EnumsService {
  MyEnumType = MyEnumType;
  // ...
}

Jangan lupa untuk memasukkannya ke dalam daftar penyedia modul Anda.

Kelas komponen

export class MyComponent {
  constructor(public enums: EnumsService) {}
  @Input() public someProperty: MyEnumType;

  // ...
}

Komponen html

<div *ngIf="someProperty === enums.MyEnumType.SomeValue">Match!</div>
Vincent Sels
sumber
Saya juga perlu mengubah layanan dan menulis @Injectable ({providedIn: 'root'}) untuk membuatnya berfungsi. Terima kasih!
Stalli
2

Mulailah dengan mempertimbangkan, 'Apakah saya benar - benar ingin melakukan ini?'

Saya tidak punya masalah merujuk langsung ke enums dalam HTML, tetapi dalam beberapa kasus ada alternatif yang lebih bersih yang tidak kehilangan jenis keamanan. Misalnya jika Anda memilih pendekatan yang ditunjukkan dalam jawaban saya yang lain, Anda mungkin telah menyatakan TT dalam komponen Anda, seperti ini:

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

Untuk menampilkan tata letak yang berbeda di HTML Anda, Anda akan memiliki *ngIfuntuk setiap jenis tata letak, dan Anda bisa merujuk langsung ke enum di HTML komponen Anda:

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

Contoh ini menggunakan layanan untuk mendapatkan tata letak saat ini, menjalankannya melalui pipa async dan kemudian membandingkannya dengan nilai enum kami. Sangat bertele-tele, berbelit-belit dan tidak menyenangkan untuk dilihat. Itu juga memperlihatkan nama enum, yang itu sendiri mungkin terlalu bertele-tele.

Alternatif, yang mempertahankan keamanan jenis dari HTML

Atau Anda dapat melakukan hal berikut, dan mendeklarasikan fungsi yang lebih mudah dibaca dalam file .ts komponen Anda:

*ngIf="isResponsiveLayout('Horizontal')"

Jauh lebih bersih! Tetapi bagaimana jika seseorang mengetik 'Horziontal'secara tidak sengaja? Seluruh alasan Anda ingin menggunakan enum dalam HTML adalah untuk mengetik dengan benar, bukan?

Kita masih bisa mencapainya dengan keyof dan beberapa sihir naskah. Ini adalah definisi fungsi:

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

Perhatikan penggunaan FeatureBoxResponsiveLayout[string]yang mengubah nilai string yang diteruskan ke nilai numerik enum.

Ini akan memberikan pesan kesalahan dengan kompilasi AOT jika Anda menggunakan nilai yang tidak valid.

Argumen tipe '"H4orizontal"' tidak dapat dialihkan ke parameter tipe '"Vertikal" | "Horisontal"

Saat ini VSCode tidak cukup pintar untuk menggarisbawahi H4orizontaldalam editor HTML, tetapi Anda akan mendapatkan peringatan pada waktu kompilasi (dengan --prod build atau --aot switch). Ini juga dapat ditingkatkan pada pembaruan di masa mendatang.

Simon_Weaver
sumber
tidak yakin apakah saya suka konstanta di dalam htmltetapi saya melihat maksud Anda dan mulai menggunakannya; itu melakukan pekerjaan, seperti masa lalu yang indah, ketika menyusun! :)
genuinefafa
@genuinefafa pendekatan ini benar-benar tentang mendapatkan enum itu sendiri dari html tetapi masih memungkinkan nilai-nilai enum dikompilasi diperiksa. Saya kira Anda bisa mengatakan itu memisahkan html dari ts tetapi itu sendiri tidak menawarkan manfaat nyata karena mereka selalu digunakan bersama.
Simon_Weaver
Saya suka cek jenis, khususnya dalam pengembangan yang tidak diuji secara otomatis
genuinefafa
upvote karena kalimat pembuka "Mulailah dengan mempertimbangkan 'Apakah saya benar-benar ingin melakukan ini?'"
WebDever
2

Komponen saya menggunakan objek myClassObjectbertipe MyClass, yang digunakan sendiri MyEnum. Ini mengarah pada masalah yang sama seperti dijelaskan di atas. Selesaikan dengan melakukan:

export enum MyEnum {
    Option1,
    Option2,
    Option3
}
export class MyClass {
    myEnum: typeof MyEnum;
    myEnumField: MyEnum;
    someOtherField: string;
}

dan kemudian menggunakan ini di templat sebagai

<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
sumber
1

Jika menggunakan pendekatan 'typetable reference' (dari @Carl G) dan Anda menggunakan beberapa tabel tipe, Anda mungkin ingin mempertimbangkan cara ini:

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; 

Kemudian akses dengan file html Anda dengan

{{ TT.DogType[dog] }}   => "GoldenRetriever"

Saya mendukung pendekatan ini karena menjelaskan Anda merujuk pada jenis yang dapat diubah, dan juga menghindari polusi yang tidak perlu pada file komponen Anda.

Anda juga dapat meletakkan global di TTsuatu tempat dan menambahkan enum ke sana sesuai kebutuhan (jika Anda menginginkan ini, Anda juga dapat membuat layanan seperti yang ditunjukkan oleh jawaban @VincentSels). Jika Anda memiliki banyak jenis huruf ini mungkin menjadi rumit.

Anda juga selalu mengganti nama mereka dalam deklarasi Anda untuk mendapatkan nama yang lebih pendek.

Simon_Weaver
sumber