nm-search-article-selection
src/app/shared/widgets/search/article-selection/search-article-selection.component.ts
selector | nm-search-article-selection |
styleUrls | search-article-selection.component.scss |
templateUrl | ./search-article-selection.component.html |
Widget inputs |
Widget outputs |
Properties |
|
Methods |
|
Inputs |
Outputs |
constructor(widgetframeService: WidgetframeService, authHttp: HttpClient, dialog: MatDialog, _translateService: TranslateService, localStorageService: LocalStorageService)
|
||||||||||||||||||
Parameters :
|
articles
|
|
articlesFilterUri
|
|
dimensionAttributesUri
|
|
disabled
|
|
locale
|
|
maxHeight
|
|
selections
|
|
selections
|
$event type: Subject
|
Private getSavedFilterValues |
getSavedFilterValues()
|
Returns :
any
|
Private initializeDialogData | |||||||||
initializeDialogData(dimensionAttrs: any, configuration: literal type)
|
|||||||||
Parameters :
Returns :
any
|
Private mapArticlesToArticleDto | ||||
mapArticlesToArticleDto(articles: )
|
||||
Parameters :
Returns :
{}
|
Private mapAttributesToAV | ||||
mapAttributesToAV(attributes: )
|
||||
Parameters :
Returns :
{}
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
onCheckboxValueChange | ||||
onCheckboxValueChange(selectEvent: )
|
||||
Parameters :
Returns :
void
|
onSelectionChange |
onSelectionChange()
|
Returns :
void
|
openFilterDialog |
openFilterDialog()
|
Returns :
void
|
Private reApplyFilters | ||||||
reApplyFilters(articles: any)
|
||||||
Parameters :
Returns :
boolean
|
Public resetFilter |
resetFilter()
|
Returns :
void
|
selectArticles | ||||
selectArticles(articles: )
|
||||
Parameters :
Returns :
void
|
Private selectFilteredResults | ||||||||||||
selectFilteredResults(filters: any, overwriteLocalStorage: )
|
||||||||||||
Parameters :
Returns :
void
|
Private updateSelectedArticlesLocalStorage |
updateSelectedArticlesLocalStorage()
|
Returns :
void
|
Private _articles |
_articles:
|
Public dialog |
dialog:
|
Type : MatDialog
|
Public indeterminateCheckbox |
indeterminateCheckbox:
|
Type : boolean
|
Public isSelectAll |
isSelectAll:
|
Type : boolean
|
Private localeChannel |
localeChannel:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Public savedFilterValues |
savedFilterValues:
|
Type : LocalStorageEntry
|
Private selectedArticlesEntry |
selectedArticlesEntry:
|
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()
|
articles | ||||
getarticles()
|
||||
setarticles(articles: )
|
||||
Parameters :
Returns :
void
|
selectedItems | ||||
setselectedItems(items: )
|
||||
Parameters :
Returns :
void
|
locale | ||||
setlocale(value: )
|
||||
Parameters :
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
>
<ng-container *ngIf="totalSubject | async as totalValue"
> ({{ 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>