Choosing options using an enum in Angular 2

In my TypeScript code, I have defined an enum called CountryCodeEnum which contains the values for France and Belgium.

export enum CountryCodeEnum {
    France = 1,
    Belgium = 2
}

Now, I need to create a dropdown menu in my form using this enum. Each option in the select should have the enum integer value as its value and the corresponding enum text as its label. It should look something like this:

<select>
     <option value="1">France</option>
     <option value="2">Belgium</option>
</select>

Can someone guide me on how to achieve this?

Answer №1

If you prefer not to create a new pipe, here is another solution. You can extract keys into a helper property and use it like this:

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div>
      <select>
        <option *ngFor="let key of keys" [value]="key" [label]="countries[key]"></option>
      </select>
    </div>
  `,
  directives: []
})
export class App {

  countries = CountryCodeEnum

  constructor() {
    this.keys = Object.keys(this.countries).filter(k => !isNaN(Number(k)));
  }
}

Check out the demo: http://plnkr.co/edit/CMFt6Zl7lLYgnHoKKa4E?p=preview

Edit:

If you want the options as numbers instead of strings:

  • replace [value] with [ngValue]
  • add .map(Number) after .filter(...)

Answer №2

update2 enhanced by simplifying with an array structure

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

update

replace pipes: [KeysPipe]

with

@NgModule({
  declarations: [KeysPipe],
  exports: [KeysPipe],
}
export class SharedModule{}
@NgModule({
  ...
  imports: [SharedModule],
})

original

Utilizing the keys pipe sourced from

I had to make adjustments to the pipe in order for it to function correctly with enums (see also How to get names of enum entries?)

@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
  transform(value, args:string[]) : any {
    let keys = [];
    for (var enumMember in value) {
      if (!isNaN(parseInt(enumMember, 10))) {
        keys.push({key: enumMember, value: value[enumMember]});
        // Uncomment if you want log
        // console.log("enum member: ", value[enumMember]);
      } 
    }
    return keys;
  }
}

@Component({ ...
  pipes: [KeysPipe],
  template: `
  <select>
     <option *ngFor="let item of countries | keys" [value]="item.key">{{item.value}}</option>
  </select>
`
})
class MyComponent {
  countries = CountryCodeEnum;
}

Plunker

Also refer to How to iterate object keys using *ngFor?

Answer №3

Here is a simple method for Angular2 version 2.0.0. To demonstrate setting a default value for the country select using reactive forms, see below.

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div>
      <select id="country" formControlName="country">
        <option *ngFor="let key of keys" [value]="key">{{countries[key]}}</option>
      </select>
    </div>
  `,
  directives: []
})
export class App {
  keys: any[];
  countries = CountryCodeEnum;

  constructor(private fb: FormBuilder) {
    this.keys = Object.keys(this.countries).filter(Number);
    this.country = CountryCodeEnum.Belgium; //Set default value
  }
}

Answer №4

In my Angular application, I have opted to use a simple utility function that is shared across the app. This function is used to convert an enum into a standard array which is then utilized for building selects:

export function enumSelector(definition) {
  return Object.keys(definition)
    .map(key => ({ value: definition[key], title: key }));
}

I utilize this function to populate a variable in the Component as follows:

public countries = enumSelector(CountryCodeEnum);

This allows me to easily populate a Material Select component similar to how I would with regular array-based data sources:

<md-select placeholder="Country" [(ngModel)]="country" name="country">
  <md-option *ngFor="let c of countries" [value]="c.value">
    {{ c.title }}
  </md-option>
</md-select>

Grateful for the insights from this discussion!

Answer №5

Here is an alternative solution that includes the "0" index without omitting it like the "Unset" option. I believe using filter(Number) may not be the best approach in this scenario.

@Component({
  selector: 'my-app',
  providers: [],
  template: `
  <select>
    <option *ngFor="let key of keys" [value]="key" [label]="countries[key]"></option>
  </select>`,
  directives: []
})

export class App {
  countries = CountryCodeEnum;

  constructor() {
    this.keys = Object.keys(this.countries).filter(f => !isNaN(Number(f)));
  }
}

// ** NOTE: This enum contains 0 index **
export enum CountryCodeEnum {
   Unset = 0,
   US = 1,
   EU = 2
}

Answer №6

Building off of this response, a different approach is taken here by mapping the values as numbers rather than converting them to strings, thereby fixing a bug. This method also accommodates 0-based enums.

@Component({
  selector: 'my-app',
  providers: [],
  template: `
  <select>
<option *ngFor="let key of keys" [value]="key" [label]="countries[key]"></option>
  </select>`,
  directives: []
})

export class App {
  countries = CountryCodeEnum;

  constructor() {
    this.keys = Object.keys(this.countries)
                      .filter(f => !isNaN(Number(f)))
                      .map(k => parseInt(k));
  }
}

Answer №7

Angular 6.1 and later versions have a built-in KeyValuePipe that can be used as shown below (taken from angular.io docs).

