So verwenden Sie einen Typoskript-Aufzählungswert in einer Angular2 ngSwitch-Anweisung

156

Die Typescript-Aufzählung scheint eine natürliche Übereinstimmung mit der ngSwitch-Direktive von Angular2 zu sein. Wenn ich jedoch versuche, eine Aufzählung in der Vorlage meiner Komponente zu verwenden, wird "Eigenschaft 'xxx' von undefiniert in ... kann nicht gelesen werden" angezeigt. Wie kann ich Enum-Werte in meiner Komponentenvorlage verwenden?

Bitte beachten Sie, dass dies anders ist als das Erstellen von HTML-Auswahloptionen basierend auf ALLEN Werten einer Aufzählung (ngFor). Diese Frage bezieht sich auf ngSwitch basierend auf einem bestimmten Wert einer Aufzählung. Obwohl der gleiche Ansatz zum Erstellen eines klasseninternen Verweises auf die Aufzählung erscheint.

Carl G.
quelle
Mögliches Duplikat von Select basierend auf Enum in Angular2
Günter Zöchbauer
1
Ich denke nicht, dass diese Fragen Duplikate sind; Der andere fragt, wie HTML-Auswahloptionen basierend auf ALLEN Werten einer Aufzählung (ngFor) erstellt werden sollen, während es in diesem Fall um ngSwitch geht, der auf einem bestimmten Wert einer Aufzählung basiert. Obwohl der gleiche Ansatz zum Erstellen eines klasseninternen Verweises auf die Aufzählung erscheint. Vielen Dank, dass Sie auf die Ähnlichkeit hingewiesen haben.
Carl G

Antworten:

166

Sie können einen Verweis auf die Aufzählung in Ihrer Komponentenklasse erstellen (ich habe gerade das ursprüngliche Zeichen in Kleinbuchstaben geändert) und dann diesen Verweis aus der Vorlage ( Plunker ) verwenden:

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.
quelle
87

Sie können einen benutzerdefinierten Dekorator erstellen, der Ihrer Komponente hinzugefügt wird, um sie auf Aufzählungen aufmerksam zu machen.

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
quelle
7
Hat jemand Erfolg mit dieser Methode mit dem AoT-Compiler gehabt?
Danny
2
@ Simon_Weaver- Dekoratoren sind im Wesentlichen Funktionen, die eine Funktion als Parameter verwenden und das Verhalten dieser Funktion erweitern. Im Fall von ES6 / 7 beschäftigen wir uns mit der Erweiterung / Annotation von Klassen. Hier ist ein hochrangiger Artikel darüber, wie sie funktionieren . Der Vorschlag zur Implementierung in ES7 befindet sich auf Github - derzeit in Phase 2. In diesem Vorschlag werden mögliche Verwendungszwecke für Dekorateure angesprochen. TypeScript ist eine Obermenge von JS und enthält diese Funktion.
Eric Lease
2
@Simon_Weaver In diesem Fall verbirgt der syntaktische Zucker den Aufruf von MyEnumAware(), an den die EnumAwareComponentInstanz übergeben wird, und verfügt über eine Eigenschaft, MyEnumdie dem Prototyp hinzugefügt wurde. Der Wert der Eigenschaft wird in der Aufzählung selbst festgelegt. Diese Methode macht dasselbe wie die akzeptierte Antwort. Es nutzt nur den syntaktischen Zucker, der für Dekorateure vorgeschlagen und in TypeScript erlaubt ist. Wenn Sie Angular verwenden, verwenden Sie sofort die Decorator-Syntax. Das Component ist a , eine Erweiterung einer leeren Klasse, mit der Angulars Kernklassen interagieren können.
Eric Lease
5
-1: Dies scheint nicht mit aot zu funktionieren, was dazu führt ERROR in ng:///.../whatever.component.html (13,3): Property 'MyEnum' does not exist on type 'EnumAwareComponent'. Dies ist sinnvoll, da die vom Dekorateur hinzugefügte Eigenschaft niemals deklariert wird und der Typoskript-Compiler nichts von ihrer Existenz weiß.
Meriton
2
Also benutze ich das seit mehr als 4 Monaten. Jetzt, wo ich einen --prodBuild mache (Ionic 3 / Angular 4 / Typescript 2.4.2), funktioniert es jedoch nicht mehr. Ich bekomme den Fehler "TypeError: Cannot read property 'FirstValue' of undefined". Ich verwende eine numerische Standardaufzählung. Es funktioniert gut mit AoT, aber nicht mit --prod. Es funktioniert, wenn ich es in die Verwendung von Ganzzahlen im HTML ändere, aber das ist nicht der Punkt. Irgendwelche Ideen?
Russ
47

