nm-search-advanced
src/app/shared/widgets/search/advanced/search-advanced.component.ts
OnInit
OnChanges
AfterViewInit
providers |
EditAttributeService
LookupService
|
selector | nm-search-advanced |
styleUrls | search-advanced.component.scss |
templateUrl | ./search-advanced.component.html |
constructor(localstorageService: LocalStorageService, widgetframeService: WidgetframeService, dialogService: DialogService, translateService: TranslateService, datetimeAdapter: DateTimeAdapter
|
||||||||||||||||||||||||||||||
Parameters :
|
attributeData
|
Used to update missing/existing attributes.
Type:
Default value: |
attributeTypesToAddMultiple
|
Enables adding an item several times in search attributes dialog.
Type: |
attributeUrl
|
Url for fetching attributes. |
dataInput
|
Used to transfer search form fields to dataOutput channel.
Type:
Default value: |
dialogWithActionMenu
|
Enables/disables action menu for setting the default attributes
Type:
Default value: |
dynamicHeight
|
Enables/disables dynamicHeight calculation.
Type:
Default value: |
dynamicHeightAdditionalHeight
|
Viewport height in px that is not covered by attribute-container.
Type:
Default value: |
enableRoleBasedSearchTemplates
|
enable to create role-based search templates
Type:
Default value: |
fields
|
Pre-defined static fields.
Type: |
fieldsTemplate
|
Used to update search form fields according to selected search favorite template.
Type:
Default value: |
itemHeight
|
Sets height for a single attribute field.
Type:
Default value: |
links
|
Urls for fetching needed data. i.e: lookup values, search modes. |
listHeight
|
Sets height for attribute fields list.
Type:
Default value: |
listsearch
|
Enables/disables list search mode.
Type:
Default value: |
locale
|
used for loading lookups. |
lookups
|
Pre-defined static lookup fields. |
prefix
|
Local storage prefix for widget wrapping the component.
Default value: |
reloadLookups
|
Reloads lookup fields.
Type:
Default value: |
reset
|
Triggers reset of search filters.
Type:
Default value: |
resetFavorites
|
Resets favorites value.
Type:
Default value: |
searchFavorites
|
Enables/disables search favorites.
Type: |
searchModeChanged
|
Gets changes of search mode
Type:
Default value: |
searchTemplatesUri
|
Used to get search favorites templates.
Default value: |
selectFilterParams
|
Used to set attribute scopes in search attributes dialog.
Type: |
attributes
|
Emits changed attributes. $event type: ReplaySubject
|
dataOutput
|
Emits search form fields to initialize search favorites dialog. $event type: Subject
|
expandToggle
|
used for toggle-expanded-event $event type: Subject
|
listvalueOutput
|
List value output in list search mode. $event type: ReplaySubject
|
search
|
Emits form values. $event type: Subject
|
templateOutput
|
Emits search template parameter. $event type: ReplaySubject
|
Public attributeSupportsRange | ||||
attributeSupportsRange(attribute: )
|
||||
Parameters :
Returns :
boolean
|
Private buildFieldsAndAttributes |
buildFieldsAndAttributes()
|
Returns :
void
|
Private checkIsAttributeEmpty | ||||||
checkIsAttributeEmpty(attribute: Attribute)
|
||||||
Parameters :
Returns :
boolean
|
editAttribute | ||||||
editAttribute(attribute: Attribute)
|
||||||
Parameters :
Returns :
void
|
editField | ||||||
editField(field: FormWidgetField)
|
||||||
Parameters :
Returns :
void
|
Public formcontrolFor | ||||||
formcontrolFor(field: FormWidgetField)
|
||||||
Parameters :
Returns :
any
|
Public formcontrolNameFor | ||||||
formcontrolNameFor(field: FormWidgetField)
|
||||||
Parameters :
Returns :
string
|
Public getHeight |
getHeight()
|
Returns :
any
|
getLookups | ||||
getLookups(key: )
|
||||
Parameters :
Returns :
any[]
|
Public getTooltip | ||||||
getTooltip(attribute: Attribute)
|
||||||
Parameters :
Returns :
string
|
Public getTooltipValue | ||||||
getTooltipValue(field: FormWidgetField)
|
||||||
Parameters :
Returns :
any
|
Public getTooltipValueForAttribute | ||||||
getTooltipValueForAttribute(attribute: Attribute)
|
||||||
Parameters :
Returns :
any
|
Public includedInTooltip | ||||||
includedInTooltip(field: FormWidgetField)
|
||||||
Parameters :
Returns :
any
|
Public isFieldVisible | ||||||
isFieldVisible(field: FormWidgetField)
|
||||||
Parameters :
Returns :
any
|
Private loadLookups | ||||
loadLookups(locale: )
|
||||
Parameters :
Returns :
void
|
Private mapToMultiLookup | ||||
mapToMultiLookup(attribute: )
|
||||
Parameters :
Returns :
void
|
ngAfterViewInit |
ngAfterViewInit()
|
Returns :
void
|
ngOnChanges | ||||||
ngOnChanges(changes: SimpleChanges)
|
||||||
Parameters :
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
Public onInputEvent | ||||||
onInputEvent(event: FormEventPayload)
|
||||||
Parameters :
Returns :
void
|
Public openAttributeSelection |
openAttributeSelection()
|
Returns :
void
|
Private rangeAllowed | ||||||
rangeAllowed(field: FormWidgetField)
|
||||||
Parameters :
Returns :
boolean
|
rangeChange | |||||||||
rangeChange(event: any, field: FormWidgetField)
|
|||||||||
Parameters :
Returns :
void
|
Private recoverChangedAttributes |
recoverChangedAttributes()
|
Returns :
void
|
Private recoverFormValue |
recoverFormValue()
|
Returns :
void
|
Private recoverFromData | |||||||||
recoverFromData(data: Attribute[], attributes: Attribute[])
|
|||||||||
Parameters :
Returns :
void
|
Public save |
save()
|
Returns :
void
|
Private setFormValue | ||||
setFormValue(value: )
|
||||
Parameters :
Returns :
void
|
Public showLabel | ||||
showLabel(attribute: )
|
||||
Parameters :
Returns :
boolean
|
Public submit |
submit()
|
Returns :
void
|
templateChanged | ||||||
templateChanged(template: any)
|
||||||
Parameters :
Returns :
void
|
toggleExpanded |
toggleExpanded()
|
Returns :
void
|
Private updateIgxForContainerHeight |
updateIgxForContainerHeight()
|
Returns :
void
|
Private updateLookups | |||||||||
updateLookups(keys: string[], locale: )
|
|||||||||
Parameters :
Returns :
void
|
Public _editAttributeService |
_editAttributeService:
|
Type : EditAttributeService
|
Public attributes |
attributes:
|
Type : Attribute[]
|
Default value : []
|
Private attributesEntry |
attributesEntry:
|
Type : LocalStorageEntry
|
Public changedAttributes |
changedAttributes:
|
Type : []
|
Default value : []
|
Private changedAttributesEntry |
changedAttributesEntry:
|
Type : LocalStorageEntry
|
Public containerHeight |
containerHeight:
|
Type : string
|
Public containerHeightPx |
containerHeightPx:
|
Type : string
|
Public currentLocale |
currentLocale:
|
Public expanded |
expanded:
|
Type : boolean
|
Default value : true
|
Private expandedEntry |
expandedEntry:
|
Type : LocalStorageEntry
|
Private favoritesChanged |
favoritesChanged:
|
Type : boolean
|
Default value : false
|
Public fieldsAndAttributes |
fieldsAndAttributes:
|
Type : []
|
Default value : []
|
Public fieldsVisible |
fieldsVisible:
|
Type : object
|
Default value : {}
|
Private fieldsVisibleEntry |
fieldsVisibleEntry:
|
Type : LocalStorageEntry
|
Public fieldsVisibleFavorite |
fieldsVisibleFavorite:
|
Private fieldsVisibleFavoriteEntry |
fieldsVisibleFavoriteEntry:
|
Type : LocalStorageEntry
|
Private forceEmit |
forceEmit:
|
Default value : new Subject<void>()
|
Public form |
form:
|
Type : FormGroup
|
Private formValueEntry |
formValueEntry:
|
Type : LocalStorageEntry
|
Public formValues |
formValues:
|
Type : object
|
Default value : {}
|
Public hasSystemAttributes |
hasSystemAttributes:
|
Type : boolean
|
Default value : false
|
Private ignoreChanges |
ignoreChanges:
|
Type : boolean
|
Default value : false
|
Private localeChannel |
localeChannel:
|
Type : BehaviorSubject<any>
|
Default value : new BehaviorSubject<any>(
"de-DE"
)
|
Public lookups |
lookups:
|
Type : object
|
Default value : {}
|
Public rangeModes |
rangeModes:
|
Type : object
|
Default value : {}
|
Private rangeModesEntry |
rangeModesEntry:
|
Type : LocalStorageEntry
|
Public uniqueID |
uniqueID:
|
Type : string
|
Default value : "identifier"
|
Private unsubscribe |
unsubscribe:
|
Default value : NgUnsubscribe.create()
|
locale | ||||
setlocale(value: )
|
||||
used for loading lookups.
Parameters :
Returns :
void
|
import {
AfterViewInit,
ChangeDetectorRef,
Component,
HostListener,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
} from "@angular/core";
import { EditAttributeService } from "../../../components/edit-attribute/edit-attribute.service";
import {
DeletionMode,
Scope,
} from "../../../components/local-storage/local-storage-constants";
import {
LocalStorageEntry,
LocalStorageService,
} from "../../../components/local-storage/local-storage.service";
import { FormControl, FormGroup } from "@angular/forms";
import { debounceTime, filter, map, take, takeUntil } from "rxjs/operators";
import { NgUnsubscribe } from "../../../ng-unsubscribe";
import {
angularWidgetBridgeInput,
calcHeight,
createUID,
deepCopy,
notNullOrEmpty,
} from "../../../components/util/util.service";
import { WidgetframeService } from "../../widgetframe/widgetframe.service";
import { MatDialog } from "@angular/material/dialog";
import { SearchAttributeSelectionDialog } from "../attribute-selection/search-attribute-selection.component";
import * as _ from "lodash";
import { TranslateService } from "@ngx-translate/core";
import { BehaviorSubject, combineLatest, ReplaySubject, Subject } from "rxjs";
import { DateTimeAdapter } from "ng-pick-datetime";
import { SearchAttributeConfigurationDialog } from "../attribute-configuration/search-attribute-configuration.component";
import * as uriTemplates_ from "uri-templates";
import { Attribute } from "../../../components/edit-attribute/attribute";
import { SelectFilterParams } from "../../interfaces/list.interfaces";
import { LookupService } from "../../../components/lookup/lookup.service";
import { ScrollService } from "../../../components/scroll";
import { DialogService } from "../../../components/dialog";
const uriTemplates = uriTemplates_;
export class FormWidgetField {
identifier: string;
description: string;
type: string;
lookupSource?: string;
/**
* `true` enables the range selection, `false` disables it
*
* @deprecated use {@link range} instead
*/
allowRange?: boolean;
/**
* modifies the range selection behaviour
* `always` restricts the field to always have a range selection,
* `choose` allows the user to switch between the modes,
* while `never` only allows for single value search
*/
range: "never" | "choose" | "always";
defaultVisible?: boolean;
eventField?: boolean;
default?: string;
basic?: boolean;
formatDate?: boolean;
min?: number | null;
max?: number | null;
autofocus?: boolean;
}
export class FormLookup {
label: string;
collectionName: string;
valueField: string;
}
export class FormEventPayload {
field: string;
payload: any;
}
const RANGE_ATTRIBUTES = [
"PLAIN_STRING",
"INTEGER_NUMBER",
"DATE",
"DATE_TIME",
"PERCENTAGE",
"DECIMAL_NUMBER",
];
@Component({
selector: "nm-search-advanced",
templateUrl: "./search-advanced.component.html",
styleUrls: ["./search-advanced.component.scss"],
providers: [EditAttributeService, LookupService],
})
export class SearchAdvancedComponent
implements OnInit, OnChanges, AfterViewInit
{
/**
* Url for fetching attributes.
*/
@Input() public attributeUrl;
/**
* Local storage prefix for widget wrapping the component.
*/
@Input() public prefix = "search";
/**
* Pre-defined static fields.
*/
@Input() public fields: FormWidgetField[];
/**
* Urls for fetching needed data. i.e: lookup values, search modes.
*/
@Input() public links;
/**
* Used to set attribute scopes in search attributes dialog.
*/
@Input() public selectFilterParams: SelectFilterParams;
/**
* Triggers reset of search filters.
*/
@Input() public reset = new Subject();
/**
* Pre-defined static lookup fields.
*/
@Input("lookups") public lookupsInput;
/**
* Used to get search favorites templates.
*/
@Input() public searchTemplatesUri = "/api/core/search-templates";
/**
* Used to update search form fields according to selected search favorite template.
*/
@Input() public fieldsTemplate = new Subject();
/**
* Used to transfer search form fields to dataOutput channel.
*/
@Input() public dataInput = new Subject();
/**
* Enables/disables search favorites.
*/
@Input() public searchFavorites: boolean;
/**
* Enables adding an item several times in search attributes dialog.
*/
@Input() public attributeTypesToAddMultiple: string[];
/**
* Emits search form fields to initialize search favorites dialog.
*/
@Output() public dataOutput = new Subject();
/**
* Emits changed attributes.
*/
@Output("attributes") public attributesOutput = new ReplaySubject<any>(1);
/**
* Emits form values.
*/
@Output("search") public change = new Subject();
/**
* Resets favorites value.
*/
@Input() public resetFavorites = new Subject();
/**
* Used to update missing/existing attributes.
*/
@Input("attributeData") public attributeData = new ReplaySubject<any>(1);
/**
* Reloads lookup fields.
*/
@Input() public reloadLookups = new ReplaySubject<any>(1);
/**
* Sets height for attribute fields list.
*/
@Input() public listHeight: string = "340px";
/**
* Sets height for a single attribute field.
*/
@Input() public itemHeight: string = "44px";
/**
* Enables/disables list search mode.
*/
@Input() public listsearch: boolean = false;
/**
* Gets changes of search mode
*/
@Input() public searchModeChanged = new ReplaySubject<any>(1);
/**
* Enables/disables action menu for setting the default attributes
*/
@Input() public dialogWithActionMenu: boolean = false;
/**
* List value output in list search mode.
*/
@Output() public listvalueOutput = new ReplaySubject<any>(1);
/**
* Emits search template parameter.
*/
@Output() public templateOutput = new ReplaySubject<any>(1);
/**
* used for toggle-expanded-event
*/
@Output("expandToggle")
public expandedToggle = new Subject();
// Array that combines fields and attributes so we can iterate them in an igxFor
public fieldsAndAttributes = [];
private localeChannel: BehaviorSubject<any> = new BehaviorSubject<any>(
"de-DE"
);
/**
* used for loading lookups.
*/
@Input("locale")
public set locale(value) {
angularWidgetBridgeInput(value, this.localeChannel, this.unsubscribe);
}
/**
* Enables/disables dynamicHeight calculation.
*/
@Input() public dynamicHeight: boolean = true;
/**
* Viewport height in px that is not covered by attribute-container.
*/
@Input() public dynamicHeightAdditionalHeight: string = "592px";
/**
* enable to create role-based search templates
*/
@Input() public enableRoleBasedSearchTemplates: boolean = false;
public expanded: boolean = true;
public hasSystemAttributes: boolean = false;
public changedAttributes = [];
public form: FormGroup;
public formValues = {};
public lookups = {};
public currentLocale;
public attributes: Attribute[] = [];
public rangeModes = {};
public fieldsVisible = {};
public fieldsVisibleFavorite;
public uniqueID: string = "identifier";
private unsubscribe = NgUnsubscribe.create();
private forceEmit = new Subject<void>();
private ignoreChanges: boolean = false;
private expandedEntry: LocalStorageEntry;
private fieldsVisibleEntry: LocalStorageEntry;
private fieldsVisibleFavoriteEntry: LocalStorageEntry;
private attributesEntry: LocalStorageEntry;
private changedAttributesEntry: LocalStorageEntry;
private rangeModesEntry: LocalStorageEntry;
private formValueEntry: LocalStorageEntry;
private favoritesChanged: boolean = false;
public containerHeight: string;
public containerHeightPx: string;
constructor(
private localstorageService: LocalStorageService,
private widgetframeService: WidgetframeService,
private dialogService: DialogService,
private translateService: TranslateService,
private datetimeAdapter: DateTimeAdapter<any>,
public _editAttributeService: EditAttributeService,
private lookupService: LookupService,
private cdr: ChangeDetectorRef,
private _scrollService: ScrollService
) {
this.expandedEntry = localstorageService.getLocalStorageEntry(
this.prefix + "search-advanced-expanded",
Scope.GLOBAL,
DeletionMode.RESET
);
this.fieldsVisibleEntry = localstorageService.getLocalStorageEntry(
this.prefix + "search-advanced-fields-visible",
Scope.GLOBAL,
DeletionMode.RESET
);
this.fieldsVisibleFavoriteEntry = localstorageService.getLocalStorageEntry(
this.prefix + "search-advanced-fields-visible-favorite",
Scope.GLOBAL,
DeletionMode.RESET
);
this.attributesEntry = localstorageService.getLocalStorageEntry(
this.prefix + "search-advanced-attributes",
Scope.GLOBAL,
DeletionMode.RESET
);
this.changedAttributesEntry = localstorageService.getLocalStorageEntry(
this.prefix + "search-advanced-changed-attributes",
Scope.GLOBAL,
DeletionMode.RESET
);
this.rangeModesEntry = localstorageService.getLocalStorageEntry(
this.prefix + "search-advanced-range-modes",
Scope.GLOBAL,
DeletionMode.RESET
);
this.formValueEntry = localstorageService.getLocalStorageEntry(
this.prefix + "search-advanced-form-value",
Scope.GLOBAL,
DeletionMode.RESET
);
this._scrollService
.getChangeViewPortSize()
.pipe(takeUntil(this.unsubscribe))
.subscribe(() =>
requestAnimationFrame(() => this.updateIgxForContainerHeight())
);
}
private rangeAllowed(field: FormWidgetField): boolean {
return field.range === "always" || field.range === "choose";
}
private buildFieldsAndAttributes() {
this.fieldsAndAttributes = [];
this.fieldsAndAttributes.push(...this.attributes);
this.fieldsAndAttributes.push(
...this.fields.filter((field) => {
return (
this.isFieldVisible(field) &&
!field.basic &&
field.identifier !== "searchMode"
);
})
);
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.fields) {
this.fields.forEach((field) => {
(field as any).isField = true;
if (!field.hasOwnProperty("range")) {
if (field.hasOwnProperty("allowRange")) {
field.range = field.allowRange ? "choose" : "never";
console.warn(
"'allowRange=true|false' is deprecated, use 'range=never|choose|always' instead "
);
} else {
field.range = "never";
}
}
});
}
}
ngOnInit(): void {
if (this.attributeTypesToAddMultiple) {
this.uniqueID = "uid";
}
this.dataInput
.pipe(debounceTime(1000), takeUntil(this.unsubscribe))
.subscribe((value: any) => {
value.visibleFields = Object.keys(this.fieldsVisible).filter(
(key) => this.fieldsVisible[key]
);
this.dataOutput.next(value);
});
if (this.attributesEntry.exists()) {
this.attributes = JSON.parse(this.attributesEntry.value);
this.recoverChangedAttributes();
}
if (this.rangeModesEntry.exists()) {
this.rangeModes = JSON.parse(this.rangeModesEntry.value);
}
this.attributeData.pipe(takeUntil(this.unsubscribe)).subscribe((data) => {
const missingAttributes = [];
let foundAttributes = [];
data.forEach((dto) => {
const attribute = this.attributes.find(
(att) => att.identifier == dto.identifier
);
if (attribute) {
if (attribute.source && attribute.source[0]) {
attribute.source[0] = {};
}
this.mapToMultiLookup(attribute);
foundAttributes.push(attribute);
} else {
missingAttributes.push(dto.identifier);
}
});
if (missingAttributes.length !== 0) {
// TODO : , map(data => data + '?filter=identifier:in:"' + missingAttributes.join(',') + '"'),
this.widgetframeService
.getData(this.attributeUrl)
.pipe(map((data) => data["_embedded"]["attributes"]))
.subscribe((attributes) => {
attributes.forEach((attribute) => {
this.mapToMultiLookup(attribute);
});
foundAttributes = foundAttributes.concat(attributes);
this.recoverFromData(data, foundAttributes);
});
} else {
this.recoverFromData(data, foundAttributes);
}
});
this._editAttributeService
.getAttributeChangedEvent()
.pipe(takeUntil(this.unsubscribe))
.subscribe((attribute) => {
const dashIdx = attribute.identifier.indexOf("-");
// The original attribute that was changed, needed to save the from / to
let originalAttribute;
let from;
if (dashIdx !== -1) {
const rootAttributeName =
dashIdx === -1 ? null : attribute.identifier.substring(0, dashIdx);
const att = this.attributes.find(
(att) =>
att.identifier === rootAttributeName && att.uid === attribute.uid
);
const sub = attribute.identifier.substring(dashIdx + 1);
originalAttribute = attribute;
attribute = att;
from = sub === "from";
}
let dublicateIndex = this.changedAttributes.findIndex(
(item) => item[this.uniqueID] == attribute[this.uniqueID]
);
if (dublicateIndex !== -1) {
this.changedAttributes.splice(dublicateIndex, 1);
}
this.changedAttributes.push(attribute);
this.changedAttributesEntry.value = JSON.stringify(
this.changedAttributes
);
this.resetFavorites.next();
this.attributesOutput.next(this.changedAttributes);
});
this.localeChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((locale) => {
if (locale) {
this.loadLookups(locale);
if (this.attributes && this.attributes.length > 0) {
// reload the attribute descriptions
this.widgetframeService
.getData(
this.attributeUrl +
'&filter=identifier:in:"' +
this.attributes.map((attr) => attr.identifier).join(",") +
'"'
)
.pipe(map((data) => data["_embedded"]["attributes"]))
.subscribe((attributes) => {
attributes.forEach((backendAttribute) => {
var attribute = this.attributes.find(
(frontendAttribute) =>
frontendAttribute.identifier ===
backendAttribute.identifier
);
if (attribute) {
attribute.description = backendAttribute.description;
}
attribute = this.changedAttributes.find(
(frontendAttribute) =>
frontendAttribute.identifier ===
backendAttribute.identifier
);
if (attribute) {
attribute.description = backendAttribute.description;
}
});
// update the local storage
this.changedAttributesEntry.value = JSON.stringify(
this.changedAttributes
);
this.attributesEntry.value = JSON.stringify(this.attributes);
});
}
}
});
this.reloadLookups
.asObservable()
.pipe(takeUntil(this.unsubscribe), debounceTime(1))
.subscribe((lookups) => {
this.updateLookups(lookups, this.localeChannel.value);
});
let controls = {};
this.fields.forEach((field) => {
// overwrite loaded state from local storage
if (field.range === "always") {
this.rangeModes[field.identifier] = true;
}
if (this.rangeAllowed(field)) {
controls[field.identifier + "From"] = new FormControl("");
controls[field.identifier + "Until"] = new FormControl("");
} else {
controls[field.identifier] = new FormControl(field.default);
}
});
this.form = new FormGroup(controls);
combineLatest(this.form.valueChanges, this.forceEmit.asObservable())
.pipe(takeUntil(this.unsubscribe))
.subscribe((values) => {
if (!this.ignoreChanges) {
if (!this.favoritesChanged) {
this.resetFavorites.next();
}
this.favoritesChanged = false;
let data = values[0];
this.formValueEntry.value = JSON.stringify(data);
this.formValues = data;
this.hasSystemAttributes = false;
data = deepCopy(data);
const visitedFields = new Set();
this.fields
.filter((entry) => !entry.basic)
.forEach((field) => {
if (
this.formValues[field.identifier] &&
field.type === "lookup"
) {
let lookups =
this.lookups[this.currentLocale][field.lookupSource];
for (const lookup of lookups) {
if (this.formValues[field.identifier] === lookup.value) {
this.formValues[field.identifier] = lookup.label;
}
}
}
visitedFields.add(field.identifier);
visitedFields.add(field.identifier + "Until");
visitedFields.add(field.identifier + "From");
if (!this.isFieldVisible(field)) {
delete data[field.identifier];
delete data[field.identifier + "Until"];
delete data[field.identifier + "From"];
}
// If we are not in range mode dont send the range until field
// but duplicate the from field to allow searching for absolute values
if (this.rangeAllowed(field)) {
if (!this.rangeModes[field.identifier]) {
data[field.identifier] = data[field.identifier + "From"];
delete data[field.identifier + "Until"];
}
}
if (
this.isFieldVisible(field) &&
field.identifier.indexOf("articleNo") === -1 &&
field.identifier !== "description" &&
field.identifier !== "searchModes"
) {
if (data[field.identifier]) {
this.hasSystemAttributes = true;
} else if (this.rangeAllowed(field)) {
if (
data[field.identifier + "From"] ||
data[field.identifier + "From"]
) {
this.hasSystemAttributes = true;
}
}
}
});
Object.keys(data)
.filter((key) => !visitedFields.has(key))
.forEach((key) => {
delete data[key];
});
this.change.next(data);
}
});
this.forceEmit.next();
if (this.fieldsVisibleEntry.exists()) {
this.fieldsVisible = JSON.parse(this.fieldsVisibleEntry.value);
} else {
this.fields.forEach((field) => {
if (field.defaultVisible === false) {
this.fieldsVisible[field.identifier] = false;
} else {
this.fieldsVisible[field.identifier] = true;
}
});
}
if (this.fieldsVisibleFavoriteEntry.exists()) {
this.fieldsVisibleFavorite = JSON.parse(
this.fieldsVisibleFavoriteEntry.value
);
} else {
this.fieldsVisibleFavorite = this.fields.filter(
(entry) => entry.defaultVisible !== false
);
}
this.reset.pipe(takeUntil(this.unsubscribe)).subscribe(() => {
this.formValueEntry.value = "{}";
const formValue = {};
this.ignoreChanges = true;
this.fields.forEach((field) => {
if (field.default) {
formValue[field.identifier] = field.default;
} else {
if (notNullOrEmpty(field.identifier + "From")) {
formValue[field.identifier + "From"] = null;
}
if (notNullOrEmpty(field.identifier + "Until")) {
formValue[field.identifier + "Until"] = null;
}
formValue[field.identifier] = null;
}
this.setFormValue(formValue);
});
if (this.attributesEntry.exists()) {
this.attributes = JSON.parse(this.attributesEntry.value);
this.recoverChangedAttributes();
this.attributes.forEach((attribute) => {
if (attribute.source && attribute.source.length) {
attribute.source = [{}];
this._editAttributeService.setTriggerSourceRefreshEvent(attribute);
}
if (attribute.from) {
if (attribute.from.source && attribute.from.source.length) {
attribute.from.source[0].value = null;
}
this._editAttributeService.setTriggerSourceRefreshEvent(
attribute.from
);
}
if (attribute.to) {
if (attribute.to.source && attribute.to.source.length) {
attribute.to.source[0].value = null;
}
this._editAttributeService.setTriggerSourceRefreshEvent(
attribute.to
);
}
});
this.attributesEntry.value = JSON.stringify(this.attributes);
}
this.changedAttributes = [];
this.changedAttributesEntry.clear();
this.attributesOutput.next(this.changedAttributes);
this.ignoreChanges = false;
this.forceEmit.next();
this.buildFieldsAndAttributes();
});
this.fieldsTemplate.pipe(takeUntil(this.unsubscribe)).subscribe((data) => {
//this.resentFavoritesChanged = true; TODO
const visibleFieldsIdentifiers = data["visibleFields"];
this.formValueEntry.value = JSON.stringify(data);
this.formValues = data;
this.fieldsVisible = {};
this.fields.forEach((field) => {
if (this.rangeAllowed(field)) {
if (
notNullOrEmpty(data[field.identifier + "From"]) ||
notNullOrEmpty(data[field.identifier + "Until"]) ||
(visibleFieldsIdentifiers &&
visibleFieldsIdentifiers.includes(field.identifier))
) {
this.fieldsVisible[field.identifier] = true;
if (notNullOrEmpty(data[field.identifier + "Until"])) {
this.rangeModes[field.identifier] = true;
} else {
this.rangeModes[field.identifier] = false;
}
} else {
this.fieldsVisible[field.identifier] = false;
if (field.basic) {
this.fieldsVisible[field.identifier] = true;
}
}
} else {
if (
data[field.identifier] ||
field.basic ||
(visibleFieldsIdentifiers &&
visibleFieldsIdentifiers.includes(field.identifier))
) {
this.fieldsVisible[field.identifier] = true;
} else {
this.fieldsVisible[field.identifier] = false;
}
}
});
this.fieldsVisibleEntry.value = JSON.stringify(this.fieldsVisible);
this.setFormValue(data);
this.buildFieldsAndAttributes();
});
this.recoverFormValue();
this.buildFieldsAndAttributes();
this.containerHeightPx = this.listHeight;
this.containerHeight = this.getHeight();
this.searchModeChanged
.pipe(takeUntil(this.unsubscribe))
.subscribe((isListSearch) => {
if (!isListSearch) {
requestAnimationFrame(() => this.updateIgxForContainerHeight());
}
});
}
toggleExpanded() {
this.expandedToggle.next(this.expanded);
}
public formcontrolNameFor(field: FormWidgetField) {
if (this.rangeAllowed(field)) {
return field.identifier + "From";
}
return field.identifier;
}
public formcontrolFor(field: FormWidgetField) {
return this.form.controls[this.formcontrolNameFor(field)];
}
public openAttributeSelection() {
this.widgetframeService
.getData(this.attributeUrl)
.pipe(map((data) => data["_embedded"]["attributes"]))
.subscribe((attributes) => {
attributes.forEach((attribute) => {
if (
this.attributeTypesToAddMultiple &&
this.attributeTypesToAddMultiple.indexOf(
attribute.parentDescription
? attribute.parentDescription
: attribute.type
) > -1
) {
attribute.multipleAddable = true;
}
});
let dialogRef = this.dialogService.open(
SearchAttributeSelectionDialog,
{
minWidth: "900px",
maxWidth: "900px",
height: "755px",
}
);
this.fields.forEach((field) => {
(field as any).scope = "SYSTEM";
(field as any).isField = true;
});
const fields = []
.concat(this.fields)
.filter((field) => !(field.basic || field.identifier == "searchMode"))
.concat(attributes);
dialogRef.componentInstance.fields = _.sortBy(fields, [
(field) => {
return String(this.translateService.instant(field.description));
},
]);
dialogRef.componentInstance.withDefaultMenuAction =
this.dialogWithActionMenu;
dialogRef.componentInstance.selectFilterParams =
this.selectFilterParams;
dialogRef.componentInstance.currentLocale = this.currentLocale;
dialogRef.componentInstance.attributesVisible = this.attributes;
dialogRef.componentInstance.fieldsVisible = deepCopy(
this.fieldsVisible
);
dialogRef.componentInstance.resetDefault = this.fieldsVisibleFavorite;
dialogRef.componentInstance.infoText =
"visible-attribute-selection.edit.infoText";
dialogRef.componentInstance.infoTitle =
"visible-attribute-selection.edit.infoTitle";
dialogRef.componentInstance.activateMultipleAddableMode =
this.attributeTypesToAddMultiple &&
this.attributeTypesToAddMultiple.length > 0;
const sub =
dialogRef.componentInstance.saveFavoritesObservable.subscribe(
(data) => {
this.fieldsVisibleFavorite = data;
this.fieldsVisibleFavoriteEntry.value = JSON.stringify(data);
}
);
dialogRef.afterClosed().subscribe((data) => {
sub.unsubscribe();
if (data) {
this.fieldsVisible = data.fieldsVisible;
this.fieldsVisibleEntry.value = JSON.stringify(data.fieldsVisible);
const attributes = data.attributes;
let changedAttributesChanged = false;
var i = this.changedAttributes.length;
// If we have a changed attribute that is no longer editable we need to remove it from the list of changedattributes and output the changed attributes
while (i--) {
const entry = this.changedAttributes[i];
const attribute = attributes.find(
(val) => val[this.uniqueID] === entry[this.uniqueID]
);
if (!attribute) {
this.changedAttributes.splice(i, 1);
changedAttributesChanged = true;
}
}
if (changedAttributesChanged) {
this.changedAttributesEntry.value = JSON.stringify(
this.changedAttributes
);
this.resetFavorites.next();
this.attributesOutput.next(this.changedAttributes);
} else {
this.attributesOutput.next(attributes);
}
attributes.forEach((attribute) => {
this.mapToMultiLookup(attribute);
});
if (this.attributes) {
attributes.forEach((entry) => {
const value = this.attributes.find(
(att) => att[this.uniqueID] === entry[this.uniqueID]
);
if (value) {
entry.source = value.source;
}
});
}
this.attributes = attributes;
this.attributesEntry.value = JSON.stringify(attributes);
//Force a re emit, since the data that we are emitting should not contain invisible fields and we need to recompute that
this.forceEmit.next();
this.buildFieldsAndAttributes();
}
});
});
}
editField(field: FormWidgetField) {
let dialogRef = this.dialogService.open(
SearchAttributeConfigurationDialog,
{
autoFocus: true,
minWidth: "600px",
maxWidth: "600px",
}
);
dialogRef.componentInstance.field = field;
dialogRef.componentInstance.form = this.form;
dialogRef.componentInstance.fieldIsRange =
this.rangeModes[field.identifier];
dialogRef.componentInstance.lookups = this.lookups;
dialogRef.componentInstance.currentLocale = this.currentLocale;
dialogRef.componentInstance.formcontrolNameFor =
this.formcontrolNameFor(field);
dialogRef.componentInstance.onfieldRangeChange.subscribe((payload) => {
let pl: any = payload;
this.rangeChange(pl.event, pl.element);
});
dialogRef.afterClosed().subscribe((changedField) => {
this.form.patchValue(this.form.value);
});
this.buildFieldsAndAttributes();
}
editAttribute(attribute: Attribute) {
let dialogRef = this.dialogService.open(
SearchAttributeConfigurationDialog,
{
minWidth: "600px",
maxWidth: "600px",
}
);
dialogRef.componentInstance.inputAttribute = attribute;
dialogRef.componentInstance.infoText =
"visible-attribute-selection.edit.infoText";
dialogRef.componentInstance.infoTitle =
"visible-attribute-selection.edit.infoTitle";
dialogRef.componentInstance.editAttributeService =
this._editAttributeService;
dialogRef.componentInstance.showLabel = this.showLabel(attribute);
dialogRef.componentInstance.attributeSupportsRange =
this.attributeSupportsRange(attribute);
dialogRef.afterClosed().subscribe((changedAttribute) => {
if (changedAttribute) {
if (!changedAttribute.displayRange) {
delete changedAttribute.from;
delete changedAttribute.to;
}
let attributeIndex = this.attributes.findIndex(
(attribute) =>
changedAttribute[this.uniqueID] === attribute[this.uniqueID]
);
this.attributes[attributeIndex] = changedAttribute;
this._editAttributeService.setAttributeChangedEvent(changedAttribute);
const atts = this.attributes;
this.attributes = [];
this.attributes.push(...atts);
this.attributesOutput.next(this.changedAttributes);
this.attributesEntry.value = JSON.stringify(this.attributes);
this.changedAttributesEntry.value = JSON.stringify(
this.changedAttributes
);
this.buildFieldsAndAttributes();
}
});
}
public getTooltipValue(field: FormWidgetField) {
const value = this.formValues[field.identifier];
if (field.type === "CATEGORY") {
return value.categoryDescription;
}
if (field.type === "LOOKUP") {
const lookups = this.getLookups(field.lookupSource);
return this.translateService.instant(
lookups.find((lookup) => lookup.value === value).label
);
}
if (field.type === "MULTI_LOOKUP") {
const lookups = this.getLookups(field.lookupSource);
return value
.map((entry) => lookups.find((lookup) => lookup.value === entry).label)
.map((entry) => this.translateService.instant(entry))
.join(" | ");
}
if (!this.rangeAllowed(field)) {
return value;
}
let fromValue = this.formValues[field.identifier + "From"];
let toValue = this.formValues[field.identifier + "Until"];
if (field.type === "DATE") {
if (fromValue) {
this.datetimeAdapter.toIso8601(new Date(fromValue));
fromValue = this.datetimeAdapter.format(new Date(fromValue), null);
}
if (toValue) {
toValue = this.datetimeAdapter.format(new Date(toValue), null);
}
}
if (toValue) {
return `${fromValue} - ${toValue}`;
}
return fromValue;
}
public getTooltipValueForAttribute(attribute: Attribute) {
return attribute.source
.map((entry) =>
entry.description && entry.description.length > 1
? entry.description
: entry.value
)
.join(" | ");
}
public includedInTooltip(field: FormWidgetField) {
if (
!this.fieldsVisible[field.identifier] ||
field.basic ||
field.identifier === "searchMode"
) {
return false;
}
if (!this.rangeAllowed(field)) {
return this.formValues[field.identifier];
}
const fromValue = this.formValues[field.identifier + "From"];
const toValue = this.formValues[field.identifier + "Until"];
return fromValue || toValue;
}
rangeChange(event: any, field: FormWidgetField) {
this.rangeModes[field.identifier] = event.checked;
this.forceEmit.next();
this.rangeModesEntry.value = JSON.stringify(this.rangeModes);
}
getLookups(key): any[] {
if (!this.currentLocale) {
return [];
}
return this.lookups[this.currentLocale][key];
}
private recoverFormValue() {
if (this.formValueEntry.exists()) {
this.setFormValue(JSON.parse(this.formValueEntry.value));
}
}
private setFormValue(value) {
this.form.patchValue(value);
}
private recoverChangedAttributes() {
if (this.changedAttributesEntry.exists()) {
this.changedAttributes = JSON.parse(this.changedAttributesEntry.value);
for (const changedAttribute of this.changedAttributes) {
let dublicate = this.attributes.filter(
(item) => item[this.uniqueID] === changedAttribute[this.uniqueID]
);
if (dublicate.length === 1) {
dublicate[0].source = changedAttribute.source;
dublicate[0].modifier = changedAttribute.modifier;
if (changedAttribute.from) {
dublicate[0].from = changedAttribute.from;
dublicate[0].to = changedAttribute.to;
dublicate[0].displayRange = changedAttribute.displayRange;
}
}
}
}
this.attributesOutput.next(this.changedAttributes);
}
private checkIsAttributeEmpty(attribute: Attribute): boolean {
const isRootEmpty =
attribute.source.length === 0 ||
!attribute.source[0].value ||
attribute.source[0].value === "";
// The root is not empty, it cant be empty
if (!isRootEmpty) {
return false;
}
// The root is empty and it has no range attributes, it must be empty
if (!attribute.from) {
return true;
}
//Check if the children are empty
else
return (
this.checkIsAttributeEmpty(attribute.from) &&
this.checkIsAttributeEmpty(attribute.to)
);
}
private loadLookups(locale) {
this.datetimeAdapter.setLocale(locale);
this.currentLocale = locale;
if (this.lookupsInput) {
if (this.lookups[locale]) {
return;
}
this.lookupService
.getLookups(
Object.keys(this.lookupsInput),
this.lookupsInput,
this.links,
locale
)
.pipe(takeUntil(this.unsubscribe), take(1))
.subscribe((lookups) => {
this.lookups = Object.assign({}, this.lookups, { [locale]: lookups });
this.cdr.markForCheck();
});
}
}
private updateLookups(keys: string[], locale) {
this.datetimeAdapter.setLocale(locale);
this.currentLocale = locale;
if (this.lookupsInput) {
this.lookupService
.getLookups(keys, this.lookupsInput, this.links, locale)
.pipe(takeUntil(this.unsubscribe), take(1))
.subscribe((lookups) => {
Object.keys(lookups).forEach((key) => {
this.lookups[locale][key] = lookups[key];
});
this.lookups = Object.assign({}, this.lookups);
this.cdr.markForCheck();
});
}
}
public isFieldVisible(field: FormWidgetField) {
const value = this.fieldsVisible[field.identifier];
if (value === undefined) {
return true;
}
return value;
}
public showLabel(attribute) {
return attribute.type == "BOOLEAN";
}
public attributeSupportsRange(attribute) {
return RANGE_ATTRIBUTES.indexOf(attribute.type) !== -1;
}
public onInputEvent(event: FormEventPayload) {
// this.event.next(event); TODO
}
public save() {
// this.doSave.next(); TODO
}
public submit() {
// this.doSubmit.next(this.listsearch); TODO
}
private recoverFromData(data: Attribute[], attributes: Attribute[]) {
this.attributes = [];
this.changedAttributes = [];
data.forEach((entry) => {
let attribute = deepCopy(
attributes.find(
(attribute) => attribute.identifier === entry.identifier
)
);
if (entry.from) {
attribute.from = entry.from;
attribute.from.type = attribute.type;
attribute.from.parentType = attribute.parentType;
attribute.from.parentDescription = attribute.parentDescription;
attribute.displayRange = true;
}
if (entry.to) {
attribute.to = entry.to;
attribute.to.type = attribute.type;
attribute.to.parentType = attribute.parentType;
attribute.to.parentDescription = attribute.parentDescription;
attribute.displayRange = true;
}
attribute.source = entry.source;
if (!attribute.uid) {
attribute.uid = createUID();
}
this.attributes.push(attribute);
this.changedAttributes.push(attribute);
});
this.attributesOutput.next(this.changedAttributes);
this.attributesEntry.value = JSON.stringify(this.attributes);
this.changedAttributesEntry.value = JSON.stringify(this.changedAttributes);
this.buildFieldsAndAttributes();
}
public getTooltip(attribute: Attribute) {
let tooltip = "";
if (attribute.modifier === "NOT_EXISTS") {
tooltip += "<div>";
tooltip += this.translateService.instant("tooltip.no.exists");
tooltip += "</div>";
} else if (attribute.modifier === "OR") {
tooltip += "<div>";
tooltip += this.translateService.instant("tooltip.or");
tooltip += "</div>";
}
if (attribute.locale && attribute.locale.length >= 1) {
tooltip += "<div>";
tooltip += this.translateService.instant("tooltip.search.for.locale");
tooltip += "</div>";
}
if (attribute.maintenanceLevel && attribute.maintenanceLevel.length >= 1) {
tooltip += "<div>";
tooltip += this.translateService.instant(
"tooltip.search.for.maintenance.level"
);
tooltip += "</div>";
}
return tooltip;
}
templateChanged(template: any) {
this.favoritesChanged = true;
if (template.type === "standard") {
template = template.parameter;
this.templateOutput.next(template);
if (template.attributes) {
const attributetemplate = template.attributes;
delete template.attributes;
this.attributeData.next(attributetemplate);
} else {
this.attributeData.next([]);
}
if (template.publication) {
const category = template.category;
const publication = template.publication;
const paths = template.categoryPaths;
template.category = {
publication,
category,
paths,
};
} else {
template.category = null;
}
this.fieldsTemplate.next(template);
this.listsearch = false;
this.listvalueOutput.next(null);
} else {
this.listvalueOutput.next(template.parameter);
}
}
public getHeight() {
return this.dynamicHeight
? calcHeight(this.dynamicHeightAdditionalHeight)
: this.listHeight;
}
private mapToMultiLookup(attribute) {
if (attribute._links && attribute._links["attribute-uoms"] != undefined) {
delete attribute._links["attribute-uoms"];
}
const typeList = ["COMPOSITION_AMOUNT", "COMPOSITION_PERCENT", "LOOKUP"];
if (typeList.includes(attribute.type)) {
attribute.type = "MULTI_LOOKUP";
}
}
ngAfterViewInit(): void {
this.updateIgxForContainerHeight();
}
private updateIgxForContainerHeight() {
const containerEl = document.querySelector("#attribute-container");
if (!containerEl) {
return;
}
const style = getComputedStyle(containerEl);
this.containerHeightPx = style.height;
this.cdr.markForCheck();
}
}
<hr />
<ng-container *ngIf="!listsearch">
<div class="nm-widgetframe__title-inline-container">
<mat-checkbox
class="nm-widgetframe__title-inline-container"
color="primary"
(change)="toggleExpanded()"
[(ngModel)]="expanded"
>{{ "label.advanced.search" | translate }}</mat-checkbox
>
</div>
<div class="nm-widgetframe-sub-content" [class.hidden]="!expanded">
<button
mat-mini-fab
color="primary"
class="nm-advancedSearch__shownFieldsButton"
(click)="openAttributeSelection()"
>
<mat-icon>add</mat-icon>
</button>
<ng-container *ngIf="searchFavorites">
<ng-container *ngTemplateOutlet="searchFavoritesTpl"> </ng-container>
</ng-container>
<div
id="attribute-container"
class="attribute-container"
[style.height]="containerHeight"
>
<ng-template
igxFor
let-item
[igxForOf]="fieldsAndAttributes"
igxForScrollOrientation="vertical"
[igxForContainerSize]="containerHeightPx"
[igxForItemSize]="itemHeight"
>
<ng-container *ngIf="!item.isField; else fieldRender">
<ng-container
*ngTemplateOutlet="attributeTemplate; context: { attribute: item }"
></ng-container>
</ng-container>
<ng-template #fieldRender>
<ng-container
*ngTemplateOutlet="fieldTemplate; context: { field: item }"
></ng-container>
</ng-template>
</ng-template>
</div>
</div>
</ng-container>
<ng-container *ngIf="listsearch">
<ng-container *ngTemplateOutlet="searchFavoritesTpl"> </ng-container>
</ng-container>
<ng-template #popTemplateChangedValues>
<div class="nm-attribute-list-tooltip">
<div *ngFor="let field of fields">
<div class="nm-attribute-list-elements" *ngIf="includedInTooltip(field)">
<div class="nm-attribute-list-description">
<nm-ellipsis [content]="field.description | translate"></nm-ellipsis>
</div>
<div class="nm-attribute-list-value">
{{ getTooltipValue(field) }}
</div>
</div>
</div>
<br />
<div *ngFor="let attribute of attributes">
<div class="nm-attribute-list-elements" *ngIf="attribute.source[0].value">
<div class="nm-attribute-list-description">
<nm-ellipsis [content]="attribute.description"></nm-ellipsis>
</div>
<div class="nm-attribute-list-value">
{{ getTooltipValueForAttribute(attribute) }}
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #fieldTemplate let-field="field">
<div class="form-row range-wrapper" [formGroup]="form">
<div class="form-input">
<ng-container *ngIf="rangeModes[field.identifier]; else noRange">
<nm-search-input
class="nm-advancedSearch__attributeRowRange --left"
[field]="field"
[lookups]="lookups"
(event)="onInputEvent($event)"
(enterPressed)="submit()"
[locale]="currentLocale"
[floatingLabel]="
('from' | translate) + ' ' + (field.description | translate)
"
[formControl]="form.controls[field.identifier + 'From']"
></nm-search-input>
<nm-search-input
class="nm-advancedSearch__attributeRowRange --right"
[field]="field"
[lookups]="lookups"
(event)="onInputEvent($event)"
(enterPressed)="submit()"
[locale]="currentLocale"
[floatingLabel]="
('to' | translate) + ' ' + (field.description | translate)
"
[formControl]="form.controls[field.identifier + 'Until']"
[triggerAsChipList]="true"
></nm-search-input>
</ng-container>
<ng-template #noRange>
<nm-search-input
class="nm-advancedSearch__attributeRowSingle no-range"
[field]="field"
(event)="onInputEvent($event)"
(enterPressed)="submit()"
[lookups]="lookups"
[floatingLabel]="field.description | translate"
[locale]="currentLocale"
[formControl]="formcontrolFor(field)"
[triggerAsChipList]="true"
></nm-search-input>
</ng-template>
<button
*ngIf="field.range === 'choose'"
class="edit-button fade-in"
mat-icon-button
color="primary"
aria-label="Search configuration"
>
<mat-icon
(click)="editField(field)"
[svgIcon]="'comment-search-outline'"
></mat-icon>
</button>
</div>
</div>
</ng-template>
<ng-template #attributeTemplate let-attribute="attribute">
<div class="nm-attribute-list-elements">
<mat-icon
*ngIf="getTooltip(attribute) as tooltip"
color="primary"
class="modifier-icon"
[pTooltip]="tooltip"
[escape]="false"
[showDelay]="300"
>info
</mat-icon>
<div
class="range-wrapper"
[ngClass]="{ modifierActive: attribute.modifier }"
>
<ng-container *ngIf="attribute.modifier !== 'NOT_EXISTS'; else notExists">
<ng-container *ngIf="showLabel(attribute); else noLabel">
<div
class="nm-attribute-list-description"
*ngIf="showLabel(attribute)"
>
<nm-ellipsis [content]="attribute.description"></nm-ellipsis>
</div>
<div class="nm-attribute-list-value">
<nm-edit-attribute
[attribute]="attribute"
[copyContentButton]="false"
[onlyInputsForTexts]="true"
[triggerAsChipList]="true"
[editAttributeService]="_editAttributeService"
[floatingLabel]="false"
showClearButton="{{ true }}"
>
</nm-edit-attribute>
</div>
</ng-container>
<ng-template #noLabel>
<ng-container *ngIf="attribute.displayRange; else noRange">
<nm-edit-attribute
class="range-wrapper-left"
[attribute]="attribute.from"
[copyContentButton]="false"
[onlyInputsForTexts]="true"
[loadOptionsLazy]="true"
[displayValueAssets]="true"
[editAttributeService]="_editAttributeService"
[triggerAsChipList]="true"
showClearButton="{{ true }}"
[floatingLabel]="
('from' | translate) + ' ' + attribute.description
"
>
</nm-edit-attribute>
<nm-edit-attribute
class="range-wrapper-right"
[attribute]="attribute.to"
[copyContentButton]="false"
[onlyInputsForTexts]="true"
[loadOptionsLazy]="true"
[displayValueAssets]="true"
[editAttributeService]="_editAttributeService"
[triggerAsChipList]="true"
showClearButton="{{ true }}"
[floatingLabel]="('to' | translate) + ' ' + attribute.description"
>
</nm-edit-attribute>
</ng-container>
<ng-template #noRange>
<nm-edit-attribute
class="range-wrapper-single"
[attribute]="attribute"
[copyContentButton]="false"
[onlyInputsForTexts]="true"
[loadOptionsLazy]="true"
[displayValueAssets]="true"
[editAttributeService]="_editAttributeService"
[triggerAsChipList]="true"
showClearButton="{{ true }}"
[floatingLabel]="attribute.description"
>
</nm-edit-attribute>
</ng-template>
</ng-template>
</ng-container>
<ng-template #notExists>
<mat-form-field class="range-wrapper-no-value">
<input
matInput
class="full-width-margin"
[readonly]="true"
[placeholder]="
attribute.description +
' ' +
('placeholder.without.value' | translate)
"
[value]="attribute.tooltip || ''"
[matTooltip]="attribute.tooltip || ''"
/>
</mat-form-field>
</ng-template>
<button
class="edit-button"
mat-icon-button
color="primary"
(click)="editAttribute(attribute)"
aria-label="Search configuration"
>
<mat-icon [svgIcon]="'comment-search-outline'"></mat-icon>
</button>
</div>
</div>
</ng-template>
<ng-template #searchFavoritesTpl>
<nm-search-favorites
[reset]="resetFavorites"
[searchTemplatesUri]="searchTemplatesUri"
[data]="dataOutput"
[currentLocale]="currentLocale"
[listsearch]="listsearch"
[enableRoleBasedSearchTemplates]="enableRoleBasedSearchTemplates"
(template)="templateChanged($event)"
(reloadLookups)="reloadLookups.next($event)"
>
</nm-search-favorites>
</ng-template>