It is assumed that the enum contains human-readable strings, of course :)

@Component({
  selector: 'keyvalue-pipe',
  template: `<span>
    <p>Object</p>
    <div *ngFor="let item of object | keyvalue">
      {{item.key}}:{{item.value}}
    </div>
    <p>Map</p>
    <div *ngFor="let item of map | keyvalue">
      {{item.key}}:{{item.value}}
    </div>
  </span>`
})
export class KeyValuePipeComponent {
  object: {[key: number]: string} = {2: 'foo', 1: 'bar'};
  map = new Map([[2, 'foo'], [1, 'bar']]);
}

Answer №8

Utilizing string enums allows for the following approach.

A simple definition of my string enum would look like this:

    enum StatusEnum {
        Published = <any> 'published',
        Draft = <any> 'draft'
    }

When translated to JavaScript, it resembles the snippet below:

   {
       Published: "published", 
       published: "Published", 
       Draft: "draft", 
       draft: "Draft"
   }

To streamline usage across multiple instances in my project, a utility function was created within a shared service library:

   @Injectable()
   export class UtilsService {
       stringEnumToKeyValue(stringEnum) {
           const keyValue = [];
           const keys = Object.keys(stringEnum).filter((value, index) => {
               return !(index % 2);
           });

           for (const k of keys) {
               keyValue.push({key: k, value: stringEnum[k]});
           }

           return keyValue;
       }
   }

To implement this functionality in a component and bind it to the template, utilize the examples below:

In the component:

    statusSelect;

    constructor(private utils: UtilsService) {
        this.statusSelect = this.utils.stringEnumToKeyValue(StatusEnum);
    }

In the template:

    <option *ngFor="let status of statusSelect" [value]="status.value">
        {{status.key}}
    </option>

Remember to include the UtilsService in the provider array within your app.module.ts file for easy injection into different components.

As a newcomer to TypeScript, I welcome any corrections or suggestions for improvement.

Answer №9

Here is a simple and efficient way to implement this without the need for pipelines or additional code.

import { Component } from '@angular/core';

 enum UserStatus {
    active = 1,
    inactive = 2,
    pending = 3,
    blocked = 0
}


@Component({
  selector: 'my-app',
  template: `
  <h1>Select Status</h1>

  <select (change)="parseValue($event.target.value)">
    <option>--choose--</option>
    <option *ngFor="let status of statuses"
        [value]="status">{{status}}</option>
  </select>

  <h1 [hidden]="selectedStatus == null">
    You selected {{UserStatus[selectedStatus]}}

  </h1>`
})
export class AppComponent { 


  statuses : string[];
  selectedStatus: UserStatus;
  UserStatus : typeof UserStatus = UserStatus;

  ngOnInit() {
    var x = UserStatus;
    var statuses = Object.keys(UserStatus);
    this.statuses = statuses.slice(statuses.length / 2);
  }

  parseValue(value : string) {
    this.selectedStatus = UserStatus[value];

  }
}

Answer №10

export enum Unit
{
    Kg = 1,
    Pack,
    Piece,
    Litre
}

//using map method in Angular

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

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

  transform(enumObj: Object) {

    const keys = Object.keys(enumObj).filter(key => parseInt(key));
    let map = new Map<string, string>();
    keys.forEach(key => map.set(key, enumObj[key]))
    console.log( Array.from(map));
    return Array.from(map);
  }

}

//Using set method in Angular

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

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

      transform(enumObj: Object) {

        const keys = Object.keys(enumObj).filter(key => parseInt(key));
        let set = new Set();
        keys.forEach(key => set.add({ key: parseInt(key), value: enumObj[key] }))
        return Array.from(set);
      }

    }

Answer №11

A Different Approach using Angular 6.1.10 / Typescript ...

 enum Options {
   First,
   Second,
   Third,
   Fourth,
   Fifth,
   Sixth
 }

 console.log('Choices: ');
 let j = 0;
 const dropdownOptions = [
    ];
 Object.keys(Options).filter(key => !Number(key) && key !== '0').forEach(key => {
    dropdownOptions.push({index: j, text: key});
    j++;
  });
 console.log(dropdownOptions);

This code will display:

Console:
Choices: 
    (6) [{…}, {…}, {…}, {…}, {…}, {…}]
    0: {position: 0, text: "First"}
    1: {position: 1, text: "Second"}
    2: {position: 2, text: "Third"}
    3: {position: 3, text: "Fourth"}
    4: {position: 4, text: "Fifth"}
    5: {position: 5, text: "Sixth"}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Enhance Accordion Component with New Information

I am in the process of updating an accordion based on search results returned as an array. Initially, the accordion displays the full set of results from a service, but when I perform a search, the console shows the correct values, however, the ngModel i ...

Developing interconnected dropdowns in Angular 8 for input fields