Dies ist einfach und funktioniert wie ein Zauber :) Deklarieren Sie einfach Ihre Aufzählung wie folgt und Sie können sie für HTML-Vorlagen verwenden

  statusEnum: typeof StatusEnum = StatusEnum;
Aymen Boumaiza
quelle
Nach den Tagen der Forschung fand ich endlich, was ich brauchte. Danke vielmals!
Gsiradze
@ Rahul StatusEnumist in einer der .tsKlassen definiert. Binden Sie die Angular-Komponente, die Sie importieren, an eine Komponenteneigenschaft (hier statusEnum), und auf die Komponenteneigenschaften kann über die Vorlage zugegriffen werden.
Tom
Panzer das ist toll
HSN KH
45

Angular4 - Verwenden von Enum in der HTML-Vorlage ngSwitch / ngSwitchCase

Lösung hier: https://stackoverflow.com/a/42464835/802196

Bildnachweis: @snorkpete

In Ihrer Komponente haben Sie

enum MyEnum{
  First,
  Second
}

Anschließend bringen Sie in Ihrer Komponente den Aufzählungstyp über ein Mitglied 'MyEnum' ein und erstellen ein weiteres Mitglied für Ihre Aufzählungsvariable 'myEnumVar':

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

Sie können jetzt myEnumVar und MyEnum in Ihrer HTML-Vorlage verwenden. Beispiel: Verwenden von Enums in 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
quelle
Wie können Sie dieselbe Aufzählung in einer anderen Komponente wiederverwenden?
ForestG
1
Ich musste die Aufzählung in einer externen Datei mit "export enum MyEnum {...}" definieren. Importieren Sie dann in der Komponentendatei 'MyEnum' aus dieser externen Datei und fahren Sie mit der obigen Lösung für 'MyEnum = MyEnum "usw. fort.
ObjectiveTC
15

ab 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
quelle
1
Was hat sich verändert?
Carl G
ersetzt durch ngSwitchCase
born2net
Ah, okay. Vielen Dank!
Carl G
14

Als Alternative zu @Eric Leases Dekorateur, der leider nicht mit --aot(und damit --prod) Builds funktioniert , habe ich auf einen Dienst zurückgegriffen, der alle Aufzählungen meiner Anwendung verfügbar macht. Sie müssen dies nur öffentlich in jede Komponente einfügen, für die dies erforderlich ist, und zwar unter einem einfachen Namen. Danach können Sie auf die Aufzählungen in Ihren Ansichten zugreifen. Z.B:

Bedienung

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

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

Vergessen Sie nicht, es in die Anbieterliste Ihres Moduls aufzunehmen.

Komponentenklasse

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

  // ...
}

Komponente HTML

<div *ngIf="someProperty === enums.MyEnumType.SomeValue">Match!</div>
Vincent Sels
quelle
Ich musste auch den Dienst ändern und @Injectable ({BereitgestelltIn: 'root'}) schreiben, damit es funktioniert. Vielen Dank!
Stalli
2

Beginnen Sie mit der Überlegung, ob ich das wirklich tun möchte.

Ich habe kein Problem damit, auf Enums direkt in HTML zu verweisen, aber in einigen Fällen gibt es sauberere Alternativen, die die Typensicherheit nicht verlieren. Wenn Sie beispielsweise den in meiner anderen Antwort gezeigten Ansatz wählen, haben Sie möglicherweise TT in Ihrer Komponente wie folgt deklariert:

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

