Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | 1x 5x 5x 5x 5x 5x 5x 5x 4x 2x 2x 2x 1x 1x 4x 4x 5x 5x 5x 7x 7x 10x 4x 10x 7x 7x 3x 2x 3x 4x 4x 16x 16x 11x 5x 6x 6x 6x 6x | /**
* Copyright (c) Siemens 2016 - 2025
* SPDX-License-Identifier: MIT
*/
import { Directive, input, OnDestroy, signal } from '@angular/core';
import { of, Subject, switchMap } from 'rxjs';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { SelectItem, SelectOption } from '../si-select.types';
import { SelectOptionSource } from './si-select-option.source';
import { SI_SELECT_OPTIONS_STRATEGY, SiSelectOptionsStrategy } from './si-select-options-strategy';
@Directive({
// eslint-disable-next-line @angular-eslint/directive-selector
selector: 'si-select[optionSource]',
providers: [{ provide: SI_SELECT_OPTIONS_STRATEGY, useExisting: SiSelectLazyOptionsDirective }]
})
export class SiSelectLazyOptionsDirective<T> implements SiSelectOptionsStrategy<T>, OnDestroy {
/**
* {@inheritDoc SiSelectOptionsStrategy#loading}
* @defaultValue false
*/
readonly loading = signal(false);
/**
* {@inheritDoc SiSelectOptionsStrategy#rows}
* @defaultValue []
*/
readonly rows = signal<SelectItem<T>[]>([]);
/**
* {@inheritDoc SiSelectOptionsStrategy#selectedRows}
* @defaultValue []
*/
readonly selectedRows = signal<SelectOption<T>[]>([]);
readonly optionSource = input.required<SelectOptionSource<T>>();
private valueChange = new Subject<void>();
private filterChange = new Subject<string | undefined>();
constructor() {
this.filterChange
.pipe(
debounceTime(100),
switchMap(filterInput => {
if (filterInput) {
return this.optionSource().getOptionsForSearch!(filterInput);
} else {
const optionSource = this.optionSource();
if (optionSource.getAllOptions) {
return optionSource.getAllOptions();
} else {
return of(this.selectedRows());
}
}
})
)
.subscribe(rows => {
this.loading.set(false);
this.rows.set(rows);
});
}
ngOnDestroy(): void {
this.valueChange.next();
this.valueChange.complete();
this.filterChange.complete();
}
/** {@inheritDoc SiSelectOptionsStrategy.onValueChange} */
onValueChange(value: T[]): void {
this.valueChange.next();
// To prevent flickering, we need to check if we already got all the requested options.
// This should always be the case if the user selects one
const knownSelectedOptions = value
.map(
valueEntry =>
this.selectedRows().find(selected => this.valueEqual(selected.value, valueEntry)) ??
(this.rows().find(
selected => selected.type === 'option' && this.valueEqual(selected.value, valueEntry)
) as SelectOption<T>)
)
.filter(v => !!v);
const optionSource = this.optionSource();
if (knownSelectedOptions.length === value.length) {
if (optionSource.compareOptions) {
knownSelectedOptions.sort(optionSource.compareOptions);
}
this.selectedRows.set(knownSelectedOptions);
} else {
// We don't have all options, so we need to fetch them.
optionSource
.getOptionsForValues(value)
.pipe(takeUntil(this.valueChange))
.subscribe(selectedOptions => this.selectedRows.set(selectedOptions));
}
}
/** {@inheritDoc SiSelectOptionsStrategy.onFilter} */
onFilter(filterInput?: string): void {
this.filterChange.next(filterInput);
this.loading.set(true);
}
private valueEqual(a?: T, b?: T): boolean {
if (a === b) {
return true;
}
Iif (!a || !b) {
return false;
}
const optionSource = this.optionSource();
Iif (optionSource.valuesEqual) {
return optionSource.valuesEqual(a, b);
}
return false;
}
}
|