nm-category-select-control
src/app/shared/widgets/search-app-search-widget/category-select-control/category-select-control.component.ts
changeDetection | ChangeDetectionStrategy.OnPush |
selector | nm-category-select-control |
styleUrls | category-select-control.component.scss |
templateUrl | ./category-select-control.component.html |
Widget inputs |
Widget outputs |
Properties |
|
Methods |
Inputs |
Outputs |
constructor(widgetframeService: WidgetframeService, store: AppdataStore, cd: ChangeDetectorRef, currentLocaleService: CurrentLocaleService)
|
|||||||||||||||
Parameters :
|
autofocus
|
Type:
Default value: |
categoryPlaceholder
|
Type: |
categoryUrl
|
|
filterChannelTypes
|
Type: |
includeSubCategories
|
Default value: |
initialCategoryValues
|
Type: |
initPublication
|
|
localStorageRecovery
|
Default value: |
multi
|
Default value: |
placeholder
|
Type: |
preferredPosition
|
Default value: |
readonly
|
Type: |
selectedPublication
|
|
showIncludeSubCategories
|
Default value: |
showPublicationDropdown
|
Default value: |
value
|
|
change
|
$event type: EventEmitter
|
includeSubCategoriesChange
|
$event type: EventEmitter
|
Private configureTreeWidget |
configureTreeWidget()
|
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
onIncludeSubCategoriesChange |
onIncludeSubCategoriesChange()
|
Returns :
void
|
onPublicationChange | ||||||
onPublicationChange(fromSelect: )
|
||||||
Parameters :
Returns :
void
|
Private recoverPublication |
recoverPublication()
|
Returns :
void
|
resetValue | ||||
resetValue(event: )
|
||||
Parameters :
Returns :
boolean
|
Private _isRecovering |
_isRecovering:
|
Type : boolean
|
Default value : false
|
Public _selectedPublication |
_selectedPublication:
|
Private _value |
_value:
|
Private elementRef |
elementRef:
|
Type : ElementRef
|
Decorators : ViewChild
|
Public filter |
filter:
|
Type : LookupFilter
|
Public filterCtrl |
filterCtrl:
|
Type : FormControl
|
Default value : new FormControl()
|
Private initializing |
initializing:
|
Default value : true
|
Public publications |
publications:
|
Type : []
|
Default value : []
|
Private staticPublication |
staticPublication:
|
Default value : false
|
treeComponent |
treeComponent:
|
Type : SelectTreeWidgetComponent
|
Decorators : ViewChild
|
Private unsubscribe |
unsubscribe:
|
Default value : NgUnsubscribe.create()
|
value | ||||
setvalue(value: )
|
||||
Parameters :
Returns :
void
|
selectedPublication | ||||
setselectedPublication(pub: )
|
||||
Parameters :
Returns :
void
|
initPublication | ||||
setinitPublication(publication: )
|
||||
Parameters :
Returns :
void
|
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
Input,
OnDestroy,
Output,
ViewChild,
} from "@angular/core";
import { FormControl } from "@angular/forms";
import { WidgetframeService } from "../../widgetframe/widgetframe.service";
import { AppdataStore } from "../../../components/appdata/appdata.store";
import { combineLatest, of } from "rxjs";
import { SelectTreeWidgetComponent } from "../../select-tree";
import { WidgetConfig } from "../../../widgets/widget.configuration";
import { NgUnsubscribe } from "../../../ng-unsubscribe";
import { flatMap, map, takeUntil } from "rxjs/operators";
import { CurrentLocaleService } from "../../../components/i18n/currentLocale.service";
import { LookupFilter } from "../../../util/lookup.filter";
import { getOrDefault } from "../../widget.configuration";
@Component({
selector: "nm-category-select-control",
templateUrl: "./category-select-control.component.html",
styleUrls: ["./category-select-control.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CategorySelectControlComponent implements OnDestroy {
private unsubscribe = NgUnsubscribe.create();
@Input()
public placeholder: string;
@Input()
public categoryPlaceholder: string;
@Input()
public showPublicationDropdown = true;
@Input()
public localStorageRecovery = true;
@Input()
public showIncludeSubCategories = false;
@Input()
public includeSubCategories = false;
@Input()
public multi = true;
@Input()
public autofocus: boolean = false;
@Input()
public filterChannelTypes: string[];
@Input()
public categoryUrl;
@Input()
public preferredPosition = ["combo-bottom", "combo-top"];
@Input()
public readonly: boolean;
@Input() public set value(value) {
if (value === this._value) {
return;
}
if (value && this._value) {
if (
value.publication === this._value.publication &&
value.category === this._value.category
) {
return;
}
}
if (this._value && !value) {
this._selectedPublication = null;
this.treeComponent.resetChannel.next();
}
this._value = value;
if (value && value.category) {
this.recoverPublication();
}
}
@Input() public set selectedPublication(pub) {
this._selectedPublication = pub;
this.staticPublication = true;
this.onPublicationChange(true);
}
@Input() public set initPublication(publication) {
if (publication) {
this._selectedPublication = publication;
this.staticPublication = true;
this.treeComponent.rootCategoryLink = publication._links.self.href;
this.treeComponent.rootSearchCategoryLink =
publication._links.search.href;
this.treeComponent.reloadChannel.next();
this.initializing = false;
}
}
@Input() public initialCategoryValues: any[];
@Output() public change = new EventEmitter<{
publication: any;
category: any;
paths: string[][];
}>();
@Output() public includeSubCategoriesChange = new EventEmitter<any>();
public publications = [];
public _selectedPublication;
private staticPublication = false;
private initializing = true;
private _value;
public filter: LookupFilter;
public filterCtrl: FormControl = new FormControl();
@ViewChild("tree", { read: SelectTreeWidgetComponent, static: true })
treeComponent: SelectTreeWidgetComponent;
@ViewChild("input") private elementRef: ElementRef;
private _isRecovering: boolean = false;
constructor(
private widgetframeService: WidgetframeService,
private store: AppdataStore,
private cd: ChangeDetectorRef,
private currentLocaleService: CurrentLocaleService
) {}
private recoverPublication() {
//We are pre ngOnInit so the filter isnt setup yet. We will do this again in the init
if (!this.filter) {
return;
}
const publication = this.filter
.getLookups()
.find((publication) => publication.identifier == this._value.publication);
if (publication === this._selectedPublication) {
return;
}
this._isRecovering = true;
if (publication) {
this._selectedPublication = publication;
this.onPublicationChange(false);
this.cd.markForCheck();
if (this._value.category) {
this.treeComponent.selectionPath.next(this._value.paths);
}
}
this._isRecovering = false;
}
ngOnInit(): void {
this.filter = new LookupFilter("description");
this.placeholder = getOrDefault(this.placeholder, "table.head.taxonomy");
this.categoryPlaceholder = getOrDefault(
this.categoryPlaceholder,
"table.head.category"
);
const linkObs = this.categoryUrl
? of(this.categoryUrl)
: this.store
.getAppdata()
.pipe(
map((data) => data.ipim._links["category-select-publications"].href)
);
combineLatest([linkObs, this.currentLocaleService.getCurrentLocale()])
.pipe(
map((data) => data[0]),
flatMap((link) => this.widgetframeService.getData(link))
)
.subscribe((data) => {
const dataType = data.type;
this.publications =
this.filterChannelTypes && this.filterChannelTypes.length > 0
? data._embedded[dataType].filter(
(publication) =>
this.filterChannelTypes.indexOf(publication["channel-type"]) >
-1
)
: data._embedded[dataType];
this.filter.setLookups(this.publications);
if (this.staticPublication) {
return;
}
if (this._value) {
this.recoverPublication();
this.cd.markForCheck();
} else {
if (this._selectedPublication) {
this._selectedPublication = null;
this.onPublicationChange(true);
this.cd.markForCheck();
}
}
});
this.configureTreeWidget();
if (this.initialCategoryValues && this.initialCategoryValues.length > 0) {
const categories = this.initialCategoryValues
.filter((source) => source.id && source.value)
.map((source) => {
return { id: source.id, description: source.value };
});
this.treeComponent.initCategoryFromInput(categories);
}
}
private configureTreeWidget() {
const treeConfig: WidgetConfig = {
id: "category-select-edit-tree",
component: "",
configuration: {
"localstorage-category": this.localStorageRecovery
? "category-select-edit-tree"
: null,
"local-storage-key": this.localStorageRecovery
? "category-select-edit-tree-key"
: null,
dataType: "products",
placeholder: this.categoryPlaceholder,
multi: this.multi,
triggerAsChipList: true,
},
};
this.treeComponent.configuration = treeConfig;
this.treeComponent.configureWidget(treeConfig);
this.treeComponent.selectionEmitter
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((selection) => {
if (this._isRecovering) {
return;
}
const description = selection
.map((value) => value.description)
.join(" | ");
const category = selection.map((value) => value.category);
const paths = selection.map((value) => value.path);
const rootNode = selection.length > 0 ? selection[0].root : null;
if (!this._value) {
this._value = {};
}
this._value.category = category;
this._value.paths = paths;
if (this._selectedPublication) {
const data = {
publication: this._selectedPublication.identifier,
publicationDescription: this._selectedPublication.description,
category,
categoryDescription: description,
includeSubCategories: this.includeSubCategories,
rootNode: rootNode,
paths,
};
this.change.next(data);
}
});
}
onPublicationChange(fromSelect = true) {
if (this._selectedPublication) {
this.treeComponent.rootCategoryLink = this._selectedPublication._links.self.href;
this.treeComponent.rootSearchCategoryLink = this._selectedPublication._links.search?.href;
if (!fromSelect && this.initializing) {
this.treeComponent.reloadChannel.next();
} else {
this.treeComponent.resetChannel.next();
}
} else {
// clear selection from tree, if the publication is reset
this.treeComponent.rootCategoryLink = null;
this.treeComponent.rootSearchCategoryLink = null;
this.treeComponent.resetChannel.next();
this.change.next({ publication: null, category: null, paths: null });
}
this.initializing = false;
}
resetValue(event) {
this._selectedPublication = null;
this.treeComponent.resetChannel.next();
event.preventDefault();
event.stopPropagation();
this.change.next({ publication: null, category: null, paths: null });
return false;
}
onIncludeSubCategoriesChange() {
this.includeSubCategoriesChange.emit(this.includeSubCategories);
}
ngOnDestroy(): void {
this.unsubscribe.destroy();
}
}
<div class="nm-categorySelector">
<div class="nm-categorySelector__publication" *ngIf="showPublicationDropdown">
<mat-form-field [ngClass]="{ virgin: !_selectedPublication }">
<mat-select
class="{{ _selectedPublication ? 'with-clear-button' : '' }}"
[(value)]="_selectedPublication"
(selectionChange)="onPublicationChange()"
[placeholder]="placeholder | translate"
(keydown.backspace)="resetValue($event)"
[nmAutofocus]="autofocus"
>
<mat-select-trigger *ngIf="_selectedPublication">
{{ _selectedPublication.description }}
<div
class="nm-categorySelector__selectedRowChip"
*ngIf="_selectedPublication.chips"
>
<nm-chip
*ngFor="let chip of _selectedPublication.chips"
class="nm-select-chips__chip"
[modifier]="chip.color"
[content]="chip.value"
[toUpperCase]="true"
>
</nm-chip>
</div>
</mat-select-trigger>
<mat-option>
<ngx-mat-select-search
placeholderLabel="{{ 'placeholder.search' | translate }}"
[formControl]="filter.getFilterControl()"
noEntriesFoundLabel="{{ 'select.no.options' | translate }}"
(keydown.escape)="selectSearch.matSelect.focus()"
#selectSearch
>
</ngx-mat-select-search>
</mat-option>
<mat-option></mat-option>
<mat-option
*ngFor="let publication of filter.getFilteredLookups() | async"
[value]="publication"
>
{{ publication.description }}
<div class="nm-categorySelector__chip" *ngIf="publication.chips">
<nm-chip
*ngFor="let chip of publication.chips"
class="nm-select-chips__chip"
[modifier]="chip.color"
[content]="chip.value"
[toUpperCase]="true"
>
</nm-chip>
</div>
</mat-option>
</mat-select>
<button
mat-icon-button
matSuffix
aria-label="Clear"
color="primary"
class="clear-button"
*ngIf="_selectedPublication"
(click)="resetValue($event)"
tabIndex="-1"
>
<mat-icon color="primary" class="fade-in">close</mat-icon>
</button>
</mat-form-field>
</div>
<div
class="nm-categorySelector__tree"
[class.edit-tree]="showPublicationDropdown"
>
<nm-edit-tree
#tree
[disabled]="readonly || !_selectedPublication"
[filterable]="true"
[preferredPosition]="preferredPosition"
>
</nm-edit-tree>
</div>
<mat-checkbox
*ngIf="showIncludeSubCategories"
[(ngModel)]="includeSubCategories"
class="nm-categorySelector__checkbox"
color="primary"
(ngModelChange)="onIncludeSubCategoriesChange()"
>
<span> {{ "label.include-sub-categories" | translate }} </span>
</mat-checkbox>
</div>