File

src/app/shared/widgets/search/advanced/search-advanced.component.ts

Index

Widget inputs
Widget outputs
Properties

Properties

Optional allowRange
allowRange: boolean
Type : boolean

true enables the range selection, false disables it

Optional autofocus
autofocus: boolean
Type : boolean
Optional basic
basic: boolean
Type : boolean
Optional default
default: string
Type : string
Optional defaultVisible
defaultVisible: boolean
Type : boolean
description
description: string
Type : string
Optional eventField
eventField: boolean
Type : boolean
Optional formatDate
formatDate: boolean
Type : boolean
identifier
identifier: string
Type : string
Optional lookupSource
lookupSource: string
Type : string
Optional max
max: number | null
Type : number | null
Optional min
min: number | null
Type : number | null
range
range: "never" | "choose" | "always"
Type : "never" | "choose" | "always"

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

type
type: string
Type : string
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();
  }
}

results matching ""

    No results matching ""