# @eqproject/eqp-select

> Generic Material select

Latest version **21.0.0** (published 2026-04-29) · MIT license · 0 weekly downloads

## Install

```sh
npm install @eqproject/eqp-select
pnpm add @eqproject/eqp-select
yarn add @eqproject/eqp-select
bun add @eqproject/eqp-select
```

## Health

**Score 55/100 (C)** — status: active.

Positive: has types; esm support; no vulnerabilities.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 21.0.0 |
| Published | 2026-04-29 |
| First published | 2020-07-20 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 2 |
| Unpacked size | 168 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | EqProject |
| Maintainers | eqp, a.cipollone |

## Links

- npm: https://www.npmjs.com/package/@eqproject/eqp-select
- npm.io page: https://npm.io/package/@eqproject/eqp-select

## Dependencies (2)

- [tslib](https://npm.io/package/tslib.md) ^2.3.0
- [@eqproject/eqp-lookup](https://npm.io/package/@eqproject/eqp-lookup.md) latest

## Recent versions

- 21.0.0 (latest) — 2026-04-29
- 3.0.20 (v17-lts) — 2026-05-21
- 2.5.1 (v15-lts) — 2024-01-23
- 3.0.19 — 2026-02-18
- 3.0.18 — 2026-02-13
- 3.0.17 — 2026-02-05
- 3.0.16 — 2026-01-29
- 3.0.15 — 2026-01-29
- 3.0.14 — 2026-01-21
- 3.0.13 — 2025-12-09
- 3.0.12 — 2025-12-02
- 3.0.11 — 2025-11-27
- 3.0.10 — 2025-11-26
- 3.0.9 — 2025-11-25
- 3.0.8 — 2025-11-24
- … 64 more at https://npm.io/package/@eqproject/eqp-select/versions

## README

Table of contents
=================

  * [Getting started](#getting-started)
  * [API](#api)
  * [Reload data](#reload-data)
  * [Use Cases](#use-cases)
  * [Multi-language](#multi-language)
  * [Notes on behavior](#notes-on-behavior)
  * [Credits](#credits)

## Required
- [x] Angular Material installed and imported
- [x] `@eqproject/eqp-lookup` installed (eqp-select delegates rendering to eqp-lookup internally)

## Getting started
### Step 1: Install `eqp-select`:

#### NPM
```shell
npm install --save @eqproject/eqp-select
```
### Step 2: Import the EqpSelectModule and install :
```js
import { EqpSelectModule } from '@eqproject/eqp-select';

@NgModule({
  declarations: [AppComponent],
  imports: [EqpSelectModule],
  bootstrap: [AppComponent]
})
export class AppModule {}
```

## API
### Inputs
| Input  | Type | Default | Required | Description |
| ------------- | ------------- | ------------- | ------------- | ------------- |
| [placeholder] | `string` | `-` | no | Label shown as placeholder inside select. |
| [enumData] | `any` | `-` | no | Enum property to use in order to create a key-value array. Internally converted to `{ ID, Label }` pairs. When `enumData` is used, `includeFullObject` is ignored and the enum key (integer) is always emitted. |
| [enumDataToExclude] | `Array<any>` | `-` | no | Array of elements to exclude among those included inside the given Enum. Exclusion is performed by matching the key property (`arrayKeyProperty`) of each generated enum entry. |
| [arrayData] | `Array<any>` | `-` | no | Array of objects that select will show. The structure can be `{keyProperty: '', valueProperty: ''}` or an array of primitive elements. When primitives are passed, the component automatically wraps each as `{ ID: element, Label: element }` so that `arrayKeyProperty` and `arrayValueProperty` do not need to be redefined. |
| [arrayKeyProperty] | `string` | `'ID'` | no | Property name to use as the bound key. Supports dot-notation paths (e.g. `'Company.Id'`) to access nested object properties. Must be `'ID'` when `arrayData` is an array of primitive elements. |
| [arrayValueProperty] | `string` | `'Label'` | no | Property name to use as the displayed label. Supports dot-notation paths (e.g. `'Company.Name'`) to access nested object properties. Must be `'Label'` when `arrayData` is an array of primitive elements. |
| [ngModelOptions] | `any` | `-` | no | Options to pass to ngModel. |
| [ngModelInput] | `any` | `-` | no | Binded property of object. Supports two-way binding with `[(ngModelInput)]`. When this value changes at runtime (and differs from the previous value), the component automatically refreshes its internal selection. |
| [formGroupInput] | `any` | `-` | no | FormGroup name, in case of formControlNameInput, that property need to be repeated for EVERY eqp-select used. |
| [formControlNameInput] | `any` | `-` | no | formControlName defined in ts formGroup control. When provided together with `formGroupInput`, the component subscribes to `statusChanges` to keep `isDisabled` in sync with the FormControl's disabled state. |
| [isRequired] | `boolean` | `false` | no | Set select as required. |
| [isDisabled] | `boolean` | `false` | no | Disable the select. When a FormControl is used, the disabled state is also kept in sync automatically via `statusChanges`. |
| [isMultiLanguage] | `boolean` | `false` | no | If true, see section and examples about use, if not interested in use, set to false or don't write it. Requires the `TranslateService` instance to be passed to `TranslateSelectHelper.loadTranslateService()` before the component initializes. |
| [multilanguagePrefixKey] | `string` | `-` | no | Prefix of the key used to translate the property. The full translation key is built as `multilanguagePrefixKey + enumValue`. Include any separators (e.g. `'ENUMS.GENDER.'`). |
| [isAlphabeticalOrderable] | `boolean` | `false` | no | Define if the datasource will be ordered alphabetically. Alphabetical ordering is also re-applied after each search when `isSearchable` is true. |
| [suffixIcon] | `string` | `-` | no | String containing the icon (mat-icon) that will appear after the label of the selected object(s). Visible in the selected-item chip inside the input. |
| [prefixIcon] | `string` | `-` | no | String containing the icon (mat-icon) that will appear before the label of the selected object(s). Visible in the selected-item chip inside the input. |
| [isMultiSelect] | `boolean` | `false` | no | Define if the Select allows multiple selection. In multi-select mode the value container has a maximum height of 90px with vertical scroll so that it does not expand the form layout unboundedly. |
| [showCancelButton] | `boolean` | `true` | no | Property to define if the button to clear the selection is visible. In multi-select mode, clears the array (setting it to empty); in single-select mode, sets the value to null. |
| [isSearchable] | `boolean` | `false` | no | Defines if it's possible to type inside select and search among items. The search filters `arrayValueProperty` (including nested dot-notation paths). During search, `onSearching` is set to `true` to show a loading state. |
| [noResultSearchText] | `string` | `'Nessun elemento trovato'` | no | Custom text when filter returns empty result. |
| [isReadonly] | `boolean` | `false` | no | Set select as readonly. |
| [isSearchWhileComposing] | `boolean` | `true` | no | Whether items should be filtered while composition started (IME input). |
| [appendToInput] | `string` | `-` | no | Input used when the select is contained inside a dialog or tab and there are visualization problems. Useful value: `'body'`. |
| [dropdownPosition] | `'auto' \| 'top' \| 'bottom'` | `'auto'` | no | Specifies the position of the dropdown. |
| [selectOnTab] | `boolean` | `true` | no | Select marked dropdown item using tab. |
| [selectAll] | `boolean` | `false` | no | Add an option inside the dropdown that allows the user to select all the elements. |
| [selectAllText] | `string` | `'Seleziona tutto'` | no | Text displayed in the select all option. |
| [selectableGroupAsModel] | `boolean` | `false` | no | Indicates whether to select all children or group itself when the select-all option is used. |
| [clearAllText] | `string` | `'Elimina'` | no | Text displayed as tooltip when hovering the clear button. |
| [customOption] | `TemplateRef<any>` | `-` | no | Custom template for the option inside the dropdown panel. The template context exposes `item` and `index`. |
| [customLabel] | `TemplateRef<any>` | `-` | no | Custom template for the selected item's label. When `prefixIcon` or `suffixIcon` is set and no `customLabel` is provided, the component uses an internal template that renders the icons alongside the label and an optional `Total` badge. |
| [includeFullObject] | `boolean` | `true` | no | When `true` (default), the full item object from `arrayData` is emitted on selection. When `false`, only the key value (`arrayKeyProperty`) is emitted. This input is ignored when `enumData` is used (the enum key is always emitted) and when `arrayData` is a primitive array (the primitive value itself is always emitted). |
| [appearance] | `string` | `''` | no | Controls the Angular Material form-field appearance. Accepts the same values as `mat-form-field` (`'outline'`, `'fill'`, `'standard'`). Passed through to the underlying `eqp-lookup`. |
| [class] | `string` | `-` | no | CSS class applied to the ng-select element for custom styling. |


N.B. One of `enumData` or `arrayData` is required.

### Outputs
| Output| Event arguments | Required | Description |
| ------------- | ------------- | ------------- | ------------- |
| (ngModelInputChange) | `any` | `-` | Invoked when the value changes and the ngModelInput is binded. When items are grouped (selectAll) it returns the group 'exploded'. The emitted value respects the `includeFullObject` setting. |
| (formControlChange) | `any` | `-` | Invoked when the value changes and a form control is binded. Also marks the FormControl as dirty and touched. When items are grouped (selectAll) it returns the group 'exploded'. |

### Public methods (accessible via `@ViewChild`)

| Method | Description |
| ------------- | ------------- |
| `reloadData()` | Re-initializes the internal datasource from the current `arrayData` or `enumData`. Call this when the datasource changes programmatically. Triggers change detection after a 50ms delay to allow the underlying ng-select to re-render. |


## Reload data

When your datasource changes, you can update the datasource inside the select calling the function "reloadData", as follows:

```js
  reload() {
    this.selectToReload.reloadData();
  }
```
It will work for both arrayData and enumData changes.

## Use cases
### Case 1: Select with arrayData and ngModelInput

Define selector in html for normal use
```html
  <eqp-select
    [arrayData]="dataSource"
    [isSearchable]="true"
    [(ngModelInput)]="selectWithArrayData"
    placeholder="Seleziona elemento"
    (ngModelInputChange)="selectChange($event)"
    [includeFullObject]="false"
    [arrayKeyProperty]="'Key'"
    [arrayValueProperty]="'Description'">
  </eqp-select>
```

Define variables and functions in ts file
```ts

  dataSource : Array<{Key: string, Description: string}>;
  selectWithArrayData: any;

  ...

  selectChange(event) {
    // Body
  }
```

### Case 2: Multi-select with enumData and form control
Define selector in HTML
```html
    <eqp-select
        [enumData]="testEnum"
        [isMultiSelect]="true"
        placeholder="Seleziona elemento"
        (formControlChange)="selectChange($event)"
        [formGroupInput]="formGroup"
        [formControlNameInput]="'selectWithEnumData'">
    </eqp-select>
```

Define variables and functions in ts file
```ts

  testEnum = TestEnum;
  formGroup: FormGroup;

  ...

  createForm() {
    this.formGroup = this.formBuilder.group({
      selectWithEnumData: []
    })
  }

  ...

  selectChange(event) {
    // Body
  }
```

### Case 3: Multi-select with select all
Define selector in HTML
```html
    <eqp-select
        [enumData]="testEnum"
        (formControlChange)="selectChange($event)"
        [formGroupInput]="formGroup"
        [formControlNameInput]="'selectWithEnumDataAndSelectAll'"
        [isMultiSelect]="true"
        [selectAll]="true"
        [selectableGroupAsModel]="true"
        placeholder="Seleziona elemento">
    </eqp-select>
```

Define variables and functions in ts file
```ts

  testEnum = TestEnum;
  formGroup: FormGroup;

  ...

  createForm() {
    this.formGroup = this.formBuilder.group({
      selectWithEnumDataAndSelectAll: []
    })
  }

  ...

  selectChange(event) {
    // Body
  }
```

### Case 4: Select with primitive array

When `arrayData` is a plain array of strings or numbers, the component automatically wraps each element as `{ ID: element, Label: element }`. There is no need to define `arrayKeyProperty` or `arrayValueProperty`.

```html
  <eqp-select
    [arrayData]="['Option A', 'Option B', 'Option C']"
    [(ngModelInput)]="selectedValue"
    placeholder="Scegli un'opzione">
  </eqp-select>
```

### Case 5: Select with nested property paths

Both `arrayKeyProperty` and `arrayValueProperty` support dot-notation to access nested object properties.

```html
  <eqp-select
    [arrayData]="employees"
    [arrayKeyProperty]="'Person.Id'"
    [arrayValueProperty]="'Person.FullName'"
    [(ngModelInput)]="selectedEmployee"
    [includeFullObject]="true"
    placeholder="Seleziona dipendente">
  </eqp-select>
```

```ts
  employees = [
    { Person: { Id: 1, FullName: 'Mario Rossi' }, Department: 'IT' },
    { Person: { Id: 2, FullName: 'Luigi Bianchi' }, Department: 'HR' }
  ];
```

### Case 6: Select with custom option and label templates

Use `customOption` and `customLabel` to fully control how items are rendered in the dropdown and the selection input respectively.

```html
  <ng-template #optionTpl let-item="item">
    <div class="d-flex align-items-center gap-2">
      <mat-icon>person</mat-icon>
      <span>{{ item.FullName }}</span>
      <small class="text-muted">{{ item.Department }}</small>
    </div>
  </ng-template>

  <ng-template #labelTpl let-item="item" let-clear="clear">
    <span>{{ item.FullName }}</span>
    <span class="ng-value-icon right" (click)="clear(item)">×</span>
  </ng-template>

  <eqp-select
    [arrayData]="employees"
    [arrayKeyProperty]="'Id'"
    [arrayValueProperty]="'FullName'"
    [customOption]="optionTpl"
    [customLabel]="labelTpl"
    [(ngModelInput)]="selectedEmployee"
    placeholder="Seleziona">
  </eqp-select>
```

### Case 7: Select with prefix/suffix icons

When `prefixIcon` or `suffixIcon` is defined, the component uses an internal label template that renders Material icons alongside the label. If an item also has a `Total` property, a secondary badge is shown next to the label.

```html
  <eqp-select
    [arrayData]="categories"
    [arrayKeyProperty]="'ID'"
    [arrayValueProperty]="'Name'"
    [prefixIcon]="'label'"
    [suffixIcon]="'arrow_right'"
    [(ngModelInput)]="selectedCategory"
    placeholder="Categoria">
  </eqp-select>
```

### Case 8: Select with excluded enum values

Use `enumDataToExclude` to hide specific enum values from the list while keeping the rest.

```html
  <eqp-select
    [enumData]="statusEnum"
    [enumDataToExclude]="[StatusEnum.Deleted, StatusEnum.Archived]"
    [(ngModelInput)]="selectedStatus"
    placeholder="Stato">
  </eqp-select>
```

### Case 9: Select emitting only the key value

By default, eqp-select emits the full item object when `arrayData` is used. Set `[includeFullObject]="false"` to emit only the key value instead.

```html
  <eqp-select
    [arrayData]="users"
    [arrayKeyProperty]="'ID'"
    [arrayValueProperty]="'FullName'"
    [includeFullObject]="false"
    (ngModelInputChange)="onUserSelected($event)"
    placeholder="Utente">
  </eqp-select>
```

```ts
  onUserSelected(userId: number) {
    // event is just the ID, not the full user object
  }
```

## Multi-language

To use multilanguage, set `isMultiLanguage` to `true` and pass a prefix string that matches the path in your translation JSON. Before the component initializes, inject the `TranslateService` instance into the `TranslateSelectHelper` singleton:

```ts
  constructor(private translateSelectHelper: TranslateSelectHelper, private translate: TranslateService) {}

  ngOnInit(): void {
    this.translateSelectHelper.loadTranslateService(this.translate);
  }
```

Then use the component with the `multilanguagePrefixKey` pointing to the correct section of your JSON:

```html
    <eqp-select
        [enumData]="testEnum"
        [isMultiSelect]="true"
        placeholder="Seleziona elemento"
        (formControlChange)="selectChange($event)"
        [formGroupInput]="formGroup"
        [formControlNameInput]="'selectWithEnumData'"
        [isMultiLanguage]="true"
        [multilanguagePrefixKey]="'ENUMS.TEST.'">
    </eqp-select>
```

The translation key for each enum entry is built as `multilanguagePrefixKey + enumStringValue`. For example, given prefix `'ENUMS.TEST.'` and an enum value `'Active'`, the lookup key will be `'ENUMS.TEST.Active'`.

If `isMultiLanguage` is `true` but `TranslateService` has not been injected into `TranslateSelectHelper`, the component throws an error at init: `Configurazione multilingua errata. Passare l'istanza del translateService`.

## Notes on behavior

### ControlValueAccessor support

`eqp-select` implements `ControlValueAccessor` and registers itself with `NG_VALUE_ACCESSOR`. This means it can be used directly with `[(ngModel)]` or `[formControl]` in addition to the `formGroupInput`/`formControlNameInput` pair.

### Value extraction logic (includeFullObject, enumData, primitive arrays)

The value that is emitted by `ngModelInputChange` and `formControlChange` follows this logic:

- **enumData**: always emits the enum key (integer). `includeFullObject` is ignored.
- **arrayData with primitives** (strings/numbers): always emits the primitive value. `includeFullObject` is ignored.
- **arrayData with objects, `includeFullObject = true`** (default): emits the full item object.
- **arrayData with objects, `includeFullObject = false`**: emits only `item[arrayKeyProperty]`.

### Initial value resolution

On `ngAfterViewInit`, the component reads the initial value from either `ngModelInput` or the bound `FormControl` and resolves the corresponding full object(s) from `listOfElements` by matching on `arrayKeyProperty`. This ensures that pre-populated forms display the correct label rather than a raw key.

If the initial value was a primitive key (e.g. an ID from a REST response) and `includeFullObject` is `true`, the component also re-emits `ngModelInputChange` with the resolved full object so the parent has access to all item properties from the start.

### Automatic datasource refresh on input changes

`ngOnChanges` watches both `arrayData` and `ngModelInput` using deep equality (`JSON.stringify` comparison). If either changes after initialization, the datasource or selected value is automatically updated without requiring a manual `reloadData()` call. Manual `reloadData()` is only needed when you need to force a refresh for other reasons (e.g. after an async operation that doesn't change the reference).

### arrayKeyProperty validation

If `enumData` is not set and `arrayKeyProperty` evaluates to an empty/falsy value at init time, the component throws: `La proprietà arrayKeyProperty è obbligatoria`. Since the default is `'ID'`, this only occurs if the property is explicitly set to null or an empty string.

### FormControl disabled state sync

When `formGroupInput` and `formControlNameInput` are provided, the component subscribes to the FormControl's `statusChanges` observable and keeps `isDisabled` in sync. This means disabling the control programmatically via `formGroup.get('key').disable()` will immediately reflect in the visual state of the select without needing to rebind `[isDisabled]`.

### Multi-select value container

In multi-select mode (`isMultiSelect = true`), the selected-value container is capped at 90px height with vertical overflow-y scroll. This prevents the input from growing to fill the page when many items are selected.

## Credits
This library has been developed by EqProject SRL, for more info contact: info@eqproject.it

---
_Source: https://npm.io/package/@eqproject/eqp-select · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
