@WidgetComponent

nm-search-article-selection

File

src/app/shared/widgets/search/article-selection/search-article-selection.component.ts

Implements

OnDestroy OnInit

Metadata

selector nm-search-article-selection
styleUrls search-article-selection.component.scss
templateUrl ./search-article-selection.component.html

Index

Widget inputs
Widget outputs
Properties
Methods
Inputs
Outputs

Constructor

constructor(widgetframeService: WidgetframeService, authHttp: HttpClient, dialog: MatDialog, _translateService: TranslateService, localStorageService: LocalStorageService)
Parameters :
Name Type Optional
widgetframeService WidgetframeService no
authHttp HttpClient no
dialog MatDialog no
_translateService TranslateService no
localStorageService LocalStorageService no

Inputs

articles
articlesFilterUri
dimensionAttributesUri
disabled
locale
maxHeight
selections

Outputs

selections $event type: Subject

Methods

Private getSavedFilterValues
getSavedFilterValues()
Returns : any
Private initializeDialogData
initializeDialogData(dimensionAttrs: any, configuration: literal type)
Parameters :
Name Type Optional
dimensionAttrs any no
configuration literal type no
Returns : any
Private mapArticlesToArticleDto
mapArticlesToArticleDto(articles: )
Parameters :
Name Optional
articles no
Returns : {}
Private mapAttributesToAV
mapAttributesToAV(attributes: )
Parameters :
Name Optional
attributes no
Returns : {}
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
onCheckboxValueChange
onCheckboxValueChange(selectEvent: )
Parameters :
Name Optional
selectEvent no
Returns : void
onSelectionChange
onSelectionChange()
Returns : void
openFilterDialog
openFilterDialog()
Returns : void
Private reApplyFilters
reApplyFilters(articles: any)
Parameters :
Name Type Optional
articles any no
Returns : boolean
Public resetFilter
resetFilter()
Returns : void
selectArticles
selectArticles(articles: )
Parameters :
Name Optional
articles no
Returns : void
Private selectFilteredResults
selectFilteredResults(filters: any, overwriteLocalStorage: )
Parameters :
Name Type Optional Default value
filters any no
overwriteLocalStorage no true
Returns : void
Private updateSelectedArticlesLocalStorage
updateSelectedArticlesLocalStorage()
Returns : void

Properties

Private _articles
_articles:
Public dialog
dialog: MatDialog
Type : MatDialog
Public indeterminateCheckbox
indeterminateCheckbox: boolean
Type : boolean
Public isSelectAll
isSelectAll: boolean
Type : boolean
Private localeChannel
localeChannel: Subject<any>
Type : Subject<any>
Default value : new Subject<any>()
Public savedFilterValues
savedFilterValues: LocalStorageEntry
Type : LocalStorageEntry
Private selectedArticlesEntry
selectedArticlesEntry: LocalStorageEntry
Type : LocalStorageEntry
Public selectedOptions
selectedOptions:
Private selectionInitialised
selectionInitialised:
Default value : false
Public totalSubject
totalSubject:
Default value : new BehaviorSubject<number>(null)
Private unsubscribe
unsubscribe:
Default value : NgUnsubscribe.create()

Accessors

