nm-search-app-search
    src/app/shared/widgets/search-app-search-widget/search-app-search-widget.component.ts
| providers | EditAttributeService | 
| selector | nm-search-app-search | 
| styleUrls | search-app-search-widget.component.scss | 
| templateUrl | ./search-app-search-widget.component.html | 
| constructor(widgetframeService: WidgetframeService, localstorageService: LocalStorageService, dialog: MatDialog, translateService: TranslateService, datetimeAdapter: DateTimeAdapter | ||||||||||||||||||||||||||||||
| 
                                    Parameters :
                                     
 | 
| Public attributeSupportsRange | ||||
| attributeSupportsRange(attribute: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      boolean | 
| Public computeNumberListMaxHeight | 
| computeNumberListMaxHeight() | 
| 
                                Returns :      string | 
| Public configure | ||||||
| configure(configuration: WidgetConfig | ||||||
| Decorators : WidgetConfigure | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public copyToClipboard | ||||
| copyToClipboard(event: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public formcontrolNameFor | ||||||
| formcontrolNameFor(field: FormWidgetField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      any | 
| getHeightModifier | 
| getHeightModifier() | 
| 
                                Returns :      number | 
| getLookups | ||||
| getLookups(key: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      any[] | 
| Public getTooltipValueForAttribute | ||||||
| getTooltipValueForAttribute(attribute: Attribute) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      any | 
| Public isFieldVisible | ||||||
| isFieldVisible(field: FormWidgetField) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      any | 
| Private loadLookups | ||||
| loadLookups(locale: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public mergeForms | 
| mergeForms() | 
| 
                                Returns :      any | 
| onAdvancedSearchChanged | ||||||
| onAdvancedSearchChanged(data: any) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| onAttributesChanged | ||||||
| onAttributesChanged(attributes: Attribute[]) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| onExpandToggle | ||||||
| onExpandToggle($event: any) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public onInputEvent | ||||||
| onInputEvent(event: FormEventPayload) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| onListValueChange | ||||||
| onListValueChange(listValues: any) | ||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public onSubmit | 
| onSubmit() | 
| 
                                Returns :      void | 
| rangeChange | |||||||||
| rangeChange(event: , 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 resetClick | 
| resetClick() | 
| 
                                Returns :      void | 
| Public resetFavoritesSelect | 
| resetFavoritesSelect() | 
| 
                                Returns :      void | 
| Private resetSearchModeField | 
| resetSearchModeField() | 
| 
                                Returns :      void | 
| Public save | 
| save() | 
| 
                                Returns :      void | 
| Private setFormValue | ||||
| setFormValue(value: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private setListsearchValue | ||||
| setListsearchValue(value: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public showLabel | ||||
| showLabel(attribute: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      boolean | 
| Public submit | 
| submit() | 
| 
                                Returns :      void | 
| toggleContentVisibility | 
| toggleContentVisibility() | 
| 
                                Returns :      void | 
| Public toggleMode | ||||
| toggleMode($event: ) | ||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Private updateLookups | |||||||||
| updateLookups(keys: string[], locale: ) | |||||||||
| 
                                    Parameters :
                                     
 
                                Returns :      void | 
| Public _editAttributeService | 
| _editAttributeService:      | 
| Type : EditAttributeService | 
| Private _listsearch | 
| _listsearch:      | 
| Type : boolean | 
| Private advancedFormData | 
| advancedFormData:      | 
| Type : any | 
| Public attributeData | 
| attributeData:      | 
| Default value : new Subject<Attribute[]>() | 
| Decorators : WidgetInput | 
| Public attributes | 
| attributes:      | 
| Type : Attribute[] | 
| Private attributesLocalstorageEntry | 
| attributesLocalstorageEntry:      | 
| Type : LocalStorageEntry | 
| Public attributesOutput | 
| attributesOutput:      | 
| Default value : new ReplaySubject<any>(1) | 
| Decorators : WidgetOutput | 
| Public attributeTypesToAddMultiple | 
| attributeTypesToAddMultiple:      | 
| Type : string[] | 
| Private basicData | 
| basicData:      | 
| Type : any | 
| Public basicform | 
| basicform:      | 
| Public change | 
| change:      | 
| Default value : new BehaviorSubject<any>({}) | 
| Decorators : WidgetOutput | 
| Public changedAttributes | 
| changedAttributes:      | 
| Type : [] | 
| Default value : [] | 
| Private changedAttributesLocalstorageEntry | 
| changedAttributesLocalstorageEntry:      | 
| Type : LocalStorageEntry | 
| Public configuration | 
| configuration:      | 
| Type : WidgetConfig<FormWidgetConfiguration> | 
| Decorators : WidgetConfiguration | 
| Public contentVisible | 
| contentVisible:      | 
| Type : boolean | 
| Default value : false | 
| Public contentVisibleEntry | 
| contentVisibleEntry:      | 
| Type : LocalStorageEntry | 
| Public copyNonMatchingParametersToClipboard | 
| copyNonMatchingParametersToClipboard:      | 
| Default value : new Subject() | 
| Decorators : WidgetOutput | 
| Public currentLocale | 
| currentLocale:      | 
| Private data | 
| data:      | 
| Type : any | 
| Default value : {} | 
| Public dataOutput | 
| dataOutput:      | 
| Default value : new Subject() | 
| Decorators : WidgetOutput | 
| Public disableCopyNonMatchingParametersToClipboard | 
| disableCopyNonMatchingParametersToClipboard:      | 
| Default value : new Subject<boolean>() | 
| Decorators : WidgetInput | 
| Public doSave | 
| doSave:      | 
| Default value : new Subject() | 
| Decorators : WidgetOutput | 
| Public doSubmit | 
| doSubmit:      | 
| Default value : new Subject() | 
| Decorators : WidgetOutput | 
| Public enableRoleBasedSearchTemplates | 
| enableRoleBasedSearchTemplates:      | 
| Type : boolean | 
| Public event | 
| event:      | 
| Default value : new Subject<FormEventPayload>() | 
| Decorators : WidgetOutput | 
| Private expanded | 
| expanded:      | 
| Type : boolean | 
| Default value : true | 
| exportButtonDisabled | 
| exportButtonDisabled:      | 
| Type : boolean | 
| Default value : true | 
| Public favoriteField | 
| favoriteField:      | 
| Type : FormWidgetField | 
| Default value : {
    identifier: "searchfavorites",
    description: "table.head.searchfavorite",
    type: "LOOKUP",
    lookupSource: "searchfavorites",
    eventField: true,
    range: "never",
  } | 
| Public favoriteForm | 
| favoriteForm:      | 
| Public fields | 
| fields:      | 
| Type : FormWidgetField[] | 
| 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:      | 
| Private formValueEntry | 
| formValueEntry:      | 
| Type : LocalStorageEntry | 
| Public formValues | 
| formValues:      | 
| Type : object | 
| Default value : {} | 
| Public frameHeight | 
| frameHeight:      | 
| Public hasSystemAttributes | 
| hasSystemAttributes:      | 
| Type : boolean | 
| Default value : false | 
| Public header | 
| header:      | 
| Public id | 
| id:      | 
| Type : string | 
| Decorators : WidgetId | 
| Public infoText | 
| infoText:      | 
| Public isCollapsible | 
| isCollapsible:      | 
| Type : boolean | 
| Default value : false | 
| Private isListsearchEntry | 
| isListsearchEntry:      | 
| Type : LocalStorageEntry | 
| Public isSearchmodeList | 
| isSearchmodeList:      | 
| Default value : new ReplaySubject<boolean>(1) | 
| Decorators : WidgetOutput | 
| Public listchange | 
| listchange:      | 
| Default value : new ReplaySubject<any>(1) | 
| Decorators : WidgetOutput | 
| Public listHeight | 
| listHeight:      | 
| Public listSearchForm | 
| listSearchForm:      | 
| Public listsearchmodeField | 
| listsearchmodeField:      | 
| Type : FormWidgetField | 
| Default value : {
    identifier: "parameter",
    description: "table.head.listsearchmodes",
    type: "LOOKUP",
    lookupSource: "listsearchmodes",
    default: "PRODUCTNO",
    range: "never",
  } | 
| Public listsearchValue | 
| listsearchValue:      | 
| Private listsearchValueEntry | 
| listsearchValueEntry:      | 
| Type : LocalStorageEntry | 
| Public listvalue | 
| listvalue:      | 
| Default value : new Subject() | 
| Decorators : WidgetInput | 
| Public locale | 
| locale:      | 
| Default value : new BehaviorSubject(null) | 
| Decorators : WidgetInput | 
| Public lookups | 
| lookups:      | 
| Type : object | 
| Default value : {} | 
| Private nmSearchFooter_ref | 
| nmSearchFooter_ref:      | 
| Type : ElementRef | 
| Decorators : ViewChild | 
| Private numberList_ref | 
| numberList_ref:      | 
| Type : ElementRef | 
| Decorators : ViewChild | 
| Public rangeModes | 
| rangeModes:      | 
| Type : object | 
| Default value : {} | 
| Private rangeModesEntry | 
| rangeModesEntry:      | 
| Type : LocalStorageEntry | 
| Public reloadLookups | 
| reloadLookups:      | 
| Default value : new Subject<any>() | 
| Decorators : WidgetInput | 
| Public resentFavoritesChanged | 
| resentFavoritesChanged:      | 
| Type : boolean | 
| Default value : false | 
| Public reset | 
| reset:      | 
| Default value : new Subject() | 
| Decorators : WidgetInput | 
| Public resetOutput | 
| resetOutput:      | 
| Default value : new Subject() | 
| Decorators : WidgetOutput | 
| Public searchFavorites | 
| searchFavorites:      | 
| Type : boolean | 
| Default value : true | 
| Private searchMode | 
| searchMode:      | 
| Public searchModeChanged | 
| searchModeChanged:      | 
| Default value : new Subject<boolean>() | 
| Private searchModeEntry | 
| searchModeEntry:      | 
| Type : LocalStorageEntry | 
| Public searchModeField | 
| searchModeField:      | 
| Type : FormWidgetField | 
| Default value : {
    identifier: "searchMode",
    description: "table.head.searchmodes",
    type: "MENU",
    defaultVisible: false,
    lookupSource: "standardsearchmodes",
    default: "SEARCH_MODE_STANDARD",
    range: "never",
  } | 
| Public searchModeForm | 
| searchModeForm:      | 
| Public searchmodeSelect | 
| searchmodeSelect:      | 
| Default value : true | 
| Public title | 
| title:      | 
| Private unsubscribe | 
| unsubscribe:      | 
| Default value : NgUnsubscribe.create() | 
| Public value | 
| value:      | 
| Default value : new Subject() | 
| Decorators : WidgetInput | 
| Public wikiLink | 
| wikiLink:      | 
| listsearch | ||||
| get listsearch() | ||||
| set listsearch(value: ) | ||||
| 
                                        Parameters :
                                         
 
                                    Returns :      void | 
import {
  WidgetComponent,
  WidgetConfiguration,
  WidgetConfigure,
  WidgetId,
  WidgetInput,
  WidgetOutput,
} from "../widget.metadata";
import {
  ChangeDetectorRef,
  Component,
  ElementRef,
  ViewChild,
} from "@angular/core";
import {
  getOrDefault,
  throwIfUndefined,
  WidgetConfig,
} from "../widget.configuration";
import { FormControl, FormGroup } from "@angular/forms";
import { WidgetframeService } from "../widgetframe/widgetframe.service";
import { NgUnsubscribe } from "../../ng-unsubscribe";
import { debounceTime, takeUntil } from "rxjs/operators";
import { BehaviorSubject, combineLatest, ReplaySubject, Subject } from "rxjs";
import {
  LocalStorageEntry,
  LocalStorageService,
} from "../../components/local-storage/local-storage.service";
import {
  DeletionMode,
  Scope,
} from "../../components/local-storage/local-storage-constants";
import {
  deepCopy,
  notNullOrEmpty,
  UtilService,
} from "../../components/util/util.service";
import { MatDialog } from "@angular/material/dialog";
import { DateTimeAdapter } from "ng-pick-datetime";
import { TranslateService } from "@ngx-translate/core";
import { EditAttributeService } from "../../components/edit-attribute/edit-attribute.service";
import { Attribute } from "../../components/edit-attribute/attribute";
import { AppdataStore } from "../../components/appdata/appdata.store";
import { ScrollService } from "../../components/scroll/scroll.service";
import { FormWidgetField } from "../search/advanced/search-advanced.component";
const DEFAULT_FRAME_HEIGHT_PX: number = 559;
const EMPTY_ADVANCED_SEARCH_HEIGHT_PX: number = 140;
export class FormLookup {
  label: string;
  collectionName: string;
  valueField: string;
  keepOriginal: boolean;
}
export class FormEventPayload {
  field: string;
  payload: any;
}
export class FormWidgetConfiguration {
  editVisibleFields: boolean;
  attributeUrl: string;
  fields: FormWidgetField[];
  lookups: { [key: string]: FormLookup };
  infoText: string;
  infoWidth: string;
  infoHeight: string;
  listSearchInfoText: string;
  listSearchTitle: string;
  header: string;
  isCollapsible: boolean;
  title: string;
  wikiLink: string;
  children: any;
  frameHeight: any;
  listHeight: any;
  attributeTypesToAddMultiple: string[];
  selectFilterParams: any;
  localstoragePrefix: string;
  enableRoleBasedSearchTemplates: boolean;
}
@WidgetComponent("nm-search-app-search")
@Component({
  selector: "nm-search-app-search",
  templateUrl: "./search-app-search-widget.component.html",
  styleUrls: ["./search-app-search-widget.component.scss"],
  providers: [EditAttributeService],
})
export class SearchAppSearchWidgetComponent {
  public basicform;
  public form;
  public favoriteForm;
  public listSearchForm;
  public listsearchValue;
  public searchModeForm;
  public favoriteField: FormWidgetField = {
    identifier: "searchfavorites",
    description: "table.head.searchfavorite",
    type: "LOOKUP",
    lookupSource: "searchfavorites",
    eventField: true,
    range: "never",
  };
  public listsearchmodeField: FormWidgetField = {
    identifier: "parameter",
    description: "table.head.listsearchmodes",
    type: "LOOKUP",
    lookupSource: "listsearchmodes",
    default: "PRODUCTNO",
    range: "never",
  };
  public searchModeField: FormWidgetField = {
    identifier: "searchMode",
    description: "table.head.searchmodes",
    type: "MENU",
    defaultVisible: false,
    lookupSource: "standardsearchmodes",
    default: "SEARCH_MODE_STANDARD",
    range: "never",
  };
  public fields: FormWidgetField[];
  public header;
  public infoText;
  public title;
  public wikiLink;
  public isCollapsible: boolean = false;
  public contentVisible: boolean = false;
  public resentFavoritesChanged: boolean = false;
  public currentLocale;
  public rangeModes = {};
  public fieldsVisible = {};
  public fieldsVisibleFavorite;
  public lookups = {};
  public formValues = {};
  private unsubscribe = NgUnsubscribe.create();
  private isListsearchEntry: LocalStorageEntry;
  private listsearchValueEntry: LocalStorageEntry;
  private formValueEntry: LocalStorageEntry;
  private rangeModesEntry: LocalStorageEntry;
  private fieldsVisibleEntry: LocalStorageEntry;
  private fieldsVisibleFavoriteEntry: LocalStorageEntry;
  private attributesLocalstorageEntry: LocalStorageEntry;
  private changedAttributesLocalstorageEntry: LocalStorageEntry;
  public contentVisibleEntry: LocalStorageEntry;
  public hasSystemAttributes: boolean = false;
  private _listsearch: boolean;
  public attributes: Attribute[];
  public changedAttributes = [];
  public frameHeight;
  public listHeight;
  private forceEmit = new Subject<void>();
  public searchModeChanged = new Subject<boolean>();
  public attributeTypesToAddMultiple: string[];
  public enableRoleBasedSearchTemplates: boolean;
  public searchmodeSelect = true;
  public searchFavorites: boolean = true;
  private basicData: any;
  private advancedFormData: any;
  private data: any = {};
  private expanded: boolean = true;
  private searchMode;
  private searchModeEntry: LocalStorageEntry;
  exportButtonDisabled: boolean = true;
  constructor(
    private widgetframeService: WidgetframeService,
    private localstorageService: LocalStorageService,
    private dialog: MatDialog,
    private translateService: TranslateService,
    private datetimeAdapter: DateTimeAdapter<any>,
    private appStore: AppdataStore,
    public _editAttributeService: EditAttributeService,
    private scrollService: ScrollService,
    private cdr: ChangeDetectorRef
  ) {}
  @WidgetId()
  public id: string;
  @WidgetConfiguration()
  public configuration: WidgetConfig<FormWidgetConfiguration>;
  @WidgetInput()
  public locale = new BehaviorSubject(null);
  @WidgetOutput()
  public change = new BehaviorSubject<any>({});
  @WidgetOutput()
  public listchange = new ReplaySubject<any>(1);
  @WidgetInput()
  public reset = new Subject();
  @WidgetOutput()
  public resetOutput = new Subject();
  @WidgetOutput("submit")
  public doSubmit = new Subject();
  @WidgetOutput()
  public copyNonMatchingParametersToClipboard = new Subject();
  @WidgetInput()
  public value = new Subject();
  @WidgetInput()
  public listvalue = new Subject();
  @WidgetInput("attributeData")
  public attributeData = new Subject<Attribute[]>();
  @WidgetInput()
  public reloadLookups = new Subject<any>();
  @WidgetOutput("save")
  public doSave = new Subject();
  @WidgetOutput("attributes")
  public attributesOutput = new ReplaySubject<any>(1);
  @WidgetOutput()
  public isSearchmodeList = new ReplaySubject<boolean>(1);
  @WidgetOutput()
  public event = new Subject<FormEventPayload>();
  @WidgetInput()
  public disableCopyNonMatchingParametersToClipboard = new Subject<boolean>();
  @WidgetOutput("data")
  public dataOutput = new Subject();
  @ViewChild("searchNumberList")
  private numberList_ref: ElementRef;
  @ViewChild("nmSearchFooter")
  private nmSearchFooter_ref: ElementRef;
  get listsearch() {
    return this._listsearch;
  }
  set listsearch(value) {
    this._listsearch = value;
    this.isSearchmodeList.next(value);
  }
  @WidgetConfigure()
  public configure(configuration: WidgetConfig<FormWidgetConfiguration>) {
    this.enableRoleBasedSearchTemplates = this.configuration.configuration.enableRoleBasedSearchTemplates;
    this.attributesOutput.next([]);
    this.frameHeight = getOrDefault(
      this.configuration.configuration.frameHeight,
      DEFAULT_FRAME_HEIGHT_PX
    );
    if (this.configuration.configuration.listHeight) {
      this.listHeight =
        String(this.configuration.configuration.listHeight) + "px";
    } else {
      this.listHeight =
        String(
          (window.innerHeight ||
            document.documentElement.clientHeight ||
            document.body.clientHeight) -
            this.frameHeight +
            this.getHeightModifier()
        ) + "px";
    }
    this.attributeTypesToAddMultiple = getOrDefault(
      this.configuration.configuration.attributeTypesToAddMultiple,
      ["LOCALIZED_STRING", "TEXT_TYPE"]
    );
    this.scrollService
      .getChangeViewPortSize()
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((newViewPortHeight) => {
        if (!this.configuration.configuration.listHeight) {
          this.listHeight =
            String(
              newViewPortHeight - this.frameHeight + this.getHeightModifier()
            ) + "px";
        }
      });
    this.attributesLocalstorageEntry = this.localstorageService.getLocalStorageEntry(
      this.id + "-search-atributes",
      Scope.GLOBAL,
      DeletionMode.RESET
    );
    this.changedAttributesLocalstorageEntry = this.localstorageService.getLocalStorageEntry(
      this.id + "search-changed-attributes",
      Scope.GLOBAL,
      DeletionMode.LOGIN
    );
    if (this.attributesLocalstorageEntry.exists()) {
      this.attributes = JSON.parse(this.attributesLocalstorageEntry.value);
      this.recoverChangedAttributes();
    }
    this.searchModeEntry = this.localstorageService.getLocalStorageEntry(
      this.id + "-search-mode",
      Scope.GLOBAL,
      DeletionMode.RESET
    );
    if (this.searchModeEntry.exists()) {
      this.searchMode = JSON.parse(this.searchModeEntry.value);
    }
    const searchModeControls = {};
    searchModeControls[this.searchModeField.identifier] = new FormControl(
      this.searchMode ? this.searchMode : this.searchModeField.default
    );
    this.searchModeForm = new FormGroup(searchModeControls);
    this.listSearchForm = new FormGroup({
      parameter: new FormControl("PRODUCTNO"),
      list: new FormControl(""),
    });
    combineLatest([
      this.listSearchForm.valueChanges,
      this.searchModeForm.valueChanges,
    ])
      .pipe(takeUntil(this.unsubscribe))
      .subscribe(([data, searchModeData]: any) => {
        this.listsearchValueEntry.value = JSON.stringify(data);
        Object.assign(data, searchModeData);
        this.listchange.next(data);
        this.data.listvalue = data;
        this.dataOutput.next(this.data);
      });
    this.searchModeForm.valueChanges
      .asObservable()
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((searchModeData) => {
        if (searchModeData?.searchMode) {
          this.searchModeEntry.value = JSON.stringify(
            searchModeData.searchMode
          );
        }
      });
    this.fields = configuration.configuration.fields.filter(
      (item) => item.basic
    );
    this.formValueEntry = this.localstorageService.getLocalStorageEntry(
      this.id + "-form",
      Scope.GLOBAL,
      DeletionMode.RESET
    );
    this.rangeModesEntry = this.localstorageService.getLocalStorageEntry(
      this.id + "-range-modes",
      Scope.GLOBAL,
      DeletionMode.RESET
    );
    this.fieldsVisibleEntry = this.localstorageService.getLocalStorageEntry(
      this.id + "-fields-visible",
      Scope.GLOBAL,
      DeletionMode.RESET
    );
    this.fieldsVisibleFavoriteEntry = this.localstorageService.getLocalStorageEntry(
      this.id + "-fields-visible-favorite",
      Scope.GLOBAL,
      DeletionMode.RESET
    );
    this.isListsearchEntry = this.localstorageService.getLocalStorageEntry(
      this.id + "-islistsearch",
      Scope.GLOBAL,
      DeletionMode.RESET
    );
    this.listsearchValueEntry = this.localstorageService.getLocalStorageEntry(
      this.id + "-listsearch",
      Scope.GLOBAL,
      DeletionMode.RESET
    );
    if (this.rangeModesEntry.exists()) {
      this.rangeModes = JSON.parse(this.rangeModesEntry.value);
    }
    this.isCollapsible = getOrDefault(
      this.configuration.configuration.isCollapsible,
      false
    );
    this.contentVisibleEntry = this.localstorageService.getLocalStorageEntry(
      this.id + "-contentVisible",
      Scope.GLOBAL,
      DeletionMode.RESET
    );
    if (this.contentVisibleEntry.exists()) {
      this.contentVisible = this.contentVisibleEntry.value === "true";
    }
    this.header = getOrDefault(
      this.configuration.configuration.header,
      "primary"
    );
    if (this.isListsearchEntry.exists()) {
      this.listsearch = this.isListsearchEntry.value === "true";
    } else {
      this.listsearch = false;
    }
    if (this.listsearchValueEntry.exists()) {
      this.listSearchForm.patchValue(
        JSON.parse(this.listsearchValueEntry.value)
      );
    }
    this.infoText = configuration.configuration.infoText;
    this.title = throwIfUndefined(this.configuration.configuration.title);
    this.wikiLink = this.configuration.configuration.wikiLink;
    this.locale
      .asObservable()
      .pipe(takeUntil(this.unsubscribe), debounceTime(1))
      .subscribe((locale) => this.loadLookups(locale));
    let controls = {};
    this.fields.forEach((field) => {
      if (field.allowRange) {
        controls[field.identifier + "From"] = new FormControl("");
        controls[field.identifier + "Until"] = new FormControl("");
      } else {
        controls[field.identifier] = new FormControl(field.default);
      }
    });
    this.reloadLookups
      .asObservable()
      .pipe(takeUntil(this.unsubscribe), debounceTime(1))
      .subscribe((lookups) => {
        this.updateLookups(lookups, this.locale.getValue());
      });
    this.favoriteForm = new FormGroup({
      searchfavorites: new FormControl(""),
    });
    this.form = new FormGroup(controls);
    this.favoriteForm.valueChanges
      .asObservable()
      .pipe(takeUntil(this.unsubscribe))
      .subscribe(() => {
        this.resentFavoritesChanged = true;
      });
    combineLatest([
      this.form.valueChanges,
      this.searchModeForm.valueChanges,
      this.forceEmit.asObservable(),
    ])
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((values) => {
        this.resetFavoritesSelect();
        this.resentFavoritesChanged = false;
        let data = values[0];
        const searchModeData = values[1];
        this.formValueEntry.value = JSON.stringify(data);
        this.formValues = data;
        this.hasSystemAttributes = false;
        data = deepCopy(data);
        this.fields.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;
              }
            }
          }
          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
          if (field.allowRange) {
            if (!this.rangeModes[field.identifier]) {
              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 (field.allowRange) {
              if (
                data[field.identifier] + "From" ||
                data[field.identifier] + "From"
              ) {
                this.hasSystemAttributes = true;
              }
            }
          }
        });
        Object.assign(data, searchModeData);
        this.basicData = data;
        this.change.next(this.mergeForms());
      });
    this.forceEmit.next();
    this.searchModeForm.updateValueAndValidity({
      onlySelf: false,
      emitEvent: true,
    });
    this.listvalue.pipe(takeUntil(this.unsubscribe)).subscribe((data) => {
      this.listsearch = true;
      this.isListsearchEntry.value = "true";
      this.listSearchForm.patchValue(data);
      this.listsearchValueEntry.value = JSON.stringify(data);
    });
    this.value.pipe(takeUntil(this.unsubscribe)).subscribe((data) => {
      this.resentFavoritesChanged = true;
      this.setFormValue(data);
      this.formValueEntry.value = JSON.stringify(data);
      this.formValues = data;
      this.listsearch = false;
      this.isListsearchEntry.value = "false";
      this.fieldsVisible = {};
      this.fields.forEach((field) => {
        if (field.allowRange) {
          // We can not use a if(data[field.identifier]) check here, since 0 will validate to false!
          if (
            notNullOrEmpty(data[field.identifier + "From"]) ||
            notNullOrEmpty(data[field.identifier + "Until"])
          ) {
            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 (notNullOrEmpty(data[field.identifier]) || field.basic) {
            this.fieldsVisible[field.identifier] = true;
          } else {
            this.fieldsVisible[field.identifier] = false;
          }
        }
      });
      this.fieldsVisibleEntry.value = JSON.stringify(this.fieldsVisible);
    });
    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
      );
    }
    this.recoverFormValue();
    this.reset.pipe(takeUntil(this.unsubscribe)).subscribe(() => {
      this.formValueEntry.value = "{}";
      const formValue = {};
      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);
      });
      this.setListsearchValue("");
      this.changedAttributesLocalstorageEntry.clear();
      this.changedAttributes = [];
      this.onAdvancedSearchChanged({});
      this.onAttributesChanged([]);
      this.forceEmit.next();
      this.resetSearchModeField();
    });
    this.disableCopyNonMatchingParametersToClipboard
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((disable) => (this.exportButtonDisabled = disable));
  }
  private resetSearchModeField() {
    this.searchModeEntry.value = null;
    this.searchModeForm.controls.searchMode.setValue("SEARCH_MODE_STANDARD");
  }
  private recoverChangedAttributes() {
    if (this.changedAttributesLocalstorageEntry.exists()) {
      this.changedAttributes = JSON.parse(
        this.changedAttributesLocalstorageEntry.value
      );
      for (const changedAttribute of this.changedAttributes) {
        let dublicate = this.attributes.filter(
          (item) => item.identifer == changedAttribute.identifer
        );
        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 recoverFormValue() {
    if (this.formValueEntry.exists()) {
      this.setFormValue(JSON.parse(this.formValueEntry.value));
    }
  }
  private setFormValue(value) {
    this.form.patchValue(value);
  }
  private loadLookups(locale) {
    this.datetimeAdapter.setLocale(locale);
    this.currentLocale = locale;
    if (this.configuration.configuration.lookups) {
      if (this.lookups[locale]) {
        return;
      }
      const lookups = {};
      Object.keys(this.configuration.configuration.lookups).forEach((key) => {
        const lookup = this.configuration.configuration.lookups[key];
        lookups[key] = [];
        const link = this.configuration._links[key] as any;
        if (!link) {
          console.error(`Link for lookup ${key} is not configured`);
          return;
        }
        link.href = UtilService.setUrlParam(
          link.href,
          "locale",
          this.currentLocale
        );
        this.widgetframeService.getData(link.href).subscribe((data) => {
          if (!data["_embedded"][lookup.collectionName]) {
            console.error(
              `Cant find data under the collectionname ${lookup.collectionName}, is it configured correctly? ${link.href}`
            );
          }
          data = data["_embedded"][lookup.collectionName];
          if (lookup.keepOriginal === true) {
            lookups[key] = data;
            return;
          }
          data.forEach((value) => {
            const label = value[lookup.label];
            if (lookup.valueField) {
              value = value[lookup.valueField];
            }
            lookups[key].push({ value, label });
          });
          if (locale) {
            this.lookups = Object.assign({ [locale]: lookups }, this.lookups);
            this.cdr.markForCheck();
          }
        });
      });
    }
  }
  private updateLookups(keys: string[], locale) {
    this.datetimeAdapter.setLocale(locale);
    this.currentLocale = locale;
    if (this.configuration.configuration.lookups) {
      keys.forEach((key) => {
        const lookup = this.configuration.configuration.lookups[key];
        const link = this.configuration._links[key] as any;
        if (!link) {
          console.error(`Link for lookup ${key} is not configured`);
          return;
        }
        link.href = UtilService.setUrlParam(
          link.href,
          "locale",
          this.currentLocale
        );
        this.widgetframeService.getData(link.href).subscribe((data) => {
          if (!data["_embedded"][lookup.collectionName]) {
            console.error(
              `Cant find data under the collectionname ${lookup.collectionName}, is it configured correctly? ${link.href}`
            );
          }
          data = data["_embedded"][lookup.collectionName];
          this.lookups[locale][key] = [];
          data.forEach((value) => {
            const label = value[lookup.label];
            if (lookup.valueField) {
              value = value[lookup.valueField];
            }
            this.lookups[locale][key].push({ value, label });
          });
        });
      });
    }
  }
  getLookups(key): any[] {
    if (!this.currentLocale) {
      return [];
    }
    return this.lookups[this.currentLocale][key];
  }
  toggleContentVisibility() {
    this.contentVisible = !this.contentVisible;
    this.contentVisibleEntry.value = this.contentVisible.toString();
  }
  public onSubmit() {
    console.debug(this.form.value);
  }
  rangeChange(event, field: FormWidgetField) {
    this.rangeModes[field.identifier] = event.checked;
    this.forceEmit.next();
    this.rangeModesEntry.value = JSON.stringify(this.rangeModes);
  }
  public isFieldVisible(field: FormWidgetField) {
    if (field.basic) {
      return true;
    }
    const value = this.fieldsVisible[field.identifier];
    if (value === undefined) {
      return true;
    }
    return value;
  }
  public toggleMode($event) {
    this.listsearch = $event.checked;
    this.searchModeChanged.next(this.listsearch);
    this.isListsearchEntry.value = $event.checked;
    if (this.listsearch) {
      this.listchange.next(this.listSearchForm.value);
    } else {
      this.forceEmit.next();
    }
  }
  public submit() {
    this.doSubmit.next(this.listsearch);
  }
  public save() {
    this.doSave.next();
  }
  public resetClick() {
    this.resetOutput.next();
    this.reset.next();
  }
  private setListsearchValue(value) {
    this.listSearchForm.patchValue({ list: value });
    this.listsearchValueEntry.value = JSON.stringify(this.listSearchForm.value);
  }
  public formcontrolNameFor(field: FormWidgetField) {
    if (field.allowRange) {
      return field.identifier + "From";
    }
    return field.identifier;
  }
  public onInputEvent(event: FormEventPayload) {
    this.event.next(event);
  }
  public resetFavoritesSelect() {
    if (!this.resentFavoritesChanged) {
      this.favoriteForm.patchValue({ searchfavorites: null });
    }
  }
  public showLabel(attribute) {
    return attribute.type == "BOOLEAN";
  }
  public attributeSupportsRange(attribute) {
    return !(
      attribute.type == "BOOLEAN" ||
      attribute.type == "LOOKUP" ||
      attribute.type == "MULTI_LOOKUP" ||
      attribute.type == "STRING" ||
      attribute.type == "LOCALIZED_STRING"
    );
  }
  public mergeForms() {
    if (this.expanded && this.advancedFormData) {
      const data = Object.assign({}, this.advancedFormData, this.basicData);
      return data;
    }
    return this.basicData;
  }
  public computeNumberListMaxHeight(): string {
    if (this.listsearch) {
      let topList = this.numberList_ref?.nativeElement?.getBoundingClientRect()
        .top;
      let topFooter = this.nmSearchFooter_ref?.nativeElement?.getBoundingClientRect()
        .top;
      let maxHeight = topFooter - topList - EMPTY_ADVANCED_SEARCH_HEIGHT_PX;
      return maxHeight + "px";
    }
    return "0";
  }
  private recoverFromData(data: Attribute[], attributes: Attribute[]) {
    this.attributes = [];
    this.changedAttributes = [];
    data.forEach((entry) => {
      const attribute: Attribute = deepCopy(
        attributes.find((attribute) => attribute.id === entry.id)
      );
      attribute.source = entry.source;
      if (entry.from) {
        attribute.from = entry.from;
        attribute.displayRange = true;
      } else {
        attribute.from = {
          identifier: attribute.identifier,
          source: [],
          type: attribute.type,
        };
      }
      if (entry.to) {
        attribute.to = entry.to;
        attribute.displayRange = true;
      } else {
        attribute.to = {
          identifier: attribute.identifier,
          source: [],
          type: attribute.type,
        };
      }
      this.attributes.push(attribute);
      this.changedAttributes.push(attribute);
    });
    this.attributesOutput.next(this.changedAttributes);
    this.attributesLocalstorageEntry.value = JSON.stringify(this.attributes);
    this.changedAttributesLocalstorageEntry.value = JSON.stringify(
      this.changedAttributes
    );
  }
  public getTooltipValueForAttribute(attribute: Attribute) {
    return attribute.source
      .map((entry) =>
        entry.description && entry.description.length > 1
          ? entry.description
          : entry.value
      )
      .join(" | ");
    // return attribute.source[0].description && attribute.source[0].description.length > 1) ? attribute.source[0].description : attribute.source[0].value
  }
  onAdvancedSearchChanged(data: any) {
    this.advancedFormData = data;
    let merged = this.mergeForms();
    this.change.next(merged);
    this.data.fields = merged;
    this.dataOutput.next(this.data);
  }
  onAttributesChanged(attributes: Attribute[]) {
    let mappedAttributes = attributes;
    this.attributesOutput.next(mappedAttributes);
    this.data.attributes = mappedAttributes;
    this.dataOutput.next(this.data);
  }
  getHeightModifier() {
    let fontSize = this.scrollService.getFontSize().value;
    let modifier: number = 0;
    switch (fontSize) {
      case "small": {
        modifier = +14;
        break;
      }
      case "medium": {
        modifier = 0;
        break;
      }
      case "large":
      default: {
        modifier = -32;
        break;
      }
    }
    return modifier;
  }
  onListValueChange(listValues: any) {
    if (listValues) {
      this.listsearch = true;
      this.listvalue.next(listValues);
    } else {
      this.listsearch = false;
    }
  }
  onExpandToggle($event: any) {
    this.expanded = $event;
    this.onAdvancedSearchChanged(this.advancedFormData);
    // hide/unhide attributes for next search
    if (this.expanded) {
      this.attributesOutput.next(this.data.attributes);
    } else {
      this.attributesOutput.next([]);
    }
  }
  public copyToClipboard(event) {
    this.copyNonMatchingParametersToClipboard.next(this.listsearch);
  }
}
<nm-widgetframe
  [header]="header"
  [configuration]="configuration"
  [infoTitle]="title"
  [infoText]="
    listsearch ? configuration.configuration.listSearchInfoText : infoText
  "
  [infoWidth]="configuration.configuration.infoWidth"
  [infoHeight]="configuration.configuration.infoHeight"
  [widgetId]="id"
  [infoPlacement]="'bottom'"
  [wikiLink]="wikiLink"
  [isCollapsible]="isCollapsible"
>
  <div slot="title" class="nm-widgetframe__title">
    {{
      (listsearch ? configuration.configuration.listSearchTitle : title)
        | translate
    }}
  </div>
  <div slot="buttons" class="nm-widgetframe__buttons">
    <mat-slide-toggle
      matTooltip="{{ 'infotext.switchsearchmode' | translate }}"
      matTooltipShowDelay="500"
      [(ngModel)]="listsearch"
      (change)="toggleMode($event)"
    >
    </mat-slide-toggle>
  </div>
  <div slot="content" class="nm-widgetframe__content basic-search">
    <div>
      <form
        [class.hidden]="listsearch"
        [formGroup]="form"
        (ngSubmit)="onSubmit()"
        autocomplete="off"
      >
        <div>
          <div *ngFor="let field of fields" class="form-row">
            <ng-container *ngIf="field.basic">
              <div class="form-input">
                <ng-container
                  *ngIf="rangeModes[field.identifier]; else noRange"
                >
                  <div class="range-left">
                    <nm-form-widget-input
                      [field]="field"
                      [lookups]="lookups"
                      (event)="onInputEvent($event)"
                      (enterPressed)="submit()"
                      [locale]="currentLocale"
                      [placeholder]="
                        ('from' | translate) +
                        ' ' +
                        (field.description | translate)
                      "
                      [formControlName]="field.identifier + 'From'"
                      [autofocus]="field.autofocus"
                    ></nm-form-widget-input>
                  </div>
                  <div class="range-right">
                    <nm-form-widget-input
                      [field]="field"
                      [lookups]="lookups"
                      (event)="onInputEvent($event)"
                      (enterPressed)="submit()"
                      [locale]="currentLocale"
                      [placeholder]="
                        ('to' | translate) +
                        ' ' +
                        (field.description | translate)
                      "
                      [formControlName]="field.identifier + 'Until'"
                      [autofocus]="field.autofocus"
                    ></nm-form-widget-input>
                  </div>
                </ng-container>
                <ng-template #noRange>
                  <nm-form-widget-input
                    [field]="field"
                    (event)="onInputEvent($event)"
                    (enterPressed)="submit()"
                    [lookups]="lookups"
                    [placeholder]="field.description | translate"
                    [locale]="currentLocale"
                    [formControlName]="formcontrolNameFor(field)"
                    [autofocus]="field.autofocus"
                  ></nm-form-widget-input>
                </ng-template>
              </div>
              <div class="range-switch" *ngIf="field.allowRange">
                <mat-slide-toggle
                  [checked]="rangeModes[field.identifier]"
                  (change)="rangeChange($event, field)"
                ></mat-slide-toggle>
              </div>
            </ng-container>
          </div>
        </div>
      </form>
    </div>
    <div [class.hidden]="!listsearch">
      <form [formGroup]="listSearchForm" autocomplete="off">
        <nm-form-widget-input
          [field]="listsearchmodeField"
          [lookups]="lookups"
          [placeholder]="listsearchmodeField.description | translate"
          [locale]="currentLocale"
          [formControlName]="formcontrolNameFor(listsearchmodeField)"
        ></nm-form-widget-input>
        <mat-form-field>
          <textarea
            matInput
            #searchNumberList
            id="searchNumberList"
            formControlName="list"
            [placeholder]="'searchlist.placeholder' | translate"
            [ngStyle]="{ 'max-height': computeNumberListMaxHeight() }"
          >
          </textarea>
        </mat-form-field>
      </form>
      <nm-button
        [disabled]="exportButtonDisabled"
        (fireEvent)="copyToClipboard($event)"
        class="nm-search__footerButton"
        [buttonType]="'mat-icon-button'"
        [pTooltip]="'ipim.web.search.errors.export.tooltip' | translate"
        [icon]="'article'"
      >
      </nm-button>
    </div>
    <div class="spacer"></div>
    <nm-search-advanced
      [lookups]="configuration.configuration.lookups"
      [fields]="configuration.configuration.fields"
      [links]="configuration._links"
      [locale]="locale"
      (search)="onAdvancedSearchChanged($event)"
      (attributes)="onAttributesChanged($event)"
      (expandToggle)="onExpandToggle($event)"
      [reset]="resetOutput"
      [prefix]="configuration.configuration.localstoragePrefix"
      [attributeUrl]="configuration.configuration.attributeUrl"
      [selectFilterParams]="configuration.configuration.selectFilterParams"
      [dataInput]="dataOutput"
      [searchFavorites]="searchFavorites"
      [attributeTypesToAddMultiple]="attributeTypesToAddMultiple"
      [dialogWithActionMenu]="true"
      [listsearch]="listsearch"
      [searchModeChanged]="searchModeChanged"
      [listHeight]="listHeight"
      (listvalueOutput)="onListValueChange($event)"
      (templateOutput)="value.next($event)"
      [enableRoleBasedSearchTemplates]="enableRoleBasedSearchTemplates"
    >
    </nm-search-advanced>
    <div style="clear: both"><br /></div>
    <div #nmSearchFooter class="nm-search__footer">
      <form
        [formGroup]="searchModeForm"
        class="nm-search__searchModeInput"
        autocomplete="off"
      >
        <div class="form-input">
          <nm-form-widget-input
            [field]="searchModeField"
            (event)="onInputEvent($event)"
            [lookups]="lookups"
            [placeholder]="searchModeField.description | translate"
            [locale]="currentLocale"
            [formControlName]="formcontrolNameFor(searchModeField)"
          ></nm-form-widget-input>
        </div>
      </form>
      <div style="flex-grow: 1"></div>
      <button
        mat-raised-button
        color="primary"
        class="nm-search__footerButton"
        (click)="submit()"
      >
        {{ "placeholder.search" | translate }}
      </button>
      <button
        mat-button
        type="button"
        class="nm-search__footerButton"
        style="margin-right: 16px"
        (click)="resetClick()"
      >
        {{ "button.reset" | translate }}
      </button>
    </div>
  </div>
</nm-widgetframe>