Um ein anderes Layout in Ihrem HTML-Code anzuzeigen, haben Sie *ngIffür jeden Layouttyp ein Layout, und Sie können direkt auf die Aufzählung im HTML-Code Ihrer Komponente verweisen:

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

In diesem Beispiel wird ein Dienst verwendet, um das aktuelle Layout abzurufen, es durch die asynchrone Pipe auszuführen und es dann mit unserem Aufzählungswert zu vergleichen. Es ist ziemlich ausführlich, verschlungen und macht nicht viel Spaß, es anzuschauen. Es enthüllt auch den Namen der Aufzählung, die selbst zu ausführlich sein kann.

Alternativ bleibt die Typensicherheit des HTML-Codes erhalten

Alternativ können Sie Folgendes tun und eine besser lesbare Funktion in der .ts-Datei Ihrer Komponente deklarieren:

*ngIf="isResponsiveLayout('Horizontal')"

Viel sauberer! Aber was ist, wenn jemand versehentlich 'Horziontal'tippt? Der ganze Grund, warum Sie eine Aufzählung im HTML verwenden wollten, war typsicher zu sein, oder?

Wir können das immer noch mit keyof und etwas Typoskript-Magie erreichen. Dies ist die Definition der Funktion:

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

Beachten Sie, bei FeatureBoxResponsiveLayout[string]welcher Verwendung der übergebene Zeichenfolgenwert in den numerischen Wert der Aufzählung konvertiert wird .

Dies gibt eine Fehlermeldung mit einer AOT-Kompilierung aus, wenn Sie einen ungültigen Wert verwenden.

Das Argument vom Typ '"H4orizontal"' kann nicht dem Parameter vom Typ '"Vertikal" | zugewiesen werden "Horizontal"

Derzeit ist VSCode nicht intelligent genug, um H4orizontalim HTML-Editor zu unterstreichen , aber Sie erhalten die Warnung beim Kompilieren (mit --prod build oder --aot switch). Dies kann auch in einem zukünftigen Update verbessert werden.

Simon_Weaver
quelle
Ich bin mir nicht sicher, ob ich Konstanten im Inneren mag, htmlaber ich sehe Ihren Standpunkt und habe angefangen, ihn zu verwenden. es macht den Job, wie die guten alten Zeiten, beim Kompilieren! :)
echtfafa
@genuinefafa Bei diesem Ansatz geht es wirklich darum, die Aufzählung selbst aus dem HTML-Code herauszuholen, aber dennoch zu ermöglichen, dass die Aufzählungswerte kompiliert werden. Ich nehme an, man könnte sagen, es entkoppelt HTML von ts, aber das an sich bietet keine wirklichen Vorteile, weil sie immer zusammen verwendet werden.
Simon_Weaver
Ich mag Typprüfung, besonders in nicht automatisch getesteten Entwicklungen
Originalfafa
Upvote wegen Eröffnungszeile "Beginnen Sie mit der Überlegung, ob ich das wirklich tun möchte?"
WebDever
2

Meine Komponente verwendete ein Objekt myClassObjectvom Typ MyClass, das selbst verwendet wurde MyEnum. Dies führte zu dem oben beschriebenen Problem. Es wurde gelöst durch:

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

und dann in der Vorlage als verwenden

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

Wenn Sie den Ansatz "Typetable Reference" (von @Carl G) verwenden und mehrere Typentabellen verwenden, sollten Sie Folgendes berücksichtigen:

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; 

Dann greifen Sie in Ihrer HTML-Datei mit zu

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

Ich bevorzuge diesen Ansatz, da er deutlich macht, dass Sie sich auf eine Typetabelle beziehen, und außerdem eine unnötige Verschmutzung Ihrer Komponentendatei vermeidet.

Sie können auch TTirgendwo eine globale Datei einfügen und nach Bedarf Aufzählungen hinzufügen (wenn Sie dies möchten, können Sie auch einen Dienst erstellen, wie in der Antwort von @VincentSels gezeigt). Wenn Sie viele, viele Typetabellen haben, kann dies umständlich werden.

Außerdem benennen Sie sie in Ihrer Deklaration immer um, um einen kürzeren Namen zu erhalten.

Simon_Weaver
quelle