Wie kann ich ngFor verwenden, um Typescript Enum als Array von Zeichenfolgen zu durchlaufen?

76

Ich benutze Angular2 und Typscript. Ich habe eine Aufzählung:

export enum Role {
    ServiceAdmin, CompanyAdmin, Foreman, AgentForeman, 
    CrewMember, AgentCrewMember, Customer
}

Ich möchte * ngFor verwenden, um über die Aufzählung zu iterieren. Was ist der beste Weg, dies zu tun? Muss ich ein Rohr erstellen? Oder gibt es einen einfacheren Weg?

Rob Gorman
quelle
Bitte erwägen Sie, die akzeptierte Antwort auf Murolacks zu aktualisieren, da dies die einfachste und aktuellste ist. Vielen Dank!
Yulian

Antworten:

81

Eine Aufzählung ist nur ein Objekt.

Ihre Aufzählung ist ungefähr so ​​in JavaScript geschrieben:

{
    0: "ServiceAdmin", 
    1: "CompanyAdmin", 
    2: "Foreman", 
    3: "AgentForeman", 
    4: "CrewMember", 
    5: "AgentCrewMember", 
    6: "Customer", 
    ServiceAdmin: 0, 
    CompanyAdmin: 1, 
    Foreman: 2, 
    AgentForeman: 3, 
    CrewMember: 4,
    AgentCrewMember: 5,
    Customer: 6
}

Sie können es also folgendermaßen iterieren ( plnkr ):

@Component({
    ...
    template: `
    <div *ngFor="let item of keys()">
      {{ item }}
    </div>  
  `
})
export class YourComponent {
    role = Role;
    keys() : Array<string> {
        var keys = Object.keys(this.role);
        return keys.slice(keys.length / 2);
    }
}

Oder wäre besser, benutzerdefinierte Pipe zu erstellen :

@Pipe({
  name: 'enumToArray'
})
export class EnumToArrayPipe implements PipeTransform {
  transform(data: Object) {
    const keys = Object.keys(data);
    return keys.slice(keys.length / 2);
  }
}

Beispiel

Aktualisieren

Mit Typescript 2.4 können Enum-Mitglieder Zeichenfolgeninitialisierer enthalten wie:

enum Colors {
    Red = "RED",
    Green = "GREEN",
    Blue = "BLUE",
}

In diesem Fall können Sie einfach Object.keys(data);vom Rohr zurückkehren.

Yurzui
quelle
Dies funktioniert nicht gut, wenn negative Werte enthalten sind. Übrigens: das plnkr funktioniert nicht mehr.
Martin Schneider
@ MA-Maddin Aktualisierter Plunker
Yurzui
Wenn jemand die benutzerdefinierte Pipe in einem freigegebenen Modul verwendet, vergessen Sie nicht, EnumToArrayPipe beim Export hinzuzufügen
Rob
2
In Winkel 6 können Sie einfach das Array von Schlüsseln zurückgeben und Sie müssen nicht die Hälfte davon in die Pipe return Object.keys(data);
schneiden
5
Tatsächlich benötigen Sie in Angular 6 nicht einmal mehr das Rohr, sondern nur noch role = Object.keys(Role)in Ihrer Komponente.
Bastien Jansen
53

Sie können einfach die in Angular 6.1 eingeführte "keyvalue" -Pipe verwenden.

<p *ngFor="let enum of TestEnum | keyvalue">
  {{ enum.key }} - {{ enum.value}}
</p>

Ein vollständiges Beispiel finden Sie hier -> https://stackblitz.com/edit/angular-gujg2e

Murolack
quelle
5
Es hat funktioniert, aber die Reihenfolge der Aufzählung alphabetisch sortiert.
Ravi Raj
1
@RRaj können Sie die Sortierreihenfolge steuern: stackoverflow.com/a/52794221/5253897
Christian Jensen
14

Der Bereich der Vorlage ist die Komponenteninstanz. Wenn Sie auf etwas außerhalb dieses Bereichs zugreifen möchten, müssen Sie es über Ihre Komponenteninstanz verfügbar machen:

Dies funktioniert auch, wenn die Aufzählungstasten nicht mit 0 beginnen

@Pipe({name: 'enumToArray'})
export class EnumToArrayPipe implements PipeTransform {
  transform(value) : Object {
    return Object.keys(value).filter(e => !isNaN(+e)).map(o => { return {index: +o, name: value[o]}});
  }
}

@Component({
  ...
  imports: [EnumsToArrayPipe],
  template: `<div *ngFor="let item of roles | enumToArray">{{item.index}}: {{item.name}}</div>`
})
class MyComponent {
  roles = Role;
}

Siehe auch https://stackoverflow.com/a/35750252/217408

