Як я можу використовувати ngFor для ітерації над Typescript Enum як масиву рядків


79

Я використовую Angular2 та Typscript. У мене перелік:

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

Я хочу використовувати * ngFor для перебору переліку. Який найкращий спосіб це зробити? Чи повинен я створити трубу? Або є простіший спосіб?


Будь ласка, розгляньте можливість оновлення прийнятої відповіді до відповіді Муролака, оскільки вона є найпростішою та найсучаснішою. Дякую!
Юліан

Відповіді:


86

Перерахування - це просто об'єкт.

Ваш перелік написаний приблизно таким чином у JavaScript:

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

Тож ви можете повторити це таким чином ( 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);
    }
}

Або було б краще створити власний канал :

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

Приклад

Оновлення

Typescript 2.4 дозволяє учасникам enum містити ініціалізатори рядків, такі як:

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

в цьому випадку ви можете просто повернутися Object.keys(data);з труби.


це погано працює, якщо включати негативні значення. До речі: plnkr вже не працює.
Мартін Шнайдер

@ MA-Maddin Оновлений плюнкер
yurzui

Якщо хтось використовує спеціальний конвеєр у спільному модулі, не забудьте додати EnumToArrayPipe в експорті
Роб

2
У кутовому 6 ви можете просто повернути масив ключів, і вам не потрібно нарізати половину з них у трубі:return Object.keys(data);
K1ngjulien_

5
Насправді в Angular 6 вам навіть не потрібна труба, просто використовуйте role = Object.keys(Role)у своєму компоненті.
Бастієн Янсен

59

Ви можете просто використати трубу "keyvalue", представлену в Angular 6.1.

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

Повний приклад див. Тут -> https://stackblitz.com/edit/angular-gujg2e


6
Це спрацювало, але порядок переліку сортувався за алфавітом.
Раві Радж

1
@RRaj ви можете контролювати порядок сортування: stackoverflow.com/a/52794221/5253897
Крістіан Дженсен

14

Сфера дії шаблону - екземпляр компонента. Якщо ви хочете отримати доступ до чогось поза цим обсягом, вам потрібно зробити це доступним із вашого екземпляра компонента:

Це також працює, якщо клавіші переліку не починаються з 0

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

Див. Також https://stackoverflow.com/a/35750252/217408


Дякую за допомогу. Я щойно спробував це, але отримав помилку "NgFor підтримує прив'язку лише до Iterables, таких як масиви." Отже, схоже, я міг би створити конвеєр для перетворення ролей у масив перелічень або рядків. Але мені здається, що я мав би це якось зробити вдома.
Роб Горман

Вибачте, забув трубу. Оновив свою відповідь.
Günter Zöchbauer

1
Дякую! Краще рішення, ніж акторський склад для Array :-)
Аксель Шмітц

Я отримую аргумент про помилку типу '{import: (typeof EnumToArrayPipe) []; селектор: рядок; templateUrl: рядок; styleUrls: string []; } 'не можна призначити параметру типу' Компонент '. Об’єктний літерал може вказувати лише відомі властивості, а „імпорт” не існує у типі „Компонент”. Коли я поміщаю його в імпорт в ngModule, він не може знайти трубу. Будь-яка ідея, що не так?
GeekPeek

1
Використання / посилання на конвеєр повинно виконуватися з переліком (-ами) ToArray, як це видно з декларації конвеєра (імені). Я зробив редагування.
Bernoulli IT

9

Мені потрібно було зробити те саме, і, можливо, це те, що ти хотів.
Більше СУХОГО, і його також можна використовувати module.

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

виведення об'єкта перед зрізом

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

машинопис об'єднує два оголошення, отже, keys.lenght-1

та ngFor:

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

детальніше:
злиття декларації машинопису

на основі:
машинопис: Додати функції до Enum https://basarat.gitbooks.io/typescript/content/docs/enums.html (в кінці Перерахування глави.)


1
а щоб отримати значення, ви зробите навпаки: let values ​​= Object.keys (ApplicationMode); повертає values.slice (0, values.length / 2); Дякую за ідею.
AFD

перед фрагментом немає введення "ключів", отже вам не потрібноkeys.lenght-1
KlsLondon

7

Після подальших досліджень та перегляду інших відповідей я можу сформулювати відповідь на своє запитання. Я думаю, що неможливо просто використовувати * ngFor для ітерації переліку без певної підтримки коду в компоненті. Підтримка коду може складатися з коду конструктора, який перетворює Enum у якийсь масив, або ми можемо створити власний канал, який робить щось подібне.


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

Ваш кутовий компонент.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;
  }
}

3

Я маю перелік:

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

У файлі ts компонента:

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

У шаблоні HTML відображаються ці значення та ключі переліку:

  <a *ngFor="let cat of fileVals()"
     (click)="addFileCategory(cat)">{{cat}}</a>
  <a *ngFor="let cat of fileKeys()"
     (click)="addFileCategory(cat)">{{cat}}</a>

використання методів всередині ngFor призведе до значної кількості виявлення змін, оскільки зміни поверненого значення функції виявляються не дуже добре. Ви повинні помістити значення змінної "fileVals ()" у змінну і прочитати його звідти.
Надін

3

за допомогою труби:

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

і в шаблоні:

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

2

Я рекомендую вам використовувати загальний Pipe, він буде більш гнучким і менш зайвим у вашому коді. Проблема деяких попередніх пропозицій полягає в тому, що машинопис дозволяє вам мати різний тип перерахування, а не лише число / рядок.

Наприклад:

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

Ось моє рішення:

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

і html буде:

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

в ts:

public notificationGrouping : NotificationGrouping

Примітка: Все ще цікаво спостерігати, як люди ставлять мінус без пояснень ... Для інших, кого це рішення може зацікавити, я можу підтвердити, що воно працює правильно.


де визначено групу сповіщень?
LearningPal

1
хоча спочатку я очікував більш простого рішення, яке вже пропонується з коробки за допомогою машинопису або angular (яке за замовчуванням не включає жодної сортування), ваше рішення ідеальне, і сьогодні я дізнався щось нове: Pipes! :) Щиро дякую
Mihai Cicu

2

ES6 опори

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

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

1

У Angular 7 все ще отримуємо список усіх ключів та значень при використанні ключів ().

Виходячи з наведених вище відповідей, я використовую це для простого ENUM, здається чистішим та іншим OO:

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

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

то в шаблоні:

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

Функція стає ще одним записом у переліку, але фільтрується, як і інші нечислові числа.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.