Imagine we have a list of names structured like this: nameSelected: string; names: Name[ {firstName: 'John', middleName: 'Danny', lastName: 'Smith'}, {firstName: 'Bob', middleName: 'Chris', lastN ...

Encountering 404 errors when reloading routes on an Angular Azure static web app

After deploying my Angular app on Azure static web app, I encountered an error. Whenever I try to redirect to certain routes, it returns a 404 error. However, if I navigate from one route to another within the app, everything works fine. I have attempted t ...

What is the best approach for transferring non-string objects between routes in Angular?

I am currently developing a custom file viewer. To achieve this, I have created a specialized component known as FileBrowserComponent that is specifically designed to be displayed when the route /files is accessed. When navigating, I include a query param ...

Creating a message factory in Typescript using generics

One scenario in my application requires me to define message structures using a simple TypeScript generic along with a basic message factory. Here is the solution I devised: export type Message< T extends string, P extends Record<string, any> ...

Troubleshooting issue with applying hover effect to child divs

How come when I hover over one of the child items in my parentDiv, the background of the last childDiv changes no matter which child I place my mouse on? for (let i = 0; i < Number(height); i++) { for (let j = 0; j < Number(width); j++ ...

Tips for ensuring that npm only creates one instance of a dependency when it is being used by multiple projects

Struggling a bit here. I am diving into npm and configuration files for the first time. The current challenge involves setting up a vue project (though it might not be directly related to the issue) with typescript and then reusing its code and components. ...

Tips for maintaining the original data type while passing arguments to subsequent functions?

Is there a way to preserve generic type information when using typeof with functions or classes? For instance, in the code snippet below, variables exampleNumber and exampleString are of type Example<unknown>. How can I make them have types Example& ...

Launching Angular 7 on github pages

I am facing an issue with my Angular 7 app that features two routes: the main route and the 'article' route. While the app functions perfectly when tested locally, it fails to load the page's css when deployed on GitHub pages. I followed the ...

Issues with ng2-pdf-viewer arising in Angular versions 12 and above

Issue Detected If 'pdf-viewer' is an Angular component with the 'src' input, ensure it is included in this module. If 'pdf-viewer' is a Web Component, add 'CUSTOM_ELEMENTS_SCHEMA' to '@NgModule.schemas' of ...

The type 'string | undefined' cannot be assigned to type 'string'

I am facing a challenge in comparing two arrays, where one array is sourced from a third-party AWS service and its existence cannot be guaranteed. Despite my efforts to handle potential errors by incorporating return statements in my function calls, I con ...

Attempting to publish and install a unique angular2 component using NPM and Angular-CLI results in successful compilation only on the initial try

I am facing a peculiar and frustrating issue. The problem revolves around an Ng2 component I have developed called via-date-picker. My goal is to publish it on NPM so that it can be easily utilized in other projects. To achieve this, I converted it into a ...

Access to Firebase using Google authentication is currently restricted (permission denied)

Currently, I am utilizing Firebase to authenticate users with Google in my Angular project, "project1." When signing anonymously into Firebase, everything runs smoothly. However, if I attempt to sign in with Google using the popup feature, an error occurs: ...

Transforming a non-specific type into a container permits precarious assignments

I'm encountering an issue with the code snippet provided below. Despite having a specific type where Type<boolean> cannot be assigned to Type<true>, when wrapping it in an object type (BoxType as shown), suddenly BoxType<boolean> wro ...

Ensuring type safety for functions with multiple definitions in TypeScript

The code provided above is prone to failure. TypeScript may mistakenly infer the return type as `string`, allowing you to use the `charAt` method on it even though the actual type is `number`. Is there a solution to enhance the code in a way that TypeScri ...

Encountering a compiler error due to lack of patience for a promise?

In the current TypeScript environment, I am able to write code like this: async function getSomething():Promise<Something> { // ... } And later in my code: const myObject = getSomething(); However, when I attempt to use myObject at a later po ...

Unable to access a particular page on Angular

I am facing an issue while trying to navigate to a specific page in my angular app. I am attempting to navigate to (tabs/market-palce/fornecedores) from app.component.ts using my routing Modules. Error: Cannot match any routes. URL Segment: 'tabs/mar ...

Variations in default export behavior in Webpack when using Typescript versus Javascript

In my React application, I have a page that imports a component from another file. // Code snippet from IconPage.tsx import {AccountBalanceIcon} from './icons'; <AccountBalanceIcon /> // Code snippet from ./icons.ts export { default as A ...

An issue occurred when retrieving the input value during a (change) event within a nested *ngFor loop in Angular

Within my table, I have implemented a nested loop using the *ngFor directive: <tbody> <tr *ngFor="let dept of salesTarget; let i = index"> <td>{{dept.dept}}</td> <td *ngFor="let month of monthList; ...

Leveraging vue-devtools in combination with the composition-api while implementing a render function (such as JSX)

Ever since I made the transition to utilizing the composition-api and incorporating a render function (primarily leveraging JSX with TypeScript for type safety within the template), I've encountered an issue where vue-devtools cannot inspect the compu ...