src/app/shared/widgets/buy/category/category.component.ts
Properties |
|
_links |
_links:
|
Type : any[]
|
Optional |
Category embedded _links, it should contain root category link in this path _links.categories.href |
allowDrag |
allowDrag:
|
Type : boolean
|
Optional |
Allow drag and drop of categories. @default(false) |
allowSelectableAll |
allowSelectableAll:
|
Type : boolean
|
Optional |
Overrides the global statement whether a category is selectable or not. @default(false) |
categoryExpand |
categoryExpand:
|
Type : boolean
|
Optional |
Expand tree categories. @default(false) |
dataType |
dataType:
|
Type : string
|
Optional |
Categories list name in json returned from backend. |
display-include-children-toggle |
display-include-children-toggle:
|
Type : boolean
|
Optional |
Display include children checkbox. @default(false) |
display-scroll-to-top |
display-scroll-to-top:
|
Type : boolean
|
Optional |
Display scroll to top icon. @default(false) |
dynamicHeight |
dynamicHeight:
|
Type : boolean
|
Optional |
Enables dynamicHeight calculation |
dynamicHeightAdditionalHeight |
dynamicHeightAdditionalHeight:
|
Type : string
|
Optional |
Height not filled by category tree |
dynamicTreeHeightInDialog |
dynamicTreeHeightInDialog:
|
Type : boolean
|
Optional |
Dynamic height calculation container is set to |
emitSelectedCategory |
emitSelectedCategory:
|
Type : boolean
|
Optional |
Whether to emit the selected category to "selectedCategory" widget output subject. @default(false) |
includeChildrenValue |
includeChildrenValue:
|
Type : boolean
|
Optional |
Initial value for include children check box. @default(false) |
infoText |
infoText:
|
Type : string
|
Optional |
Information text. |
inline |
inline:
|
Type : boolean
|
Optional |
View category component inline or surrounded by widget frame. @default(false) |
localstorage-category |
localstorage-category:
|
Type : string
|
Optional |
Local storage key for selected category. |
localstorage-content-visible |
localstorage-content-visible:
|
Type : string
|
Optional |
Local storage key for visible content property. |
localstorage-include-children |
localstorage-include-children:
|
Type : string
|
Optional |
Local storage key for include children checkbox value. |
multi |
multi:
|
Type : boolean
|
Optional |
Allow selection of multiple categories. @default(false) |
outputType |
outputType:
|
Type : string
|
Optional |
Output type. @default(uri) |
selectFirstNode |
selectFirstNode:
|
Type : boolean
|
Optional |
Select first node of the category tree. @default(false) |
showAutoSelectRootNodeButton |
showAutoSelectRootNodeButton:
|
Type : boolean
|
Optional |
Show auto select root node button. @default(false) |
showAutoSelectRootNodeButtonLabel |
showAutoSelectRootNodeButtonLabel:
|
Type : string
|
Optional |
Auto select root node button label |
title |
title:
|
Type : string
|
Optional |
Category tree title. |
tree |
tree:
|
Type : CategoryTreeConfiguration
|
Optional |
Configures the category selection tree |
wikiLink |
wikiLink:
|
Type : string
|
Optional |
Wiki help link. |
import {
from,
NEVER,
Observable,
of,
of as observableOf,
ReplaySubject,
Subject,
} from "rxjs";
import {
concatAll,
concatMap,
debounceTime,
distinctUntilChanged,
flatMap,
map,
mapTo,
mergeMap,
skip,
takeUntil,
tap,
scan,
last,
} from "rxjs/operators";
import {
AfterViewChecked,
AfterViewInit,
Component,
EventEmitter,
Input,
Output,
ViewChild,
} from "@angular/core";
import {
AppdataStore,
Appdata,
} from "../../../components/appdata/appdata.store";
import { getOrDefault, WidgetConfig } from "../../widget.configuration";
import {
IActionMapping,
KEYS,
TREE_ACTIONS,
TreeComponent,
} from "angular-tree-component";
import {
WidgetComponent,
WidgetConfiguration,
WidgetConfigure,
WidgetId,
WidgetInput,
WidgetOutput,
} from "../../widget.metadata";
import { Category } from "./index";
import { CategoryResource, CategoryService } from "./category.service";
import { NgUnsubscribe } from "../../../ng-unsubscribe";
import { MatSlideToggleChange } from "@angular/material/slide-toggle";
import { ScrollService } from "../../../components/scroll/scroll.service";
import { TranslateService } from "@ngx-translate/core";
import {
LocalStorageEntry,
LocalStorageService,
} from "../../../components/local-storage/local-storage.service";
import {
DeletionMode,
Scope,
} from "../../../components/local-storage/local-storage-constants";
import { CurrentLocaleService } from "../../../components/i18n/currentLocale.service";
import {
OnDragEvent,
OnDroppedEvent,
SelectionEvent,
} from "../../../components/tree-selector/tree-selector.api";
import { TreeSelectorComponent } from "../../../components/tree-selector/tree-selector.component";
import * as _uriTemplates from "uri-templates";
import { WidgetframeService } from "../../widgetframe/widgetframe.service";
const uriTemplates = _uriTemplates;
declare var jQuery: any;
const actionMapping: IActionMapping = {
mouse: {
contextMenu: (tree, node, $event) => {
$event.preventDefault();
},
dblClick: (tree, node, $event) => {
if (node.hasChildren) TREE_ACTIONS.TOGGLE_EXPANDED(tree, node, $event);
},
click: (tree, node, $event) => {
if (!node.hasChildren) {
$event.ctrlKey
? TREE_ACTIONS.TOGGLE_ACTIVE_MULTI(tree, node, $event)
: TREE_ACTIONS.TOGGLE_SELECTED(tree, node, $event);
}
},
},
keys: {
[KEYS.ENTER]: (tree, node, $event) => {
node.expandAll();
},
},
};
export interface CategoryTreeConfiguration {
/**
* configure the tree's height
*/
height?: number | string;
/**
* `true` to display and enable the tree's filter field
*/
filterable?: boolean;
}
export interface CategoryConfiguration {
/**
* Initial value for include children check box. @default(false)
*/
includeChildrenValue?: boolean;
/**
* Expand tree categories. @default(false)
*/
categoryExpand?: boolean;
/**
* Select first node of the category tree. @default(false)
*/
selectFirstNode?: boolean;
/**
* Information text.
*/
infoText?: string;
/**
* Wiki help link.
*/
wikiLink?: string;
/**
* Category tree title.
*/
title?: string;
/**
* Category embedded _links, it should contain root category link in this path _links.categories.href
*/
_links?: any[];
/**
* Categories list name in json returned from backend.
*/
dataType?: string;
/**
* Local storage key for selected category.
*/
"localstorage-category"?: string;
/**
* Local storage key for include children checkbox value.
*/
"localstorage-include-children"?: string;
/**
* Allow selection of multiple categories. @default(false)
*/
multi?: boolean;
/**
* Output type. @default(uri)
*/
outputType?: string;
/**
* View category component inline or surrounded by widget frame. @default(false)
*/
inline?: boolean;
/**
* Display include children checkbox. @default(false)
*/
"display-include-children-toggle"?: boolean;
/**
* Display scroll to top icon. @default(false)
*/
"display-scroll-to-top"?: boolean;
/**
* Local storage key for visible content property.
*/
"localstorage-content-visible"?: string;
/**
* Show auto select root node button. @default(false)
*/
showAutoSelectRootNodeButton?: boolean;
/**
* Auto select root node button label
*/
showAutoSelectRootNodeButtonLabel?: string;
/**
* Whether to emit the selected category to "selectedCategory" widget output subject. @default(false)
*/
emitSelectedCategory?: boolean;
/**
* Allow drag and drop of categories. @default(false)
*/
allowDrag?: boolean;
/**
* Overrides the global statement whether a category is selectable or not. @default(false)
*/
allowSelectableAll?: boolean;
/**
* Configures the category selection tree
*/
tree?: CategoryTreeConfiguration;
/**
* Enables dynamicHeight calculation
*/
dynamicHeight?: boolean;
/**
* Dynamic height calculation container is set to
*/
dynamicTreeHeightInDialog?: boolean;
/**
* Height not filled by category tree
*/
dynamicHeightAdditionalHeight?: string;
}
/**
* Used in iPIM Buy amd iPIM Apps.
* Surrounded by Widgetframe or diaplayed inline.
*
* Tree component used for category trees.
*/
@WidgetComponent("nm-category")
@Component({
selector: "nm-category",
templateUrl: "./category.component.html",
styleUrls: ["./category.component.scss"],
providers: [CategoryService],
})
export class CategoryWidgetComponent
implements AfterViewChecked, AfterViewInit
{
private currentLevel: number = 0;
private programaticExpansion: boolean = false;
public inputLink: string;
public rootCategoryLink: string;
public dataType: string;
private localstorageSelectedCategoryEntry: LocalStorageEntry;
private localstorageIncludeChildrenEntry: LocalStorageEntry;
private localstorageLastRootUrlEntry: LocalStorageEntry;
private localStorageVisibiltyEntry: LocalStorageEntry;
public categories: any[] = [];
public appdata: Appdata;
public wikiLink: string;
public infotext: string;
public title: string;
public outputType: string;
public inline: boolean = false;
public currentVersion: string = "";
public currentPublication: string = "";
public currentDescription: string = "";
public currentCategory: any;
public contentVisible: boolean = true;
public includeChildrenValue: boolean = false;
public displayIncludeChildrenToggle: boolean = false;
public displayScrollToTopIcon: boolean = false;
public isSrollToTopNeeded: boolean = false;
public categoryExpand: boolean = false;
public initLoad: boolean = false;
public initialExpansion: boolean = false;
public selectFirstNode: boolean = false;
public showAutoSelectRootNodeButton: boolean = false;
public showAutoSelectRootNodeButtonLabel: string;
public autoSelectRootNodeButtonDisabled: boolean = true;
public emitSelectedCategory: boolean = false;
public treeHeight: string | number = "550px";
public treeFilterable: boolean = false;
public allowDrag: boolean = false;
public allowSelectableAll: boolean = false;
public height: string = "";
private _searchUri: string = null;
//The nodes that we are currently searching for
public searchingNodes: string[];
// The current amount of requests that are running to fetch children
private pendingRequests = 0;
public static DEFAULT_OUTPUT_TYPE: string = "uri";
@Output()
public emitCategory = new EventEmitter<Category>();
/**
* The currently selected category
*/
@WidgetOutput("category")
private selectedCategory = new Subject<any>();
/**
* Loaded categories
*/
@WidgetOutput()
private categoriesLoaded = new Subject<boolean>();
/**
* The current value of the include children toggle
*/
@WidgetOutput("includeChildren")
public includeChildren = new Subject<any>();
/**
* The link of the category if the output type is set to 'uri' else the identifier of all current active nodes
*/
@WidgetOutput("uri")
private outputUri = new Subject<any>();
/**
* Columns profile to be returned to search result list
*/
@WidgetOutput("profile")
private profile: Subject<any> = new Subject<any>();
/**
* Auto select root node button is clicked
*/
@WidgetOutput()
public autoSelectRootNodeButtonClicked = new Subject<boolean>();
/**
* Force a reload of the data from the uri
*/
@WidgetInput("reload")
private reloadChannel = new Subject<any>();
/**
* Disable auto select root node button
*/
@WidgetInput("disableAutoSelectRootNodeButton")
private disableAutoSelectRootNodeButtonChannel = new Subject<any>();
/**
* Takes a list of string arrays, which is the path of the categories to search for. This will expand the path to given nodes, and fade out every other node
*/
@WidgetInput("searchCategory")
private searchCategoryChannel = new ReplaySubject<string[][]>(1);
/**
* Input to select given category, based on its path
*/
@WidgetInput("category")
private categoryChannel = new ReplaySubject<string[]>(1);
/**
* URI used to load root node
*/
@WidgetInput("rooturi")
private rootUriInput = new Subject<any>();
@WidgetInput("searchuri")
private searchUriInput = new Subject<string>();
/**
* Disabling/Enabling action for one node, or Disabling/Enabling it for all nodes.
*/
@WidgetInput("disableAction")
private disableNodesActionChannel = new Subject<any>();
/**
* Clears the current selection and reloads
*/
@WidgetInput("clearcategorysearch")
private clearChannel: Subject<any> = new Subject<any>();
/**
* Emits the current date, every time the output is updated
*/
@WidgetOutput("cleartextsearch")
private clearTextSearch: Subject<any> = new Subject<any>();
@WidgetInput("multi")
private multi = new ReplaySubject<boolean>();
/**
* Emits when a node is dragged and dropped inside the tree
*/
@WidgetOutput("categoryMoved")
public categoryMoved: Subject<any> = new Subject<any>();
@WidgetConfiguration()
public configuration: WidgetConfig<CategoryConfiguration>;
@WidgetId()
public _id: string;
@ViewChild("treeSelector")
treeSelector: TreeSelectorComponent;
private unsubscribe = NgUnsubscribe.create();
private treeInitialized = new Subject();
public dynamicHeight: boolean = false;
public dynamicTreeHeightInDialog: boolean = false;
public dynamicHeightAdditionalHeight: string;
constructor(
private categoryService: CategoryService,
private appdataStore: AppdataStore,
private _scrollService: ScrollService,
private localStorageService: LocalStorageService,
private translateService: TranslateService,
private currentLocaleService: CurrentLocaleService,
private _widgetFrameService: WidgetframeService
) {}
ngOnInit() {
this.currentLocaleService
.getCurrentLocale()
.pipe(takeUntil(this.unsubscribe))
.subscribe(() => (this.searchingNodes = null));
}
ngAfterViewInit() {}
ngAfterViewChecked() {
if (!this.treeSelector || this.treeSelector.rootNodes().length === 0) {
return;
}
if (this.initLoad) {
return;
}
this.treeInitialized.next();
if (
(this.categoryExpand || this.selectFirstNode) &&
this.treeSelector &&
this.treeSelector.rootNodes().length > 0
) {
// TODO: expand & select first root node
}
this.initLoad = true;
}
public loadChildrenOnFilter = (query: string) => {
if (this._searchUri && this.treeSelector) {
const uri = uriTemplates(this._searchUri).fill({ query });
return this._widgetFrameService.getData(uri).pipe(
tap((data) => {
if (!data.paths) {
return;
}
(data.paths as string[][]).forEach((paths) => {
// only expand up until the second-to-last node
// we want to see the retrieved nodes themselves, not their children
this.treeSelector.expand(paths.slice(0, -1));
});
})
);
//return timer(400);
}
return of();
};
isSelectionAllowed(): (id: any) => boolean {
if (this.allowSelectableAll) {
return () => true;
} else {
return undefined;
}
}
getSelectableClass(node: any): string {
if (!this.allowSelectableAll && node.selectable === false) {
return "nonSelectable";
} else {
return "";
}
}
// using property syntax, to bind `this` correctly
loadChildNodes = (parentNode: any, done: (children: any[]) => void) => {
if (
parentNode._links &&
parentNode._links.children &&
parentNode._links.children.href
) {
this.categoryService
.getCategories(parentNode._links.children.href)
.pipe(takeUntil(this.unsubscribe))
.subscribe(
(data) => {
let parentIdentifier = parentNode.identifier;
let children = this.transformData(
data._embedded.categories,
parentIdentifier
);
done(children);
},
(err) => {
console.error(err);
done([]);
}
);
} else {
done([]);
}
};
loadChildren(link): Observable<any> {
return this.categoryService.getCategories(link);
}
onSelectionEvent(event: SelectionEvent) {
if (event.source === "initial") {
return;
}
// use first node from added selection
let category = event.added ? event.added[0] : null;
if (category) {
this.currentCategory = category;
this.currentCategory.version = this.currentVersion;
this.emitCategory.emit(category);
this.clearTextSearch.next(Date.now);
if (this.isSrollToTopNeeded) {
event.added.forEach((node) => {
node._actions = {};
node._actions["scroll-to-top"] = {
type: "event",
description: this.translateService.instant(
"infotext.scroll.to.list"
),
};
});
}
this.localstorageSelectedCategoryEntry.value = JSON.stringify(
this.treeSelector.pathForNode(category.identifier)
);
if (category._links[this.dataType] !== undefined) {
if (this.outputType === CategoryWidgetComponent.DEFAULT_OUTPUT_TYPE) {
this.outputUri.next(category._links[this.dataType].href);
} else {
var categories = event.newSelection.map((node) => node.identifier);
this.outputUri.next(categories);
}
this.profile.next("categorySearchResult");
category.version = this.currentVersion;
category.publication = this.currentPublication;
this.currentDescription = category.description;
this.emitSelection(event.newSelection);
} else if (this.emitSelectedCategory) {
this.emitSelection(event.newSelection);
} else {
this.outputUri.next(null);
}
}
// was the selection cleared?
else if (event.newSelection || event.newSelection.length == 0) {
this.localstorageSelectedCategoryEntry.clear();
this.selectedCategory.next(null);
event.oldSelection.forEach((node) => {
if (node._actions && node._actions["scroll-to-top"]) {
node._actions = {};
}
});
if (!(this.outputType === CategoryWidgetComponent.DEFAULT_OUTPUT_TYPE)) {
this.outputUri.next([]);
}
this.currentDescription = "";
this.currentCategory = null;
}
}
private emitSelection(selection: any[]) {
if (!this.configuration.configuration.multi) {
if (selection.length === 0) {
this.selectedCategory.next(null);
return;
}
if (selection.length > 2) {
console.warn(
"Worklist selector is not in multi mode but multiple selected nodes where detected"
);
}
this.selectedCategory.next(selection[0]);
return;
}
this.selectedCategory.next(selection);
}
@WidgetConfigure()
protected configureWidget(
configuration: WidgetConfig<CategoryConfiguration>
) {
this.includeChildrenValue = getOrDefault(
this.configuration.configuration["includeChildrenValue"],
false
);
this.categoryExpand = getOrDefault(
this.configuration.configuration["categoryExpand"],
false
);
this.selectFirstNode = getOrDefault(
this.configuration.configuration["selectFirstNode"],
false
);
this.infotext = this.configuration.configuration["infoText"];
this.wikiLink = this.configuration.configuration["wikiLink"];
this.title = this.configuration.configuration["title"];
if (
this.configuration["_links"] &&
this.configuration["_links"]["categories"]
) {
this.rootCategoryLink =
this.configuration["_links"]["categories"]["href"];
}
let tree =
this.configuration.configuration.tree ||
({} as CategoryTreeConfiguration);
this.treeHeight = getOrDefault(tree.height, "550px");
this.treeFilterable = getOrDefault(tree.filterable, false);
this.dataType = this.configuration.configuration["dataType"];
this.localstorageSelectedCategoryEntry =
this.localStorageService.getLocalStorageEntry(
this.configuration.configuration["localstorage-category"],
Scope.GLOBAL,
DeletionMode.LOGIN
);
this.localstorageLastRootUrlEntry =
this.localStorageService.getLocalStorageEntry(
this.configuration.configuration["localstorage-category"] + "-root",
Scope.GLOBAL,
DeletionMode.LOGIN
);
this.localstorageIncludeChildrenEntry =
this.localStorageService.getLocalStorageEntry(
this.configuration.configuration["localstorage-include-children"],
Scope.GLOBAL,
DeletionMode.LOGIN
);
this.outputType = this.configuration.configuration["outputType"]
? this.configuration.configuration["outputType"]
: CategoryWidgetComponent.DEFAULT_OUTPUT_TYPE;
this.inline =
this.configuration.configuration["inline"] !== undefined
? this.configuration.configuration["inline"]
: false;
this.displayIncludeChildrenToggle =
this.configuration.configuration["display-include-children-toggle"] !==
undefined
? this.configuration.configuration["display-include-children-toggle"]
: false;
this.displayScrollToTopIcon =
this.configuration.configuration["display-scroll-to-top"] !== undefined
? this.configuration.configuration["display-scroll-to-top"]
: false;
this.localStorageVisibiltyEntry =
this.localStorageService.getLocalStorageEntry(
configuration.configuration["localstorage-content-visible"],
Scope.GLOBAL,
DeletionMode.LOGIN
);
this.showAutoSelectRootNodeButton =
this.configuration.configuration.showAutoSelectRootNodeButton !==
undefined
? this.configuration.configuration.showAutoSelectRootNodeButton
: false;
this.showAutoSelectRootNodeButtonLabel =
this.configuration.configuration.showAutoSelectRootNodeButtonLabel !==
undefined
? this.configuration.configuration.showAutoSelectRootNodeButtonLabel
: "";
this.emitSelectedCategory =
this.configuration.configuration.emitSelectedCategory !== undefined
? this.configuration.configuration.emitSelectedCategory
: false;
this.allowDrag = getOrDefault(
this.configuration.configuration.allowDrag,
false
);
this.dynamicHeight = getOrDefault(
this.configuration.configuration.dynamicHeight,
false
);
this.dynamicTreeHeightInDialog = getOrDefault(
this.configuration.configuration.dynamicTreeHeightInDialog,
false
);
this.dynamicHeightAdditionalHeight = getOrDefault(
this.configuration.configuration.dynamicHeightAdditionalHeight,
null
);
this.allowSelectableAll = getOrDefault(
this.configuration.configuration.allowSelectableAll,
false
);
if (this.localStorageVisibiltyEntry.exists()) {
this.contentVisible = this.localStorageVisibiltyEntry.value === "true";
}
if (this.displayScrollToTopIcon) {
this._scrollService
.getScrollToTopNeeded()
.pipe(debounceTime(100), distinctUntilChanged())
.subscribe((data) => {
this.isSrollToTopNeeded = data;
});
}
this.treeInitialized
.pipe(
flatMap(() => this.categoryChannel),
takeUntil(this.unsubscribe)
)
.subscribe((category) => {
if (!this.treeSelector) {
console.error(
"Received category before tree is loaded, can't select the node"
);
return;
}
this.treeSelector.expand(category);
this.treeSelector.selection([category[category.length - 1]], true);
});
this.treeInitialized
.pipe(
flatMap(() => this.searchCategoryChannel),
takeUntil(this.unsubscribe)
)
.subscribe((paths) => {
if (paths.length === 0) {
this.searchingNodes = null;
} else {
this.searchingNodes = paths.map((path) => path[path.length - 1]);
}
paths.forEach((path) => {
this.treeSelector.expand(path);
});
});
this.multi
.pipe(takeUntil(this.unsubscribe))
.subscribe((multi) => (this.configuration.configuration.multi = multi));
this.searchUriInput
.pipe(takeUntil(this.unsubscribe))
.subscribe((uri) => (this._searchUri = uri));
this.rootUriInput.pipe(takeUntil(this.unsubscribe)).subscribe((uri) => {
this.rootCategoryLink = uri;
if (this.rootCategoryLink) {
this.doReload(false);
} else {
this.rootCategoryLink = "";
this.localstorageSelectedCategoryEntry.clear();
this.localstorageIncludeChildrenEntry.clear();
this.selectedCategory.next(null);
this.categories = [];
if (this.treeSelector) {
this.treeSelector.collapseAll();
this.treeSelector.clearSelection();
}
}
});
this.disableNodesActionChannel
.pipe(takeUntil(this.unsubscribe))
.subscribe((disableNodesAction) => {
if (!disableNodesAction || !this.categories || !this.treeSelector) {
return;
}
const disabled = disableNodesAction.disabled;
const actionName = disableNodesAction.actionName;
const nodeIdentifier = disableNodesAction.nodeIdentifier;
if (nodeIdentifier) {
const category = this.treeSelector.nodeForId(nodeIdentifier);
if (category) {
this.disableNodeAction(category, actionName, disabled);
}
} else {
this.treeSelector.records().forEach((node) => {
this.disableNodeAction(node.data, actionName, disabled);
if (node.children) {
this.disableNodeChildrenAction(
node.children,
actionName,
disabled
);
}
});
}
});
this.reloadChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe), skip(1))
.subscribe((reload) => {
this.doReload(reload === true);
});
this.clearChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((reset) => {
this.localstorageSelectedCategoryEntry.clear();
this.localstorageIncludeChildrenEntry.clear();
this.selectedCategory.next(null);
this.doReload(true);
});
this.disableAutoSelectRootNodeButtonChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe(
(disabled) => (this.autoSelectRootNodeButtonDisabled = disabled)
);
this.appdataStore
.getAppdata()
.pipe(
takeUntil(this.unsubscribe),
mergeMap((data) => {
this.appdata = data;
if (this.rootCategoryLink) {
return this.categoryService.getRootCategories(
this.rootCategoryLink
);
}
return NEVER;
}),
mergeMap((data) => this.forceLoad(data))
)
.subscribe(
(data) => {
this.categories = data.categories;
this.currentVersion = data.root["version"];
this.currentPublication = data.root["identifier"];
},
(err) => console.error(err)
);
}
private disableNodeChildrenAction(
children: any[],
actionName: string,
disabled: boolean
) {
if (children && children.length > 0) {
children.forEach((child) => {
this.disableNodeAction(child.data, actionName, disabled);
this.disableNodeChildrenAction(child.children, actionName, disabled);
});
}
}
private disableNodeAction(
category: any,
actionName: string,
disabled: boolean
) {
const nodeActions = category._actions;
if (nodeActions) {
const action = nodeActions[actionName];
action.disabled = disabled;
}
}
private forceLoad(
data: CategoryResource,
path: string[] = []
): Observable<{ root: CategoryResource; categories: any[] }> {
const localStorageIncludeChildren =
this.localstorageIncludeChildrenEntry.value;
if (localStorageIncludeChildren) {
this.includeChildrenValue = localStorageIncludeChildren === "true";
}
this.includeChildren.next(this.includeChildrenValue);
const categories = this.transformData(data._embedded.categories);
if (path == null || path.length == 0) {
const localStorageValue = this.localstorageSelectedCategoryEntry.value;
if (localStorageValue) {
path = JSON.parse(localStorageValue);
}
}
//If the path is > 1 we need to load the children
if (path.length >= 1) {
return this.forceLoadChildren(categories, path).pipe(
scan((l, r) => [...l, ...r]),
last(),
map((nodes) => ({ root: data, categories: [...categories, ...nodes] }))
);
}
return observableOf({ root: data, categories });
}
private forceLoadChildren(categories: any, path): Observable<any[]> {
const current = path.shift();
const category = categories.find(
(category) => category.identifier == current
);
if (category && category._links.children) {
if (path.length == 0) {
return this.loadChildren(category._links.children.href).pipe(
map((data) =>
this.transformData(data._embedded.categories, category.identifier)
)
);
} else {
return this.loadChildren(category._links.children.href).pipe(
concatMap((data) => {
let children = this.transformData(
data._embedded.categories,
category.identifier
);
return from([of(children), this.forceLoadChildren(children, path)]);
}),
concatAll()
);
}
} else {
return observableOf([]);
}
}
private doReload(withReset) {
this.currentLevel = 0;
this.categoryService
.getRootCategories(this.rootCategoryLink)
.pipe(takeUntil(this.unsubscribe))
.subscribe((data) => {
this.categoriesLoaded.next(true);
const categories = this.transformData(data._embedded.categories);
// Emitting last selected category because of changing ui locale.
if (this.emitSelectedCategory && this.currentCategory) {
if (this.currentCategory.version === this.currentVersion) {
this.emitSelection([this.currentCategory]);
} else {
this.currentCategory = null;
}
}
if (withReset) {
this.treeSelector.collapseAll();
this.treeSelector.clearSelection();
this.categories = categories;
return;
}
if (this.localstorageLastRootUrlEntry) {
if (
this.rootCategoryLink !== this.localstorageLastRootUrlEntry.value
) {
this.localstorageLastRootUrlEntry.value = this.rootCategoryLink;
// force loading of the first root
// since, igx-tree-grid can't expand rows & load the data on demand via API
let path =
this.categoryExpand && categories.length > 0
? [categories[0].identifier]
: [];
this.forceLoad(data, path).subscribe((result) => {
this.categories = result.categories;
});
return;
}
this.forceLoad(data).subscribe((result) => {
this.categories = result.categories;
});
}
});
}
transformData(data: any[], parentIdentifier: any = null): any[] {
for (var item of data) {
item.label = item.description;
item.data = item.identifier;
item["parent-node"] = parentIdentifier;
item.name = item.description;
item.expandedIcon = "fa-folder-open";
item.collapsedIcon = "fa-folder";
item.allowDrag = false;
if (item._links && item._links.children != undefined) {
item.expandedIcon = "fa-folder-open";
item.collapsedIcon = "fa-folder";
item.leaf = false;
item.hasChildren = true;
} else {
item.expandedIcon = "fa-folder-open";
item.collapsedIcon = "fa-folder-o";
item.leaf = true;
item.hasChildren = false;
}
}
return data;
}
toogleContentVisibility() {
this.contentVisible = !this.contentVisible;
this.localStorageVisibiltyEntry.value = this.contentVisible.toString();
}
includeChildrenChange(event: MatSlideToggleChange) {
this.localstorageIncludeChildrenEntry.value = event.checked + "";
this.includeChildren.next(event.checked);
}
public getNodeClass(node) {
if (!this.searchingNodes) {
return null;
}
if (this.searchingNodes.indexOf(node) === -1) {
return "no-search-hit";
}
return "search-hit";
}
public autoSelectRootNode() {
if (!this.treeSelector) {
return;
}
let rootNodes = this.treeSelector.rootNodes();
if (rootNodes.length > 0) {
this.treeSelector.clearSelection();
this.treeSelector.selection([rootNodes[0].identifier]);
this.treeSelector.expand([rootNodes[0].identifier]);
this.autoSelectRootNodeButtonClicked.next(true);
}
}
public onDrag(event: OnDragEvent) {
let node = event.dragged;
let parentIdentifier = node["parent-node"];
if (!parentIdentifier) {
event.cancel = true;
}
let parent = this.treeSelector.nodeForId(parentIdentifier);
if (parent == null || parent.virtual) {
event.cancel = true;
}
}
public onDropped(event: OnDroppedEvent) {
this.categoryMoved.next(event);
}
}