nm-select
src/app/shared/widgets/select/select.component.ts
changeDetection | ChangeDetectionStrategy.OnPush |
selector | nm-select |
styleUrls | select.component.scss |
templateUrl | ./select.component.html |
constructor(_widgetframeService: WidgetframeService, dialog: DialogService, translateService: TranslateService, localStorageService: LocalStorageService, _changeDetectorRef: ChangeDetectorRef, _progressbarService: ProgressbarService)
|
|||||||||||||||||||||
Parameters :
|
Private addOptions | |||||||||
addOptions(data: any, configuration: WidgetConfig)
|
|||||||||
Parameters :
Returns :
void
|
clearCustomValue |
clearCustomValue()
|
Returns :
void
|
clearMultiCustomValues | ||||||
clearMultiCustomValues(selection: any)
|
||||||
Parameters :
Returns :
void
|
Protected configureWidget | ||||||
configureWidget(configuration: WidgetConfig
|
||||||
Decorators : WidgetConfigure
|
||||||
Parameters :
Returns :
void
|
Private extract |
extract(value: any, property: string)
|
Returns :
string
|
filterOptions | ||||
filterOptions(event?: )
|
||||
Parameters :
Returns :
void
|
groupFilterOptions | ||||
groupFilterOptions(event?: )
|
||||
Parameters :
Returns :
void
|
ngAfterViewInit |
ngAfterViewInit()
|
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
onChange |
onChange()
|
Returns :
void
|
onCloseSelect |
onCloseSelect()
|
Returns :
void
|
onOpen | ||||
onOpen(open: )
|
||||
Parameters :
Returns :
void
|
onOpenedChange | ||||
onOpenedChange(open: )
|
||||
Parameters :
Returns :
void
|
onSelectionChange | ||||
onSelectionChange(event: )
|
||||
Parameters :
Returns :
void
|
Private selectDefault |
selectDefault()
|
Returns :
void
|
selectedOptionType | ||||||
selectedOptionType(optionType: string)
|
||||||
Parameters :
Returns :
void
|
selectGroupIdentifier | ||||
selectGroupIdentifier(event: )
|
||||
Parameters :
Returns :
void
|
sendGroupedSelection | ||||
sendGroupedSelection(groupIdentifier: )
|
||||
Parameters :
Returns :
void
|
setDefaultCustomValue | ||||||
setDefaultCustomValue(defaultSelection: any)
|
||||||
Parameters :
Returns :
void
|
storeAndSend |
storeAndSend()
|
Returns :
void
|
Public _id |
_id:
|
Decorators : WidgetId
|
Public addAllOption |
addAllOption:
|
Type : boolean
|
Default value : false
|
Public addEmptyOption |
addEmptyOption:
|
Type : Boolean
|
Default value : false
|
Public addLabel |
addLabel:
|
Type : boolean
|
Default value : false
|
Private addOptionFiltered |
addOptionFiltered:
|
Type : boolean
|
Default value : false
|
Public allOption |
allOption:
|
Type : object
|
Default value : { value: "ALL", prompt: "All" }
|
Public autofocus |
autofocus:
|
Type : Boolean
|
Default value : false
|
Private clearOptionsOnReset |
clearOptionsOnReset:
|
Type : boolean
|
Public configuration |
configuration:
|
Type : WidgetConfig<SelectConfiguration>
|
Decorators : WidgetConfiguration
|
Public cssClass |
cssClass:
|
Type : string
|
Public customValueMode |
customValueMode:
|
Type : boolean
|
Default value : false
|
Public customValueObject |
customValueObject:
|
Type : object
|
Default value : { value: "", prompt: "" }
|
Public dataFromStream |
dataFromStream:
|
Type : Boolean
|
Default value : false
|
Public dialog |
dialog:
|
Type : DialogService
|
Public disabled |
disabled:
|
Type : Subject<any>
|
Default value : new BehaviorSubject<boolean>(false)
|
Decorators : WidgetInput
|
Disable the selection |
Public enableGroupSelection |
enableGroupSelection:
|
Type : boolean
|
Public enableSelectSearch |
enableSelectSearch:
|
Type : boolean
|
Public filterCtrl |
filterCtrl:
|
Type : FormControl
|
Default value : new FormControl()
|
Public floatLabel |
floatLabel:
|
Type : string
|
Default value : ""
|
Public grouped |
grouped:
|
Type : boolean
|
Public groupFilterCtrl |
groupFilterCtrl:
|
Type : FormControl
|
Default value : new FormControl()
|
Public hideSelect |
hideSelect:
|
Type : boolean
|
Default value : false
|
Public initialOptions |
initialOptions:
|
Type : any[]
|
Default value : []
|
Public isMandatory |
isMandatory:
|
Type : Boolean
|
Default value : false
|
Public labelName |
labelName:
|
Type : string
|
matSelect |
matSelect:
|
Type : MatSelect
|
Decorators : ViewChild
|
Public multi |
multi:
|
Type : boolean
|
Public needsConfirmation |
needsConfirmation:
|
Type : Boolean
|
Default value : false
|
Private newSelection |
newSelection:
|
Type : any
|
Public options |
options:
|
Type : any[]
|
Default value : []
|
Private optionsInput |
optionsInput:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetInput
|
Alternative to uri input for options |
Public placeholder |
placeholder:
|
Type : string
|
Public priorSelection |
priorSelection:
|
Type : string
|
Public required |
required:
|
Type : boolean
|
Private requiredChannel |
requiredChannel:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetInput
|
Updates required status of select |
Private resetChannel |
resetChannel:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetInput
|
Trigger the current selection to reset when triggered |
Private resetToDefault |
resetToDefault:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetInput
|
Reset selection to default value |
Public searchPlaceholder |
searchPlaceholder:
|
Type : string
|
Public searchString |
searchString:
|
Type : string
|
Public selectedArray |
selectedArray:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetOutput
|
Outputs selection when panel is closed, used mainly for multi-select |
Private selectedDefault |
selectedDefault:
|
Type : any
|
Public selectedRow |
selectedRow:
|
Type : any
|
Private selectHide |
selectHide:
|
Type : Subject<boolean>
|
Default value : new Subject<boolean>()
|
Decorators : WidgetInput
|
Hide the selection |
Public selection |
selection:
|
Type : any
|
Private selectionEntry |
selectionEntry:
|
Type : LocalStorageEntry
|
Private selectionTypeEntry |
selectionTypeEntry:
|
Type : LocalStorageEntry
|
selectSearch |
selectSearch:
|
Type : MatSelectSearchComponent
|
Decorators : ViewChild
|
Public selectWidth |
selectWidth:
|
Type : string
|
Public shouldLabelFloat |
shouldLabelFloat:
|
Type : Boolean
|
Default value : false
|
Private subscription |
subscription:
|
Type : Subscription
|
Public translateService |
translateService:
|
Type : TranslateService
|
Public unsubscribe |
unsubscribe:
|
Default value : NgUnsubscribe.create()
|
Private uri |
uri:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetInput
|
The uri to fetch the options from |
import {
BehaviorSubject,
combineLatest as observableCombineLatest,
ReplaySubject,
Subject,
Subscription,
} from "rxjs";
import { map, mergeMap, takeUntil, filter } from "rxjs/operators";
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnDestroy,
ViewChild,
} from "@angular/core";
import { WidgetframeService } from "../widgetframe/widgetframe.service";
import { getOrDefault, WidgetConfig } from "../widget.configuration";
import {
WidgetComponent,
WidgetConfiguration,
WidgetConfigure,
WidgetId,
WidgetInput,
WidgetOutput,
} from "../widget.metadata";
import { NgUnsubscribe } from "../../ng-unsubscribe";
import { TranslateService } from "@ngx-translate/core";
import { ConfirmationDialogComponent } from "../../components/dialog/confirmationDialog.component";
import { FormControl } from "@angular/forms";
import * as uriTemplates_ from "uri-templates";
import {
LocalStorageEntry,
LocalStorageService,
} from "../../components/local-storage/local-storage.service";
import {
DeletionMode,
Scope,
} from "../../components/local-storage/local-storage-constants";
import { ProgressbarService } from "../../components/progressbar/progressbar.service";
import { MatSelectSearchComponent } from "ngx-mat-select-search";
import { deepCopy } from "../../components/util/util.service";
import { MatSelect } from "@angular/material/select";
import { DialogService } from "../../components/dialog/dialog.service";
export interface SelectConfiguration {
/**
* css class that should be applied for the selection.
*/
cssClass?: string;
/**
* Title of component.
*/
title?: string;
/**
* A flag that controls if the selection is mandatory or not. @default(false)
*/
isMandatory?: boolean;
/**
* Allow multiple selection. @default(false)
*/
multi?: boolean;
/**
* Allow group selection. @default(false)
*/
grouped?: boolean;
/**
* The placeholder for the value.
*/
placeholder?: string;
/**
* Whether the label should try to float or not. @default(false)
*/
shouldLabelFloat?: boolean;
/**
* Add or Hide label. @default(false)
*/
addLabel?: boolean;
/**
* The label name.
*/
labelName?: string;
/**
* Enable or Disable custom mode. @default(false)
*/
customValueMode?: boolean;
/**
* Enable or Disable clear options selected on reset. @default(false)
*/
clearOptionsOnReset?: boolean;
/**
* Enable or Disable required selection. @default(false)
*/
required?: boolean;
/**
* Whether the label for fields should by default float.
*/
floatLabel?: string;
/**
* Values to be selected.
*/
values?: any[];
/**
* Enable or disable group selection. @default(false)
*/
enableGroupSelection?: boolean;
/**
* Storage key to keep the selected option.
*/
"local-storage-key": string;
/**
* Storage key type to keep the selected option.
*/
"local-storage-key-type"?: string;
/**
* Allow to fetch data from a stream, if true then data are fetched from input channel. @default(false)
*/
"data-from-stream"?: boolean;
/**
* Fetch data of the select from a url.
*/
"remote-data-url"?: string;
/**
* The datatype of the values that will be selected.
*/
"remote-data-dataType"?: any;
/**
* The represented object value of the selection on emit.
*/
"remote-data-value"?: string;
/**
* The value that is shown to the user on selection.
*/
"remote-data-prompt"?: string;
/**
* Creating a small circle, typically containing a number or other short set of characters.
*/
"remote-data-badge"?: string;
/**
* Extract value of UOM.
*/
"remote-data-measurementUnit"?: string;
/**
* A flag to add 'ALL' as an option.
*/
"add-all-option"?: boolean;
/**
* A flag to add empty option.
*/
"remote-data-add-empty-option"?: boolean;
/**
* Title of the info-dialog. Should be a translation-key
*/
infoTitle?: string;
/**
* Text of the info-dialog. Should be a translation-key
*/
infoText?: string;
/**
* Sets width of select
*/
selectWidth?: string;
/**
* Allow select search @default(true)
*/
enableSelectSearch?: boolean;
/**
* Auto focus field.
*/
autofocus?: boolean;
}
const uriTemplates = uriTemplates_;
export const GROUP_VALUE = "groupValue";
export const OPTION_VALUE = "optionValue";
@WidgetComponent("nm-select")
@Component({
selector: "nm-select",
templateUrl: "./select.component.html",
styleUrls: ["./select.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SelectWidgetComponent implements OnDestroy, AfterViewInit {
@WidgetId()
public _id;
@WidgetConfiguration()
public configuration: WidgetConfig<SelectConfiguration>;
/**
* Outputs the selected value, whenever it changes. This will emit a single object, or an array (if in multi mode). The content is the property value of the options
*/
@WidgetOutput()
public selectedValue: Subject<any> = new ReplaySubject<any>(1);
/**
* Outputs the selected objects, whenever it changes. This will emit a single object, or an array (if in multi mode). The content is the whole option
*/
@WidgetOutput()
public selectedObject: Subject<any> = new BehaviorSubject<any>(null);
/**
* Takes a value as input that, if not null, will trigger a confirmation dialog on change before the changes are emitted
*/
@WidgetInput()
public payload: Subject<any> = new Subject<any>();
/**
* Takes the current locale as input. This is needed to reload the options if the language changes
*/
@WidgetInput()
public selectedUiLocale: Subject<any> = new ReplaySubject<any>(1);
/**
* Trigger the current selection to reset when triggered
*/
@WidgetInput("reset")
private resetChannel: Subject<any> = new Subject<any>();
/**
* Set the current selection to the inputted value when triggered
*/
@WidgetInput("defaultSelection")
private defaultSelection: Subject<any> = new ReplaySubject<any>(1);
/**
* The uri to fetch the options from
*/
@WidgetInput("uri")
private uri: Subject<any> = new Subject<any>();
/**
* Disable the selection
*/
@WidgetInput("disabled")
public disabled: Subject<any> = new BehaviorSubject<boolean>(false);
/**
* Alternative to uri input for options
*/
@WidgetInput("optionsInput")
private optionsInput: Subject<any> = new Subject<any>();
/**
* Hide the selection
*/
@WidgetInput("selectHide")
private selectHide: Subject<boolean> = new Subject<boolean>();
/**
* Reset selection to default value
*/
@WidgetInput("resetToDefault")
private resetToDefault: Subject<any> = new Subject<any>();
/**
* Outputs selected value type only if enableGroupSelection configuration is set to true
*/
@WidgetOutput("selectedValueType")
private selectedValueType: Subject<any> = new ReplaySubject<any>(1);
/**
* Outputs selection when panel is closed, used mainly for multi-select
*/
@WidgetOutput()
public selectedArray: Subject<any> = new Subject<any>();
/**
* Updates required status of select
*/
@WidgetInput("required")
private requiredChannel: Subject<any> = new Subject<any>();
@ViewChild("selectSearch") selectSearch: MatSelectSearchComponent;
@ViewChild("matSelect") matSelect: MatSelect;
public multi: boolean;
public selection: any;
public placeholder: string;
public shouldLabelFloat: Boolean = false;
public autofocus: Boolean = false;
public floatLabel: string = "";
public priorSelection: string;
private selectionEntry: LocalStorageEntry;
private selectionTypeEntry: LocalStorageEntry;
public isMandatory: Boolean = false;
public needsConfirmation: Boolean = false;
public addEmptyOption: Boolean = false;
public options: any[] = [];
public initialOptions: any[] = [];
public searchString: string;
public filterCtrl: FormControl = new FormControl();
public unsubscribe = NgUnsubscribe.create();
public dataFromStream: Boolean = false;
public grouped: boolean;
public groupFilterCtrl: FormControl = new FormControl();
public addAllOption: boolean = false;
private addOptionFiltered: boolean = false;
public hideSelect: boolean = false;
public labelName: string;
public addLabel: boolean = false;
public allOption = { value: "ALL", prompt: "All" };
private selectedDefault: any;
public selectedRow: any;
public cssClass: string;
private newSelection: any;
public customValueMode: boolean = false;
public searchPlaceholder: string;
public customValueObject = { value: "", prompt: "" };
private clearOptionsOnReset: boolean;
public required: boolean;
public selectWidth: string;
public enableSelectSearch: boolean;
public enableGroupSelection: boolean;
private subscription: Subscription;
constructor(
protected _widgetframeService: WidgetframeService,
public dialog: DialogService,
public translateService: TranslateService,
protected localStorageService: LocalStorageService,
protected _changeDetectorRef: ChangeDetectorRef,
protected _progressbarService: ProgressbarService
) {}
onSelectionChange(event) {
this.newSelection = event.source.value;
if (this.enableGroupSelection) {
this.selectedOptionType(OPTION_VALUE);
}
}
onChange() {
if (!this.needsConfirmation) {
if (
this.isMandatory &&
this.multi &&
this.selection.includes(this.allOption.value) &&
this.newSelection !== this.allOption.value
) {
this.selection = [this.newSelection];
}
if (
this.isMandatory &&
this.multi &&
this.addAllOption &&
(this.newSelection === this.allOption.value ||
this.selection.length === 0)
) {
this.selection = [this.allOption.value];
}
if (
this.isMandatory &&
this.multi &&
!this.addAllOption &&
this.selection.length === 0
) {
if (this.grouped) {
this.selection = [this.options[0].groupValues[0]["value"]];
} else {
this.selection = [this.options[0]["value"]];
}
}
if (this.customValueMode) {
this.clearCustomValue();
}
this.storeAndSend();
} else {
let dialogRef = this.dialog.open(ConfirmationDialogComponent, {
height: "400px",
width: "400px",
});
dialogRef.componentInstance["title"] =
this.translateService.instant("hl.confirmation");
dialogRef.componentInstance["message"] = this.translateService.instant(
"message.confirmation.unsavedChanges"
);
dialogRef.componentInstance["buttonAcceptTitle"] =
this.translateService.instant("button.accept");
dialogRef.componentInstance["buttonCancelTitle"] =
this.translateService.instant("button.cancel");
dialogRef.afterClosed().subscribe((confirmed) => {
if (confirmed) {
if (
this.isMandatory &&
this.multi &&
this.selection.includes(this.allOption.value) &&
this.newSelection !== this.allOption.value
) {
this.selection = [this.newSelection];
}
if (
this.isMandatory &&
this.multi &&
this.addAllOption &&
(this.newSelection === this.allOption.value ||
this.selection.length === 0)
) {
this.selection = [this.allOption.value];
}
if (
this.isMandatory &&
this.multi &&
!this.addAllOption &&
this.selection.length === 0
) {
if (this.grouped) {
this.selection = [this.options[0].groupValues[0]["value"]];
} else {
this.selection = [this.options[0]["value"]];
}
}
this.storeAndSend();
} else {
this.selection = this.priorSelection;
this._changeDetectorRef.markForCheck();
}
});
}
}
onOpenedChange(open) {
open ? this.onOpen : this.onCloseSelect();
}
onOpen(open) {
this.priorSelection = this.selection;
}
storeAndSend() {
if (this.selectionEntry) {
if (this.multi) {
this.selectionEntry.value = JSON.stringify(this.selection);
} else {
this.selectionEntry.value = this.selection;
}
}
let groupIdentifier;
if (this.grouped && this.selection.includes("_" + GROUP_VALUE)) {
groupIdentifier = this.selection.split("_" + GROUP_VALUE)[0];
this.selectedValue.next(groupIdentifier);
} else {
this.selectedValue.next(this.selection);
}
if (this.options && this.grouped) {
this.sendGroupedSelection(groupIdentifier);
} else if (this.options) {
this.selectedRow = this.options.filter(
(option) => option.value == this.selection
)[0];
let filtered;
if (this.multi) {
filtered = this.initialOptions.filter(
(option) =>
this.selection.findIndex((val) => val == option.value) >= 0
);
} else {
filtered = this.options.filter(
(option) => option.value == this.selection
);
}
if (this.customValueMode) {
this.selectedObject.next(
filtered.length > 0 || !this.selection || this.selection.length == 0
? filtered
: [{ value: this.selection, prompt: this.selection }]
);
} else {
this.selectedObject.next(filtered);
}
}
this._changeDetectorRef.markForCheck();
}
selectGroupIdentifier(event) {
this.selectedOptionType(GROUP_VALUE);
}
@WidgetConfigure()
protected configureWidget(configuration: WidgetConfig<SelectConfiguration>) {
this.cssClass = configuration.configuration["cssClass"]
? configuration.configuration["cssClass"]
: "";
let title = configuration.configuration["title"];
this.isMandatory = configuration.configuration["isMandatory"]
? configuration.configuration["isMandatory"]
: false;
this.multi = configuration.configuration["multi"]
? configuration.configuration["multi"]
: false;
this.grouped = configuration.configuration["grouped"]
? configuration.configuration["grouped"]
: false;
this.placeholder = configuration.configuration["placeholder"];
this.shouldLabelFloat = configuration.configuration["shouldLabelFloat"]
? configuration.configuration["shouldLabelFloat"]
: false;
this.autofocus = configuration.configuration["autofocus"]
? configuration.configuration["autofocus"]
: false;
this.floatLabel = configuration.configuration.floatLabel;
if (configuration.configuration["local-storage-key"]) {
this.selectionEntry = this.localStorageService.getLocalStorageEntry(
configuration.configuration["local-storage-key"],
Scope.GLOBAL,
DeletionMode.NEVER
);
}
if (configuration.configuration["local-storage-key-type"]) {
this.selectionTypeEntry = this.localStorageService.getLocalStorageEntry(
configuration.configuration["local-storage-key-type"],
Scope.GLOBAL,
DeletionMode.NEVER
);
}
this.addEmptyOption = configuration.configuration[
"remote-data-add-empty-option"
]
? configuration.configuration["remote-data-add-empty-option"]
: false;
this.dataFromStream =
configuration.configuration["data-from-stream"] !== undefined
? configuration.configuration["data-from-stream"]
: false;
this.addLabel = configuration.configuration["addLabel"];
this.labelName = configuration.configuration["labelName"];
this.addAllOption =
configuration.configuration["add-all-option"] !== undefined
? configuration.configuration["add-all-option"]
: false;
this.enableGroupSelection = configuration.configuration[
"enableGroupSelection"
]
? configuration.configuration["enableGroupSelection"]
: false;
this.customValueMode = getOrDefault(
configuration.configuration.customValueMode,
false
);
this.searchPlaceholder = this.customValueMode
? "placeholder.search-and-save"
: "placeholder.search";
this.clearOptionsOnReset = getOrDefault(
configuration.configuration.clearOptionsOnReset,
false
);
this.required = getOrDefault(configuration.configuration.required, false);
this.selectWidth = configuration.configuration.selectWidth;
this.enableSelectSearch = getOrDefault(
configuration.configuration.enableSelectSearch,
true
);
if (configuration.configuration["values"]) {
this.options = configuration.configuration["values"];
this.initialOptions = configuration.configuration["values"];
this.selectedDefault = configuration.configuration["default-value"];
if (this.selectionEntry && this.selectionEntry.exists()) {
const value = this.selectionEntry.value;
if (this.multi === Array.isArray(value)) {
this.selection = value;
}
} else if (
this.isMandatory &&
!configuration.configuration["remote-data-url"] &&
!this.multi
) {
this.selection = this.addAllOption
? this.allOption.value
: this.options[0]["value"];
} else if (
!this.isMandatory &&
!this.multi &&
configuration.configuration["default-value"]
) {
this.selection = configuration.configuration["default-value"];
}
this.storeAndSend();
this.defaultSelection
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((defaultSelection) => {
this.selection = defaultSelection;
this.storeAndSend();
this._changeDetectorRef.markForCheck();
});
} else {
if (configuration.configuration["remote-data-url"]) {
this.selectedUiLocale
.pipe(
takeUntil(this.unsubscribe),
map((locale) => {
let uriParams = Object.assign({}, { locale: locale });
return uriParams;
}),
map((uriParams) => {
let template = uriTemplates(
configuration.configuration["remote-data-url"]
);
return template.fill(uriParams);
}),
mergeMap((uri) => {
return this._widgetframeService.getData(uri);
})
)
.subscribe(
(data) => {
if (data && !(data.type && data.type === "ERROR")) {
this.addOptions(data, configuration);
}
},
(err) => {
console.log(err);
}
);
}
if (configuration.configuration["remote-data-url-from-stream"]) {
this.subscription = observableCombineLatest(
this.uri.asObservable(),
this.selectedUiLocale.asObservable(),
function (uri, selectedUiLocale) {
let template = uriTemplates(uri);
let uriParams = Object.assign({}, { locale: selectedUiLocale });
return template.fill(uriParams);
}
)
.pipe(
mergeMap((uri) => {
return this._widgetframeService.getData(uri);
})
)
.subscribe(
(data) => {
if (data && !(data.type && data.type === "ERROR")) {
this.addOptions(data, configuration);
}
},
(err) => {
console.log(err);
}
);
}
if (configuration.configuration["data-from-stream"]) {
this.optionsInput
.pipe(takeUntil(this.unsubscribe))
.subscribe((data) => {
if (data) {
this.addOptions(data, configuration);
}
});
}
}
this.filterCtrl.valueChanges
.pipe(takeUntil(this.unsubscribe))
.subscribe((val) => {
this.filterOptions();
});
this.groupFilterCtrl.valueChanges
.pipe(takeUntil(this.unsubscribe))
.subscribe(() => {
this.groupFilterOptions();
});
this.selectHide
.pipe(takeUntil(this.unsubscribe))
.subscribe((selectHide) => {
this.hideSelect = selectHide;
this._changeDetectorRef.markForCheck();
});
this.payload.pipe(takeUntil(this.unsubscribe)).subscribe((data) => {
if (data != null) {
this.needsConfirmation = true;
} else {
this.needsConfirmation = false;
}
});
this.resetChannel.pipe(takeUntil(this.unsubscribe)).subscribe((reset) => {
if (this.multi) {
this.selection = [];
} else {
this.selection = null;
}
if (this.selectionEntry) {
this.selectionEntry.clear();
}
if (this.selectionTypeEntry) {
this.selectionTypeEntry.clear();
}
if (this.clearOptionsOnReset) {
this.initialOptions = [];
this.options = [];
}
this.selectedValue.next(this.selection);
this.selectedObject.next(null);
this._changeDetectorRef.markForCheck();
});
this.resetToDefault
.pipe(takeUntil(this.unsubscribe))
.subscribe((resetToDefault) => {
if (this.grouped) {
this.selection = this.addAllOption
? this.allOption.value
: this.options[0]["groupValues"][0].value;
} else {
if (this.selectedDefault) {
this.selection = this.selectedDefault;
} else if (this.addAllOption) {
this.selection = this.allOption.value;
} else {
this.selection = this.options[0]["value"];
}
}
if (this.multi && this.selection && !Array.isArray(this.selection)) {
this.selection = [this.selection];
}
this.storeAndSend();
this._changeDetectorRef.markForCheck();
});
this.requiredChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((required) => {
this.required = required;
this._changeDetectorRef.markForCheck();
});
}
private addOptions(data: any, configuration: WidgetConfig) {
this.options = [];
if (this.grouped && data._embedded) {
for (var entry of data._embedded[
configuration.configuration["remote-data-dataType"]
]) {
let option = {};
option["groupName"] = entry.groupName;
if (entry.groupIdentifier) {
option["groupIdentifier"] = entry.groupIdentifier.concat(
"_" + GROUP_VALUE
);
}
option["groupValues"] = [];
entry.groupValues.forEach((value) => {
let groupValue = {};
groupValue["value"] =
value[configuration.configuration["remote-data-value"]].toString();
groupValue["prompt"] =
value[configuration.configuration["remote-data-prompt"]].toString();
groupValue["_original"] = value;
option["groupValues"].push(groupValue);
});
this.options.push(option);
}
} else if (data._embedded) {
let badgeProperty = configuration.configuration["remote-data-badge"];
let valueProperty = configuration.configuration["remote-data-value"];
let promptProperty = configuration.configuration["remote-data-prompt"];
let uomProperty =
configuration.configuration["remote-data-measurementUnit"];
for (var entry of data._embedded[
configuration.configuration["remote-data-dataType"]
]) {
let option: any = {};
option["value"] = this.extract(entry, valueProperty);
option["prompt"] = this.extract(entry, promptProperty);
if (badgeProperty) {
let chips: any = entry[badgeProperty];
if (chips) {
if (typeof chips === "string") {
let chipElement: any = {};
chipElement.value = chips;
chipElement.color = "primary";
option.chipList = [chipElement];
} else if (chips instanceof Array) {
let chipList = [];
chips.forEach((chip) => {
let chipElement: any = {};
chipElement.value = chip.value;
if (chip.color) {
chipElement.color = chip.color;
} else {
chipElement.color = "primary";
}
chipList.push(chipElement);
});
option.chipList = chipList;
}
}
}
if (uomProperty) {
option["measurementUnit"] = this.extract(entry, uomProperty);
}
if (option["_actions"] !== null) {
option["_actions"] = entry["_actions"];
}
option["_original"] = entry;
this.options.push(option);
}
} else {
this.options = data.values;
}
this.selectedValue.next(null);
if (this.selectionEntry && this.selectionEntry.exists()) {
if (this.multi) {
this.selection = JSON.parse(this.selectionEntry.value);
} else {
this.selection = this.selectionEntry.value;
if (
this.isMandatory &&
(!this.selection ||
this.selection === "null" ||
!this.options?.some((option) => option.value == this.selection))
) {
this.selectDefault();
}
}
this.storeAndSend();
} else {
this.selectDefault();
}
this.initialOptions = [].concat(this.options);
this.defaultSelection
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((defaultSelection) => {
if (this.customValueMode && defaultSelection) {
this.setDefaultCustomValue(defaultSelection);
}
this.selection = defaultSelection;
this.selectedDefault = this.selection;
this.storeAndSend();
});
}
private selectDefault() {
if (this.isMandatory && !this.multi && !this.grouped) {
this.selection = this.addAllOption
? this.allOption.value
: getOrDefault(this.options[0]?.["value"], []);
this.storeAndSend();
} else if (this.isMandatory && !this.multi && this.grouped) {
this.selection = this.addAllOption
? this.allOption.value
: getOrDefault(this.options[0]?.["groupValues"][0].value, []);
this.storeAndSend();
} else if (this.isMandatory && this.multi) {
this.selection = this.addAllOption
? [this.allOption.value]
: getOrDefault(this.options[0]?.["groupValues"][0].value, []);
this.storeAndSend();
}
}
private extract(value: any, property: string): string {
if (property && value.hasOwnProperty(property)) {
let result = value[property];
return result ? result.toString() : null;
}
return null;
}
filterOptions(event?) {
if (!this.options) {
return;
}
// get the search keyword
let search = event ? event : this.filterCtrl.value;
if (search) {
if (
this.addAllOption &&
this.translateService
.instant(this.allOption["prompt"])
.toLowerCase()
.indexOf(search.toLowerCase().trim()) === -1
) {
this.addAllOption = false;
this.addOptionFiltered = true;
}
this.options = this.initialOptions.filter(
(item) =>
this.translateService
.instant(item["prompt"])
.toLowerCase()
.indexOf(search.toLowerCase().trim()) > -1
);
} else {
if (this.addOptionFiltered) {
this.addAllOption = true;
this.addOptionFiltered = false;
}
this.options = this.initialOptions.slice();
}
// this.options = search ? this.initialOptions.filter(item => {
// return this.translateService.instant(item["prompt"]).toLowerCase().indexOf(search) > -1;
// }) : this.initialOptions.slice();
}
groupFilterOptions(event?) {
if (!this.options) {
return;
}
let search = event ? event : this.groupFilterCtrl.value;
if (search) {
this.options = [];
if (
this.addAllOption &&
this.translateService
.instant(this.allOption["prompt"])
.toLowerCase()
.indexOf(search.toLowerCase().trim()) === -1
) {
this.addAllOption = false;
this.addOptionFiltered = true;
}
this.initialOptions.forEach((item, index) => {
let filteredOptions = [];
filteredOptions = item.groupValues.filter(
(value) =>
this.translateService
.instant(value["prompt"])
.toLowerCase()
.indexOf(search.toLowerCase().trim()) > -1
);
if (filteredOptions.length != 0) {
let groupAttribute = JSON.parse(
JSON.stringify(this.initialOptions[index])
);
groupAttribute.groupValues = filteredOptions;
this.options.push(groupAttribute);
}
});
} else {
if (this.addOptionFiltered) {
this.addAllOption = true;
this.addOptionFiltered = false;
}
this.options = this.initialOptions.slice();
}
}
sendGroupedSelection(groupIdentifier) {
if (
this.multi &&
this.addAllOption &&
this.selection.includes(this.allOption.value)
) {
this.selectedObject.next([this.allOption]);
} else if (this.multi) {
let selectedOptions = [];
this.options.forEach((item) => {
let filteredOptions = item.groupValues.filter((option) =>
this.selection.includes(option.value)
);
if (filteredOptions.length > 0) {
selectedOptions = selectedOptions.concat(filteredOptions);
}
});
this.selectedObject.next(selectedOptions);
} else if (this.addAllOption && this.selection === this.allOption.value) {
this.selectedObject.next(this.allOption);
} else {
for (let item of this.options) {
if (groupIdentifier && item.groupIdentifier === groupIdentifier) {
this.selectedObject.next({
value: item.groupIdentifier,
prompt: item.groupName,
});
break;
}
let filteredOption = item.groupValues.find(
(option) => option.value === this.selection
);
if (filteredOption) {
this.selectedObject.next(filteredOption);
break;
}
}
}
}
onCloseSelect() {
if (this.customValueMode && this.multi) {
this.clearMultiCustomValues(this.selection);
}
this.searchString = "";
this.options = this.initialOptions.slice();
this.selectedArray.next(this.selection);
}
ngAfterViewInit() {
if (this.selectSearch && this.customValueMode) {
this.selectSearch.searchSelectInput.nativeElement.onkeydown = (event) => {
let valueEntered;
if (this.grouped) {
valueEntered = this.groupFilterCtrl.value;
} else {
valueEntered = this.filterCtrl.value;
}
if (
event.keyCode == 13 &&
valueEntered &&
this.options.filter(
(option) => option.prompt.toLowerCase() === valueEntered.trim()
).length == 0
) {
event.stopPropagation();
this.clearCustomValue();
this.customValueObject.value = this.customValueObject.prompt =
valueEntered;
this.initialOptions.push(deepCopy(this.customValueObject));
if (this.multi) {
if (this.selection) {
this.selection.push(valueEntered);
} else {
this.selection = [valueEntered];
}
} else {
this.selection = valueEntered;
}
this.storeAndSend();
this.matSelect.close();
}
};
}
}
clearCustomValue() {
if (!this.multi && this.customValueObject.value) {
let customValueIndex = this.initialOptions.indexOf(
this.customValueObject
);
this.initialOptions.splice(customValueIndex, 1);
this.customValueObject.value = this.customValueObject.prompt = "";
}
}
clearMultiCustomValues(selection: any) {
let customAddedObjects = this.initialOptions.filter(
(option) => !option._original
);
customAddedObjects.forEach((addedObject) => {
if (!selection.includes(addedObject.value)) {
let removedObjectIndex = this.initialOptions.findIndex(
(option) => option.value === addedObject.value
);
this.initialOptions.splice(removedObjectIndex, 1);
}
});
this.options = this.initialOptions;
}
setDefaultCustomValue(defaultSelection: any) {
if (this.multi && !this.grouped) {
defaultSelection.forEach((selection) => {
if (
this.initialOptions.filter((option) => option.value === selection)
.length == 0
) {
this.initialOptions.push({ value: selection, prompt: selection });
}
});
this.options = this.initialOptions.slice();
this.clearMultiCustomValues(defaultSelection);
} else if (
!this.multi &&
!this.grouped &&
!this.options.filter((option) => option.value === defaultSelection).length
) {
this.customValueObject.value = this.customValueObject.prompt =
defaultSelection;
this.initialOptions.push(deepCopy(this.customValueObject));
this.options = this.initialOptions.slice();
}
}
selectedOptionType(optionType: string) {
if (this.selectionTypeEntry) {
this.selectionTypeEntry.value = optionType;
}
this.selectedValueType.next(optionType);
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
this.unsubscribe.next();
this.unsubscribe.destroy();
this._progressbarService.requestFinished();
}
}
<div *ngIf="!hideSelect" class="nm-select mat-body">
<div *ngIf="addLabel" class="nm-select__label">
<nm-ellipsis [content]="labelName | translate"></nm-ellipsis>
</div>
<mat-form-field
[style.width]="selectWidth"
[floatLabel]="floatLabel"
[ngClass]="{ '--withLabel': addLabel }"
*ngIf="!grouped; else groupedSelect"
>
<mat-select
style="width: 100%"
[disabled]="disabled | async"
[(ngModel)]="selection"
#model="ngModel"
panelClass="testClass"
(openedChange)="onOpenedChange($event)"
(onClose)="onCloseSelect()"
[multiple]="multi"
(selectionChange)="onChange()"
[placeholder]="placeholder | translate"
[name]="configuration.configuration['name']"
[id]="configuration.configuration['name']"
[panelClass]="cssClass"
[required]="required"
[nmAutofocus]="configuration.configuration.autofocus"
#matSelect
>
<mat-select-trigger *ngIf="selectedRow">
{{ selectedRow.prompt | translate }}
<div class="nm-select-chips" *ngIf="selectedRow.chipList">
<nm-chip
*ngFor="let chip of selectedRow.chipList"
class="nm-select-chips__chip"
[modifier]="chip.color"
[content]="chip.value"
[toUpperCase]="true"
>
</nm-chip>
</div>
</mat-select-trigger>
<mat-option *ngIf="enableSelectSearch">
<ngx-mat-select-search
[formControl]="filterCtrl"
placeholderLabel="{{ searchPlaceholder | translate }}"
noEntriesFoundLabel="{{ 'select.no.options' | translate }}"
(keydown.escape)="selectSearch.matSelect.focus()"
#selectSearch
></ngx-mat-select-search>
</mat-option>
<mat-option *ngIf="addEmptyOption" (click)="model.reset()"></mat-option>
<mat-option
*ngIf="addAllOption"
[value]="allOption.value"
(onSelectionChange)="onSelectionChange($event)"
>
{{ allOption.prompt | translate }}
</mat-option>
<mat-option
*ngFor="let option of options"
[value]="option.value"
(onSelectionChange)="onSelectionChange($event)"
[disabled]="option.disabled"
>
<span>
{{ option.prompt | translate }}
<div class="nm-select-chips" *ngIf="option.chipList">
<nm-chip
*ngFor="let chip of option.chipList"
class="nm-select-chips__chip"
[modifier]="chip.color"
[content]="chip.value"
[toUpperCase]="true"
>
</nm-chip>
</div>
</span>
</mat-option>
</mat-select>
</mat-form-field>
<ng-template #groupedSelect>
<mat-form-field
[style.width]="selectWidth"
[floatLabel]="floatLabel"
[ngClass]="{ '--withLabel': addLabel }"
>
<mat-select
style="width: 100%"
[(ngModel)]="selection"
(selectionChange)="onChange()"
#model="ngModel"
[disabled]="disabled | async"
(openedChange)="onOpenedChange($event)"
shouldLabelFloat="shouldLabelFloat"
[placeholder]="placeholder | translate"
[name]="configuration.configuration['name']"
[id]="configuration.configuration['name']"
[panelClass]="cssClass"
[multiple]="multi"
[required]="required"
#matSelect
>
<mat-option *ngIf="enableSelectSearch">
<ngx-mat-select-search
[formControl]="groupFilterCtrl"
placeholderLabel="{{ searchPlaceholder | translate }}"
noEntriesFoundLabel="{{ 'select.no.options' | translate }}"
#selectSearch
></ngx-mat-select-search>
</mat-option>
<mat-option *ngIf="addEmptyOption" (click)="model.reset()"></mat-option>
<ng-container *ngIf="!enableGroupSelection">
<mat-option
*ngIf="addAllOption"
[value]="allOption.value"
(onSelectionChange)="onSelectionChange($event)"
>
{{ allOption.prompt | translate }}
</mat-option>
<mat-optgroup *ngFor="let group of options" [label]="group.groupName">
<mat-option
*ngFor="let option of group.groupValues"
[value]="option.value"
(onSelectionChange)="onSelectionChange($event)"
[disabled]="option.disabled"
>
{{ option.prompt | translate }}
</mat-option>
</mat-optgroup>
</ng-container>
<ng-container *ngIf="enableGroupSelection">
<mat-option
*ngIf="addAllOption"
[value]="allOption.value"
(onSelectionChange)="
$event.source.selected ? onSelectionChange($event) : ''
"
>
{{ allOption.prompt | translate }}
</mat-option>
<ng-container *ngFor="let group of options">
<mat-option
[value]="group.groupIdentifier"
(onSelectionChange)="
$event.source.selected ? selectGroupIdentifier($event) : ''
"
[disabled]="group.disabled"
>
{{ group.groupName | translate }}
</mat-option>
<mat-option
*ngFor="let option of group.groupValues"
[value]="option.value"
style="padding-left: 32px"
(onSelectionChange)="
$event.source.selected ? onSelectionChange($event) : ''
"
[disabled]="option.disabled"
>
{{ option.prompt | translate }}
</mat-option>
</ng-container>
</ng-container>
</mat-select>
</mat-form-field>
</ng-template>
<nm-help-icon
class="nm-help-icon"
*ngIf="configuration.configuration.infoText"
[info-title]="configuration.configuration.infoTitle"
[info-placement]="'bottom'"
[info-text]="configuration.configuration.infoText"
>
</nm-help-icon>
</div>