Günter Zöchbauer
quelle
Danke für die Hilfe. Ich habe es gerade versucht, erhalte aber die Fehlermeldung "NgFor unterstützt nur die Bindung an Iterables wie Arrays." Es sieht also so aus, als könnte ich eine Pipe erstellen, um Rollen in ein Array von Aufzählungen oder Zeichenfolgen umzuwandeln. Aber es scheint mir, dass ich das irgendwie nativ tun sollte.
Rob Gorman
Entschuldigung, habe die Pfeife vergessen. Meine Antwort wurde aktualisiert.
Günter Zöchbauer
1
Vielen Dank! Bessere Lösung als die Besetzung von Array :-)
Axel Schmitz
Ich erhalte den Fehler Argument vom Typ '{imports: (typeof EnumToArrayPipe) []; Selektor: Zeichenfolge; templateUrl: string; styleUrls: string []; } 'kann keinem Parameter vom Typ' Component 'zugewiesen werden. Das Objektliteral darf nur bekannte Eigenschaften angeben, und 'Importe' sind im Typ 'Komponente' nicht vorhanden. Wenn ich es im ngModule importiere, kann es die Pipe nicht finden. Irgendeine Idee, was los ist?
GeekPeek
1
Die Verwendung / Referenzierung der Pipe muss mit enum (s) ToArray erfolgen, wie aus der Deklaration der Pipe (Name) hervorgeht. Ich habe die Bearbeitung vorgenommen.
Bernoulli IT
9

Ich musste das Gleiche tun und vielleicht war es das, was du wolltest.
Mehr TROCKEN und es kann auch mit verwendet modulewerden.

export enum Role {
    ServiceAdmin, CompanyAdmin, Foreman, AgentForeman, 
    CrewMember, AgentCrewMember, Customer
}

export namespace Role {

  export function keys(): Array<string>{
    var keys = Object.keys(Role);
    return keys.slice(keys.length / 2, keys.length-1);
  }
}

das Objekt, das vor dem Slice ausgegeben wird

{
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "ServiceAdmin",
    "CompanyAdmin",
    "Foreman",
    "AgentForeman",
    "CrewMember",
    "AgentCrewMember",
    "Customer",
    "keys"
}

Typoskript führt die beiden Deklarationen zusammen, daher die keys.lenght-1

und die ngFor:

<div *ngFor="let role of Roles.keys()">{{ role }}</div>

Weitere Informationen:
Zusammenführung der Typescript-Erklärung

basierend auf:
TypeScript: Hinzufügen von Funktionen zu einer Aufzählung https://basarat.gitbooks.io/typescript/content/docs/enums.html (am Ende des Aufzählungskapitels )

Filipe Morais Jorge
quelle
1
und um Werte zu erhalten, würden Sie das Gegenteil tun: let values ​​= Object.keys (ApplicationMode); return values.slice (0, values.length / 2); Danke für die Idee.
AFD
Es gibt keinen "Schlüssel" -Eintrag vor dem Slice, daher brauchen Sie ihn nichtkeys.lenght-1
KlsLondon
7

Nach weiteren Recherchen und Überprüfungen der anderen Antworten kann ich nun eine Antwort auf meine Frage formulieren. Ich denke, es ist nicht möglich, einfach * ngFor zu verwenden, um über eine Aufzählung zu iterieren, ohne dass die Komponente Code unterstützt. Die Codeunterstützung kann aus Konstruktorcode bestehen, der die Aufzählung in eine Art Array verwandelt, oder wir können eine benutzerdefinierte Pipe erstellen, die etwas Ähnliches tut.

Rob Gorman
quelle
5
export enum Priority {
  LL = 1,   // VERY LOW
  L = 2,    // LOW
  N = 3,    // NORMAL
  U = 4,    // HIGH
  UU = 5    // VERY HIGH
}

Ihre Winkelkomponente.ts:

import { Priority } from './../shared/core/config/datas.config';

@Component({
  selector: 'app-yourcomponent',
  template: `
    <ng-container *ngFor="let p of getPriority">
       <div> {{p.key}} / {{p.value}} </div>
    </ng-container> 
  `
})

export class YourComponent {
  getPriority = this.getENUM(Priority);

  getENUM(ENUM:any): string[] {
    let myEnum = [];
    let objectEnum = Object.keys(ENUM);
    const values = objectEnum.slice( 0 , objectEnum.length / 2 );
    const keys = objectEnum.slice( objectEnum.length / 2 );

    for (let i = 0 ; i < objectEnum.length/2 ; i++ ) {
      myEnum.push( { key: keys[i], value: values[i] } ); 
    }
    return myEnum;
  }
}
Neo_Ryu
quelle
3

Ich habe die Aufzählung:

export enum FileCategory {
  passport = 'Multipass',
  agreement = 'Personal agreement',
  contract = 'Contract',
  photo = 'Self photos',
  other = 'Other'
}