articles
getarticles()
setarticles(articles: )
Parameters :
Name Optional
articles no
Returns : void
selectedItems
setselectedItems(items: )
Parameters :
Name Optional
items no
Returns : void
locale
setlocale(value: )
Parameters :
Name Optional
value no
Returns : void
import { Component, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { TranslateService } from "@ngx-translate/core";
import { Subject, BehaviorSubject } from "rxjs";
import { takeUntil } from "rxjs/operators";
import { MatDialog } from "@angular/material/dialog";
import { HttpClient } from "@angular/common/http";
import { NgUnsubscribe } from "../../../ng-unsubscribe";
import {
  LocalStorageEntry,
  LocalStorageService,
} from "../../../components/local-storage/local-storage.service";
import { angularWidgetBridgeInput } from "../../../components/util/util.service";
import { WidgetframeService } from "../../widgetframe/widgetframe.service";
import {
  Scope,
  DeletionMode,
} from "../../../components/local-storage/local-storage-constants";
import { AttributeBulkEditDialog } from "../../../components/attribute-bulk-edit-dialog";

@Component({
  selector: "nm-search-article-selection",
  templateUrl: "./search-article-selection.component.html",
  styleUrls: ["./search-article-selection.component.scss"],
})
export class SearchArticleSelectionComponent implements OnDestroy, OnInit {
  private unsubscribe = NgUnsubscribe.create();

  public selectedOptions;
  public indeterminateCheckbox: boolean;
  public isSelectAll: boolean;
  public savedFilterValues: LocalStorageEntry;
  private selectedArticlesEntry: LocalStorageEntry;
  private localeChannel: Subject<any> = new Subject<any>();
  public totalSubject = new BehaviorSubject<number>(null);

  private _articles;

  @Input("articles")
  public set articles(articles) {
    this.indeterminateCheckbox = false;
    this.isSelectAll = false;
    this._articles = articles;
    if (!this.reApplyFilters(this.articles) && !this.selectionInitialised) {
      if (this.selectedOptions && this.selectedOptions.length !== 0) {
        this.selections.next(this.selectedOptions);
      }
    }
    this.selectionInitialised = true;
    this.totalSubject.next(null);
  }

  public get articles() {
    return this._articles;
  }
  @Input() public dimensionAttributesUri;
  @Input() public articlesFilterUri;
  @Input() public maxHeight;
  @Input() public disabled;

  @Input("selections") public set selectedItems(items) {
    if (items) {
      this.selectArticles(items);
    }
  }

  @Output()
  public selections = new Subject<string[]>();

  @Input("locale")
  public set locale(value) {
    angularWidgetBridgeInput(value, this.localeChannel, this.unsubscribe);
  }

  constructor(
    private widgetframeService: WidgetframeService,
    private authHttp: HttpClient,
    public dialog: MatDialog,
    private _translateService: TranslateService,
    localStorageService: LocalStorageService
  ) {
    // this.savedFilterValues = localStorageService.getLocalStorageEntryByConfig(filterValuesLocalStorageConfig);
    this.selectedArticlesEntry = localStorageService.getLocalStorageEntry(
      "searchSelectedArticles",
      Scope.GLOBAL,
      DeletionMode.LOGIN
    );

    // We need to set this before the @Inputs are validated so we can set this when the articles get inputted
    if (this.selectedArticlesEntry.exists()) {
      this.selectedOptions = JSON.parse(this.selectedArticlesEntry.value);
    }
  }

  private selectionInitialised = false;
  ngOnInit() {
    this.localeChannel
      .asObservable()
      .pipe(takeUntil(this.unsubscribe))
      .subscribe(() => {
        if (!this.reApplyFilters(this.articles) && !this.selectionInitialised) {
          this.selections.next(this.selectedOptions);
        }

        this.selectionInitialised = true;
      });
  }

  ngOnDestroy(): void {
    this.unsubscribe.destroy();
  }

  onSelectionChange() {
    this.totalSubject.next(this.selectedOptions.length);
    this.selections.next(this.selectedOptions);
    this.updateSelectedArticlesLocalStorage();
    this.indeterminateCheckbox = true;
    if (this.selectedOptions.length === 0) {
      this.indeterminateCheckbox = false;
      this.isSelectAll = false;
    }
  }

  private updateSelectedArticlesLocalStorage() {
    this.selectedArticlesEntry.value = JSON.stringify(this.selectedOptions);
  }

  openFilterDialog() {
    let configuration: { [key: string]: any } = {};
    this.widgetframeService
      .getData(this.dimensionAttributesUri)
      .subscribe((dimensionAttrs) => {
        const dialogRef = this.initializeDialogData(
          dimensionAttrs,
          configuration
        );

        dialogRef.afterClosed().subscribe((filters) => {
          if (filters) {
            this.savedFilterValues.value = JSON.stringify(filters);
            this.selectFilteredResults(filters);
          }
        });
      });
  }

  private mapArticlesToArticleDto(articles) {
    let articleDtos = [];
    articles.forEach((article) => {
      let articleDto = {};
      articleDto["type"] = article.type;
      articleDto["identifier"] = article.identifier;
      articleDto["attributes"] = article.attributes;
      if (articleDto["attributes"].length > 0) {
        articleDtos.push(articleDto);
      }
    });
    return articleDtos;
  }

  private mapAttributesToAV(attributes) {
    if (!attributes) {
      return [];
    }
    let attrs = [];

    attributes.forEach((attribute) => {
      let attr = {};
      attr["type"] = attribute.type;
      attr["identifier"] = attribute.identifier;
      attr["values"] = attribute.values.map((value) =>
        Object.assign(value.value)
      );
      attrs.push(attr);
    });
    return attrs;
  }

  private selectFilteredResults(filters: any, overwriteLocalStorage = true) {
    console.debug(overwriteLocalStorage);
    if (!overwriteLocalStorage) {
      let isNew = true;
      // Check if the data we got is a new product or the old one - if it is the old one dont select anything new!

      for (let entry of this.articles) {
        if (this.selectedOptions.indexOf(entry.identifier) !== -1) {
          isNew = false;
          break;
        }
      }
      if (!isNew) {
        this.selectArticles(
          this.articles.filter(
            (entry) => this.selectedOptions.indexOf(entry.identifier) !== -1
          )
        );
        return;
      }
    }
    let filteringDto = {
      filters: filters,
      articles: this.mapArticlesToArticleDto(this.articles),
    };

    this.widgetframeService
      .postData(this.articlesFilterUri, JSON.stringify(filteringDto))
      .subscribe((filteredResult) => {
        if (filteredResult.length > 0) {
          this.indeterminateCheckbox = true;
        } else {
          this.indeterminateCheckbox = false;
          this.isSelectAll = false;
        }

        this.selectArticles(filteredResult);
      });
  }

  private initializeDialogData(
    dimensionAttrs: any,
    configuration: { [key: string]: any }
  ) {
    let savedValues = this.getSavedFilterValues();

    let entries = [dimensionAttrs._embedded];
    dimensionAttrs._embedded.attributes.forEach((attribute) => {
      configuration[attribute.identifier] = {
        header: attribute.description,
        active: true,
        required: true,
      };
    });
    const attributeValues: any = {};
    entries.forEach((entry) => {
      entry.attributes.forEach((attribute) => {
        if (attribute.type === "LOOKUP") {
          attribute.type = "MULTI_LOOKUP";
        }
      });
    });

    this._articles.forEach((article) => {
      article.attributes.forEach((attribute) => {
        if (
          attribute.source &&
          attribute.source[0] &&
          attribute.source[0].value
        ) {
          if (!attributeValues[attribute.identifier]) {
            attributeValues[attribute.identifier] = new Set();
          }
          attributeValues[attribute.identifier].add(attribute.source[0].value);
        }
      });
    });

    // Filter out all attributes that are some kind of lookup and reduce the lookup values to the possible ones
    entries.forEach((entry) => {
      entry.attributes = entry.attributes.filter((attribute) => {
        if (!attributeValues[attribute.identifier]) {
          return false;
        }
        if (attribute.type !== "MULTI_LOOKUP") {
          return true;
        }

        const values = Array.from(attributeValues[attribute.identifier]);
        attribute._links["values"].href =
          attribute._links["values"].href +
          `?filter=identifier:in:"${values.join(",")}"`;
        return true;
      });
    });

    // Recover values from local storage
    if (savedValues && savedValues.length > 0) {
      entries[0].attributes.forEach((attribute) => {
        const recoverAttribute = savedValues.find(
          (entry) => entry.identifier === attribute.identifier
        );
        if (!recoverAttribute) {
          return;
        }
        if (attribute.type === "MULTI_LOOKUP") {
          const values = Array.from(attributeValues[attribute.identifier]);
          attribute.source = recoverAttribute.source.filter((entry) =>
            values.find((value) => value === entry.value)
          );
          return;
        }
        attribute.source = recoverAttribute.source;
      });
    }

    const dialogRef = this.dialog.open(AttributeBulkEditDialog);
    dialogRef.componentInstance["title"] = this._translateService.instant(
      "label.selection.filter"
    );
    dialogRef.componentInstance["entries"] = entries;
    dialogRef.componentInstance["fieldConfigurations"] = configuration;
    dialogRef.componentInstance["infoText"] = this._translateService.instant(
      "apps.priceapp.filtering.info.text"
    );
    dialogRef.componentInstance["infoTitle"] =
      "visible-attribute-selection.edit.infoTitle";
    dialogRef.componentInstance["infoPlacement"] = "'bottom'";
    dialogRef.componentInstance["acceptButtonText"] =
      "apps.priceapp.button.filter";
    return dialogRef;
  }

  private getSavedFilterValues() {
    let savedValues = this.savedFilterValues.exists()
      ? JSON.parse(this.savedFilterValues.value)
      : null;
    return savedValues;
  }

  selectArticles(articles) {
    let articleIdentifiers = articles.map((article) => article.identifier);
    this.selectedOptions = articleIdentifiers;
    this.totalSubject.next(this.selectedOptions.length);
    this.selections.next(this.selectedOptions);
    this.updateSelectedArticlesLocalStorage();

    if (articles.length === 0) {
      return;
    }

    if (articles.length === this.articles.length) {
      this.isSelectAll = true;
    } else {
      this.indeterminateCheckbox = true;
    }
  }

  onCheckboxValueChange(selectEvent) {
    this.indeterminateCheckbox = false;
    if (selectEvent.checked) {
      this.selectArticles(this.articles);
      this.totalSubject.next(this.selectedOptions.length);
    } else {
      this.selectArticles([]);
      this.totalSubject.next(null);
    }
  }

  // Returns true if filters were applied, else false
  private reApplyFilters(articles: any): boolean {
    let savedValues = this.getSavedFilterValues();
    if (savedValues && articles) {
      this.selectFilteredResults(savedValues, false);
      return true;
    }
    return false;
  }

  public resetFilter() {
    this.selectArticles([]);
    this.savedFilterValues.clear();
    this.indeterminateCheckbox = false;
  }
}
<div *ngIf="articles && articles.length > 0" style="clear: both">
  <br />
  <mat-checkbox
    (change)="onCheckboxValueChange($event)"
    [indeterminate]="indeterminateCheckbox"
    [ngClass]="articles.length > 12 ? 'overflowSelect' : 'regularSelect'"
    [labelPosition]="'before'"
    [checked]="isSelectAll"
    [disabled]="disabled"
    [pTooltip]="'label.select.all' | translate"
    [showDelay]="300"
    ><span
      >&nbsp;
      <ng-container *ngIf="totalSubject | async as totalValue"
        >&nbsp;({{ totalValue }})</ng-container
      ></span
    ></mat-checkbox
  >

  <mat-icon
    style="cursor: pointer; color: rgba(0, 0, 0, 0.54)"
    (click)="!disabled ? resetFilter() : null"
    >delete</mat-icon
  >
  <button
    class="config-button"
    [disabled]="disabled"
    (click)="openFilterDialog()"
    mat-button
  >
    <mat-icon class="iconsNav" [svgIcon]="'filter-variant-plus'"></mat-icon>
    {{ "label.selection.filter" | translate }}
  </button>

  <br />
  <br />
</div>
<mat-selection-list
  class="nm-selection-list"
  [ngStyle]="{ 'max-height': maxHeight }"
  (selectionChange)="onSelectionChange()"
  [(ngModel)]="selectedOptions"
>
  <mat-list-option
    *ngFor="let entry of articles"
    [value]="entry.identifier"
    [disabled]="disabled"
  >
    <nm-ellipsis [content]="entry.description"></nm-ellipsis>
  </mat-list-option>
</mat-selection-list>
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""