Интеграция Slim Sel ect в Angular-проекты опирается на классическую
модель: библиотека работает с нативным <select>, а
Angular управляет состоянием формы и жизненным циклом компонентов. Такой
подход позволяет совместить декларативность Angular с лёгкостью и
производительностью Slim Sel ect.
Установка Slim Sel ect выполняется через пакетный менеджер:
npm install slim-select
В Angular-проекте дополнительно подключается типовой стиль библиотеки:
// angular.json
"styles": [
"node_modules/slim-select/dist/slimselect.css"
]
Импорт JavaScript-модуля выполняется внутри компонента:
import SlimSelect fr om 'slim-select';
Angular-компонент служит контейнером для инициализации Slim Sel ect
после рендера DOM-элемента. Основная задача — получить ссылку на
<select> через ViewChild.
import { Component, ElementRef, AfterViewInit, ViewChild, OnDestroy } fr om '@angular/core';
import SlimSelect fr om 'slim-select';
@Component({
selector: 'app-select',
template: `
<sel ect #select>
<option value="1">Первый</option>
<option value="2">Второй</option>
<option value="3">Третий</option>
</select>
`
})
export class SelectComponent implements AfterViewInit, OnDestroy {
@ViewChild('sel ect', { static: true }) selectRef!: ElementRef<HTMLSelectElement>;
private slim!: SlimSelect;
ngAfterViewInit(): void {
this.slim = new SlimSelect({
select: this.selectRef.nativeElement
});
}
ngOnDestroy(): void {
this.slim?.destroy();
}
}
Ключевой момент заключается в инициализации в
ngAfterViewInit, поскольку DOM должен быть уже
сформирован.
Интеграция с реактивными формами требует синхронизации состояния Angular FormControl и внутреннего состояния Slim Select.
import { Component, AfterViewInit, OnDestroy, ViewChild, ElementRef } fr om '@angular/core';
import { FormControl, ReactiveFormsModule } fr om '@angular/forms';
import SlimSelect fr om 'slim-select';
@Component({
selector: 'app-select-reactive',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<sel ect #select [formControl]="control">
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
`
})
export class SelectReactiveComponent implements AfterViewInit, OnDestroy {
@ViewChild('select', { static: true }) selectRef!: ElementRef<HTMLSelectElement>;
control = new FormControl('a');
private slim!: SlimSelect;
ngAfterViewInit(): void {
this.slim = new SlimSelect({
select: this.selectRef.nativeElement,
value: this.control.value
});
this.control.valueChanges.subscribe(value => {
this.slim.setSelected(value as string);
});
}
ngOnDestroy(): void {
this.slim?.destroy();
}
}
Синхронизация работает в обе стороны: Angular обновляет Slim Select,
а изменения выбора можно пробрасывать через события Slim Select в
FormControl.
Slim Select предоставляет callback onChange, который
используется для синхронизации с Angular формами или локальным
состоянием компонента.
this.slim = new SlimSelect({
select: this.selectRef.nativeElement,
onChange: (info) => {
this.control.setValue(info.value);
}
});
info.value содержит выбранное значение, что позволяет
избежать ручного чтения DOM.
Angular часто работает с данными, полученными из API. Slim Select требует ручного обновления списка опций при изменении массива.
options = [
{ text: 'Первый', value: '1' },
{ text: 'Второй', value: '2' }
];
updateOptions(): void {
this.options = [
{ text: 'Обновлённый 1', value: '10' },
{ text: 'Обновлённый 2', value: '20' }
];
const select = this.selectRef.nativeElement;
select.innerHTML = this.options
.map(o => `<option value="${o.value}">${o.text}</option>`)
.join('');
this.slim.setData(this.options);
}
Важный аспект — Slim Select не отслеживает изменения Angular
автоматически, поэтому обновление данных требует явного вызова
setData.
Типовой сценарий — загрузка данных с сервера и последующая инициализация.
ngAfterViewInit(): void {
fetch('/api/options')
.then(res => res.json())
.then(data => {
const select = this.selectRef.nativeElement;
select.innerHTML = data
.map((d: any) => `<option value="${d.id}">${d.name}</option>`)
.join('');
this.slim = new SlimSelect({
select
});
});
}
При таком подходе инициализация происходит только после получения данных, что исключает необходимость последующего пересоздания экземпляра.
Template-driven подход требует минимальной логики в компоненте.
@Component({
selector: 'app-template-select',
template: `
<select [(ngModel)]="value" #select="ngModel">
<option value="x">X</option>
<option value="y">Y</option>
</select>
`
})
export class TemplateSelectComponent implements AfterViewInit {
@ViewChild('select', { static: true }) selectRef!: ElementRef<HTMLSelectElement>;
value = 'x';
private slim!: SlimSelect;
ngAfterViewInit(): void {
this.slim = new SlimSelect({
select: this.selectRef.nativeElement,
value: this.value,
onChange: (info) => {
this.value = info.value;
}
});
}
}
Angular и Slim Select в этом случае работают через общий источник
данных — переменную value.
Slim Select полностью поддерживает множественный выбор, но Angular требует массив значений.
<select #select multiple>
<option value="1">A</option>
<option value="2">B</option>
<option value="3">C</option>
</select>
control = new FormControl(['1', '3']);
ngAfterViewInit(): void {
this.slim = new SlimSelect({
select: this.selectRef.nativeElement,
value: this.control.value
});
this.control.valueChanges.subscribe(values => {
this.slim.setSelected(values as string[]);
});
}
Slim Select возвращает массив значений в onChange, что
напрямую соответствует Angular модели формы.
Angular уничтожает компоненты при навигации, поэтому экземпляр Slim Select должен быть корректно освобождён.
ngOnDestroy(): void {
if (this.slim) {
this.slim.destroy();
}
}
Отсутствие уничтожения приводит к утечкам памяти и сохранению слушателей событий.
При использовании *ngIf или динамического рендеринга
важно учитывать момент создания DOM.
<div *ngIf="visible">
<select #select>
<option value="1">One</option>
</select>
</div>
setTimeout(() => {
this.slim = new SlimSelect({
select: this.selectRef.nativeElement
});
});
Более корректный подход — отслеживание visible через
ngAfterViewChecked, но это требует защиты от повторной
инициализации.
Slim Select позволяет напрямую управлять выбранным значением без вмешательства DOM.
setValue(): void {
this.slim.setSelected('2');
}
Для множественного выбора:
this.slim.setSelected(['1', '3']);
Angular при этом должен синхронизировать своё состояние через
FormControl.
При использовании TypeScript удобно вводить строгую модель опций:
interface SelectOption {
text: string;
value: string;
}
options: SelectOption[] = [];
Slim Select принимает тот же формат, что позволяет избегать преобразований при обновлении данных:
this.slim.setData(this.options);
При частых обновлениях данных важно минимизировать пересоздание экземпляра Slim Select. Оптимальная стратегия:
ngAfterViewInitsetDataFormControlПри больших списках целесообразно использовать серверную фильтрацию, так как Slim Select не предназначен для виртуализации списка на уровне Angular CDK.
Slim Select работает вне зоны Angular, поэтому изменения,
происходящие в onChange, могут не инициировать
детектирование автоматически.
import { ChangeDetectorRef } fr om '@angular/core';
constructor(private cdr: ChangeDetectorRef) {}
this.slim = new SlimSelect({
select: this.selectRef.nativeElement,
onChange: (info) => {
this.control.setValue(info.value);
this.cdr.detectChanges();
}
});
Такой подход гарантирует актуализацию UI при внешних изменениях.
Slim Select часто используется как базовый слой для кастомных Angular компонентов селектов, где логика UI полностью переопределяется, а Slim Select отвечает за обработку выбора и управление состоянием списка.