In der Komponente ts-Datei:

export class MyBestComponent implements OnInit {
  fileCategory = FileCategory;

  // returns keys of enum
  fileKeys(): Array<string> {
    const keys = Object.keys(this.fileCategory);
    return keys;
  }

  // returns values of enum
  fileVals(): Array<string> {
    const keys = Object.keys(this.fileCategory);
    return keys.map(el => Object(this.fileCategory)[el]);
  }

Zeigen Sie in der HTML-Vorlage die Werte und Schlüssel dieser Aufzählung an:

  <a *ngFor="let cat of fileVals()"
     (click)="addFileCategory(cat)">{{cat}}</a>
  <a *ngFor="let cat of fileKeys()"
     (click)="addFileCategory(cat)">{{cat}}</a>
Pax Beach
quelle
Die Verwendung der Methoden in ngFor führt zu einer Vielzahl von Änderungserkennungen, da Änderungen des Funktionsrückgabewerts nicht sehr gut erkennbar sind. Sie sollten den Rückgabewert "fileVals ()" in eine Variable einfügen und von dort aus lesen.
Nadine
3

mit Rohr:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'enum'
})
export class EnumSelectPipe implements PipeTransform {
  transform(value: any): [number, string][] {
    return Object.keys(value).filter(t => isNaN(+t)).map(t => [value[t], t]);
  }
}

und in der Vorlage:

<mat-select formControlName="type" placeholder="Package Type">
  <mat-option *ngFor="let pType of PackageTypes | enum" [value]="pType[0]">{{ pType[1] | title}}</mat-option>
</mat-select>
EladTal
quelle
2

Ich empfehle Ihnen, ein generisches Pipe zu verwenden. Es ist flexibler und weniger redundant in Ihrem Code. Das Problem bei einigen früheren Aussagen ist, dass das Typoskript es Ihnen ermöglicht, eine andere Art von Aufzählung zu haben, nicht nur Zahl / Zeichenfolge.

Zum Beispiel:

export enum NotificationGrouping {
    GroupByCreatedAt = "GroupByCreatedAt", 
    GroupByCreatedByUser = "GroupByCreatedByUser", 
    GroupByEntity = "GroupByEntity", 
    GroupByAction = "GroupByAction", 
}

Hier ist meine Lösung:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'enumToArray'
})
export class EnumToArrayPipe implements PipeTransform {

  transform(value, args: string[]): any {
    let result = [];
    var keys = Object.keys(value);
    var values = Object.values(value);
    for (var i = 0; i < keys.length; i++) {
      result.push({ key: keys[i], value: values[i] });
    }
    return result; 
    //or if you want to order the result: 
    //return result.sort((a, b) => a.value < b.value ? -1 : 1);
  }
}

und das HTML wird sein:

<mat-select [(ngModel)]="groupKey">
  <mat-option *ngFor="let group of notificationGrouping | enumToArray"
              [value]="group.key">
    {{ group.value }}
  </mat-option>
</mat-select>

in ts:

public notificationGrouping : NotificationGrouping

Hinweis: Es ist immer noch interessant zu sehen, wie Leute ohne Erklärung ein Minus setzen ... Für andere, die an dieser Lösung interessiert sein könnten, kann ich bestätigen, dass sie korrekt funktioniert.

Cedric Arnould
quelle
Wo ist Benachrichtigungsgruppe definiert?
LearningPal
1
Obwohl ich anfangs eine einfachere Lösung erwartet hatte, die bereits standardmäßig per Typoskript oder Winkel bereitgestellt wurde (die standardmäßig keine Sortierung beinhalten würde), ist Ihre Lösung perfekt und ich habe heute etwas Neues gelernt: Pipes! :) Vielen Dank
Mihai Cicu
2

ES6 unterstützt

export enum E {
    a = 'First',
    b = 'Second',
    c = 'Third'
}

let keyValueArray = Object.keys(E).map(k => ({key: k, value: E[k as any]}));
Sameera R.
quelle
1

In Angular 7 wird bei Verwendung von keys () weiterhin eine Liste aller Schlüssel und Werte angezeigt.

Basierend auf den obigen Antworten verwende ich dies für eine einfache ENUM, scheint sauberer und OO:

export enum CategoryType {
    Type1,
    Type2,
    ...,
}

export namespace CategoryType {
    export function keys() {
        return Object.keys(CategoryType).filter(k => !isNaN(Number(k)));
    }
}

dann in der Vorlage:

<option *ngFor="let type of types.keys()" [value]="type">{{types[type]}}</option>

Die Funktion wird zu einem weiteren Eintrag in der Aufzählung, wird jedoch wie die anderen Nicht-Zahlen herausgefiltert.

Dovev Hefetz
quelle