@WidgetComponent

nm-shop-category

File

src/app/shared/widgets/apps/my-shop-md/shop-category/shop-category.component.ts

Metadata

changeDetection ChangeDetectionStrategy.OnPush
selector nm-shop-category
styleUrls shop-category.component.scss
templateUrl ./shop-category.component.html

Index

Widget inputs
Widget outputs
Properties
Methods

Constructor

constructor(categoryService: ShopCategoryService, appdataStore: AppdataStore, halService: HalService, dialog: MatDialog, _widgetframeService: WidgetframeService, _notificationService: CustomNotificationService, _progressbarService: ProgressbarService, zone: NgZone, dragAndDrop: DragAndDropService, localStorageService: LocalStorageService, _changeDetectorRef: ChangeDetectorRef)
Parameters :
Name Type Optional
categoryService ShopCategoryService no
appdataStore AppdataStore no
halService HalService no
dialog MatDialog no
_widgetframeService WidgetframeService no
_notificationService CustomNotificationService no
_progressbarService ProgressbarService no
zone NgZone no
dragAndDrop DragAndDropService no
localStorageService LocalStorageService no
_changeDetectorRef ChangeDetectorRef no

Methods

Protected configureWidget
configureWidget(configuration: WidgetConfig)
Decorators : WidgetConfigure
Parameters :
Name Type Optional
configuration WidgetConfig no
Returns : void
Private doReload
doReload(withReset: )
Parameters :
Name Optional
withReset no
Returns : void
Private forceLoad
forceLoad(data: any)
Parameters :
Name Type Optional
data any no
Returns : Observable<any>
Private forceLoadChildren
forceLoadChildren(categories: any, path: )
Parameters :
Name Type Optional
categories any no
path no
Returns : Observable<any>
getChildren
getChildren(node: any)
Parameters :
Name Type Optional
node any no
Returns : any
loadChildren
loadChildren(link: )
Parameters :
Name Optional
link no
Returns : Observable<any>
markOffliine
markOffliine(node: )
Parameters :
Name Optional
node no
Returns : void
ngOnInit
ngOnInit()
Returns : void
onActivatTreeNode
onActivatTreeNode(event: )
Parameters :
Name Optional
event no
Returns : void
onUpdateData
onUpdateData()
Returns : void
refreshDragAndDrop
refreshDragAndDrop()
Returns : void
transformData
transformData(data: any[])
Parameters :
Name Type Optional
data any[] no
Returns : any[]
trim
trim(arr: , key: )
Parameters :
Name Optional
arr no
key no
Returns : any
updateOutput
updateOutput(event: )
Parameters :
Name Optional
event no
Returns : void
updateState
updateState(categories: , id: , isVisible: )
Parameters :
Name Optional
categories no
id no
isVisible no
Returns : void

Properties

Public _id
_id: string
Type : string
Decorators : WidgetId
Public appdata
appdata: Appdata
Type : Appdata
Public categories
categories: any[]
Type : any[]
Private clearChannel
clearChannel: Subject<any>
Type : Subject<any>
Default value : new Subject<any>()
Decorators : WidgetInput
Private clearTextSearch
clearTextSearch: Subject<any>
Type : Subject<any>
Decorators : WidgetOutput
Public colorListIdentifier
colorListIdentifier: string
Type : string
Public configuration
configuration: WidgetConfig
Type : WidgetConfig
Decorators : WidgetConfiguration
Private currentLevel
currentLevel: number
Type : number
Default value : 0
Private currentUri
currentUri: any
Type : any
customTemplateStringOptions
customTemplateStringOptions: object
Type : object
Default value : { getChildren: this.getChildren.bind(this), idField: "identifier", //actionMapping }
Public dataType
dataType: string
Type : string
Static DEFAULT_OUTPUT_TYPE
DEFAULT_OUTPUT_TYPE: string
Type : string
Default value : "uri"
Public dialog
dialog: MatDialog
Type : MatDialog
Public idForOffline
idForOffline:
Public infotext
infotext: string
Type : string
Public inputLink
inputLink: string
Type : string
Public localstorageSelectedCategory
localstorageSelectedCategory: string
Type : string
Public localstorageSelectedCategoryEntry
localstorageSelectedCategoryEntry: LocalStorageEntry
Type : LocalStorageEntry
Public maintenanceLevel
maintenanceLevel:
onDeactivateTreeNode
onDeactivateTreeNode:
Default value : () => { this.localstorageSelectedCategoryEntry.clear(); }
Public orderedResults
orderedResults: any
Type : any
Public outputType
outputType: string
Type : string
Private outputUri
outputUri:
Default value : new Subject<any>()
Decorators : WidgetOutput
Public popupfilter
popupfilter: any
Type : any
Default value : {}
Private profile
profile: Subject<any>
Type : Subject<any>
Decorators : WidgetOutput
Private programaticExpansion
programaticExpansion: boolean
Type : boolean
Default value : false
Private reloadChannel
reloadChannel:
Default value : new Subject<any>()
Decorators : WidgetInput
Private resetChannel
resetChannel: Subject<any>
Type : Subject<any>
Default value : new Subject<any>()
Decorators : WidgetInput
Public rootCategoryLink
rootCategoryLink: string
Type : string
Public title
title: string
Type : string
tree
tree:
Decorators : ViewChild
Private unsubscribe
unsubscribe:
Default value : NgUnsubscribe.create()
Private uriParams
uriParams: Subject<any>
Type : Subject<any>
Default value : new Subject<any>()
Decorators : WidgetInput
Public wikiLink
wikiLink: string
Type : string
import { of as observableOf, Subject, Observable } from "rxjs";

import {
  mergeMap,
  takeUntil,
  filter,
  switchMap,
  mapTo,
  map,
  skip,
  tap,
} from "rxjs/operators";
import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  EventEmitter,
  NgZone,
  Output,
  ViewChild,
} from "@angular/core";
import {
  AppdataStore,
  Appdata,
} from "../../../../components/appdata/appdata.store";
import { WidgetConfig } from "../../../widget.configuration";
import {
  WidgetComponent,
  WidgetConfiguration,
  WidgetConfigure,
  WidgetId,
  WidgetInput,
  WidgetOutput,
} from "../../../widget.metadata";
import { WidgetframeService } from "../../../widgetframe/widgetframe.service";
import { ShopCategory } from "./index";
import { ShopCategoryService } from "./shop-category.service";
import { NgUnsubscribe } from "../../../../ng-unsubscribe";
import { HalService } from "../../../../components/hal/hal.service";
import { MatDialog } from "@angular/material/dialog";
import { ShopCategoryDetailsPopupComponent } from "./shop-category-details-popup.component";
import { ShopCategoryPropertiesPopupComponent } from "./shop-category-properties-popup.component";
import { ProgressbarService } from "../../../../components/progressbar/progressbar.service";

import { IActionMapping, KEYS, TREE_ACTIONS } from "angular-tree-component";
import { CustomNotificationService } from "../../../../components/notification/customnotification.service";
import { ActionRequest } from "../../../../components/hal/actionRequest";
import { DragAndDropService } from "../../../../components/util/drag-and-drop.service";

import * as uriTemplates_ from "uri-templates";
import {
  LocalStorageEntry,
  LocalStorageService,
} from "../../../../components/local-storage/local-storage.service";
import {
  DeletionMode,
  Scope,
} from "../../../../components/local-storage/local-storage-constants";

const uriTemplates = uriTemplates_;

declare var $;
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();
    },
  },
};

@WidgetComponent("nm-shop-category")
@Component({
  selector: "nm-shop-category",
  templateUrl: "./shop-category.component.html",
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrls: ["./shop-category.component.scss"],
})
export class ShopCategoryWidgetComponent {
  private currentLevel: number = 0;
  private programaticExpansion: boolean = false;
  public inputLink: string;
  public rootCategoryLink: string;
  public dataType: string;
  public localstorageSelectedCategory: string;
  public localstorageSelectedCategoryEntry: LocalStorageEntry;
  public categories: any[];
  public appdata: Appdata;
  public wikiLink: string;
  public infotext: string;
  public title: string;
  public idForOffline;
  public maintenanceLevel;
  public orderedResults: any;
  public popupfilter: any = {};
  public outputType: string;

  public colorListIdentifier: string;

  private currentUri: any;

  @Output()
  public emitCategory = new EventEmitter<ShopCategory>();

  @WidgetOutput("uri")
  private outputUri = new Subject<any>();

  @WidgetOutput("profile")
  private profile: Subject<any>;

  @WidgetInput("reload")
  private reloadChannel = new Subject<any>();

  @WidgetInput("reset")
  private resetChannel: Subject<any> = new Subject<any>();

  @WidgetInput("clearcategorysearch")
  private clearChannel: Subject<any> = new Subject<any>();

  @WidgetInput("uriParams")
  private uriParams: Subject<any> = new Subject<any>();

  @WidgetOutput("cleartextsearch")
  private clearTextSearch: Subject<any>;

  @WidgetConfiguration()
  public configuration: WidgetConfig;

  @WidgetId()
  public _id: string;

  @ViewChild("tree", { static: true })
  tree;

  private unsubscribe = NgUnsubscribe.create();

  customTemplateStringOptions = {
    getChildren: this.getChildren.bind(this),
    idField: "identifier",
    //actionMapping
  };

  public static DEFAULT_OUTPUT_TYPE: string = "uri";

  constructor(
    private categoryService: ShopCategoryService,
    private appdataStore: AppdataStore,
    private halService: HalService,
    public dialog: MatDialog,
    private _widgetframeService: WidgetframeService,
    private _notificationService: CustomNotificationService,
    private _progressbarService: ProgressbarService,
    private zone: NgZone,
    private dragAndDrop: DragAndDropService,
    private localStorageService: LocalStorageService,
    private _changeDetectorRef: ChangeDetectorRef
  ) {}

  ngOnInit() {
    this.halService
      .getActionEvents()
      .pipe(filter((event) => event.name === "my-shopmd-remove-dimensions"))
      .subscribe((resp) => {
        if (!!this.currentUri) {
          this.outputUri.next(this.currentUri);
          this._changeDetectorRef.markForCheck();
        }
      });

    this.halService
      .getActionEvents()
      .pipe(filter((event) => event.name === "my-shopmd-save-properties"))
      .subscribe((resp) => {
        this._notificationService.fromJson(resp.response);
      });

    this.halService
      .getActionEvents()
      .pipe(
        filter((event) => event.name === "details"),
        map((event) => (<any>event).response)
      )
      .subscribe((resp) => {
        this.dialog.closeAll();
        let dialogRef = this.dialog.open(ShopCategoryDetailsPopupComponent);
        dialogRef.componentInstance["link"] = resp.href;
        dialogRef.componentInstance["popupfilter"] = this.popupfilter;
        dialogRef.componentInstance["attachActionIdentifier"] =
          "my-shopmd-attach-dimensions";
        dialogRef.componentInstance[
          "colorListIdentifier"
        ] = this.colorListIdentifier;
        dialogRef.afterClosed().subscribe((response) => {
          this._changeDetectorRef.markForCheck();
        });
      });

    this.halService
      .getActionEvents()
      .pipe(
        filter((event) => event.name === "properties"),
        map((event) => (<any>event).response)
      )
      .subscribe((resp) => {
        this.dialog.closeAll();
        this.idForOffline = resp.payload.identifier;

        let clickedNode = this.tree.treeModel.getNodeById(this.idForOffline);
        let category = clickedNode.data.name;

        let node = this.tree.treeModel.getNodeById(resp.payload.identifier);
        let url = clickedNode.data._links.self.href;
        this._widgetframeService.getData(url).subscribe((data) => {
          let node = this.tree.treeModel.getNodeById(resp.payload.identifier);
          let dialogRef = this.dialog.open(
            ShopCategoryPropertiesPopupComponent
          );
          dialogRef.componentInstance["title"] = "Kategorieeigenschaften";
          dialogRef.componentInstance["category"] = category;
          dialogRef.componentInstance["data"] = data;

          dialogRef.afterClosed().subscribe((isVisible) => {
            if (typeof isVisible === "undefined" || isVisible == null) {
              return;
            }
            this.updateState(this.categories, this.idForOffline, isVisible);
            this._changeDetectorRef.markForCheck();
          });
        });
      });
  }

  updateState(categories, id, isVisible) {
    let node = this.tree.treeModel.getNodeById(id);
    node.data.isOnline = isVisible;
  }

  markOffliine(node) {
    node.data.isOnline = true;
  }

  getChildren(node: any) {
    let unsubscribe = this.unsubscribe;
    return new Promise((resolve, reject) => {
      if (
        node.data._links &&
        node.data._links.children &&
        node.data._links.children.href
      ) {
        this.loadChildren(node.data._links.children.href)
          .pipe(takeUntil(unsubscribe))
          .subscribe(
            (data) => {
              resolve(this.transformData(data._embedded.categories));
              this.refreshDragAndDrop();
            },
            (err) => reject(err)
          );
      } else {
        resolve(null);
      }
    });
  }

  loadChildren(link): Observable<any> {
    return this.categoryService.getCategories(link);
  }

  onUpdateData() {
    if (
      this.localstorageSelectedCategoryEntry.exists() &&
      this.currentLevel === 0
    ) {
      let path = JSON.parse(this.localstorageSelectedCategoryEntry.value);
      let node = this.tree.treeModel.getNodeByPath(path);
      if (node && node.isLeaf) {
        node.setActiveAndVisible();
      }
    }
  }

  onActivatTreeNode(event) {
    this.emitCategory.emit(event.node);
    let category = event.node;
    this.clearTextSearch.next(Date.now);
    this.localstorageSelectedCategoryEntry.value = JSON.stringify(
      category.path
    );
    if (category.data._links[this.dataType] !== undefined) {
      this.outputUri.next(category.data._links[this.dataType].href);
      this.profile.next("categorySearchResult");
    } else {
      this.outputUri.next(null);
    }
    this._changeDetectorRef.markForCheck();
  }

  /*
  onActivatTreeNode = (event) => {
    this.refreshDragAndDrop();
    this.updateOutput(event);
  }

  */
  onDeactivateTreeNode = () => {
    this.localstorageSelectedCategoryEntry.clear();
  };

  updateOutput(event) {
    this.emitCategory.emit(event.node);
    let category = event.node;
    this.clearTextSearch.next(Date.now);
    this.localstorageSelectedCategoryEntry.value = JSON.stringify(
      category.path
    );

    if (category.data._links[this.dataType] !== undefined) {
      if (this.outputType === ShopCategoryWidgetComponent.DEFAULT_OUTPUT_TYPE) {
        this.outputUri.next(category.data._links[this.dataType].href);
      } else {
        var categories = [];
        for (var item of event.node.treeModel.activeNodes) {
          categories.push(item.data.identifier);
        }
        this.outputUri.next(categories);
      }
      this.profile.next("categorySearchResult");
    } else {
      this.outputUri.next(null);
    }
  }

  @WidgetConfigure()
  protected configureWidget(configuration: WidgetConfig) {
    this.popupfilter = this.configuration.configuration["popupfilter"];
    this.infotext = this.configuration.configuration["infoText"];
    this.wikiLink = this.configuration.configuration["wikiLink"];
    this.title = this.configuration.configuration["title"];
    this.dataType = "dimensions"; //"this.configuration.configuration["dataType"]";

    this.localstorageSelectedCategoryEntry = this.localStorageService.getLocalStorageEntry(
      this.configuration.configuration["localstorage-category"],
      Scope.GLOBAL,
      DeletionMode.LOGIN
    );

    this.colorListIdentifier = this.configuration.configuration[
      "colorListIdentifier"
    ];
    if (!this.colorListIdentifier) {
      console.error(
        "colorListIdentifier is not configured but needed to work properly"
      );
    }

    this.uriParams
      .pipe(
        map((uriParams) => {
          uriParams["context"] = "my-shopMd";
          let template = uriTemplates(
            this.configuration["_links"]["categories"]["href"]
          );
          this.rootCategoryLink = template.fill(uriParams);
          return this.rootCategoryLink;
        }),
        skip(1),
        mergeMap((uri) => {
          return this.categoryService.getRootCategories(uri);
        }),
        takeUntil(this.unsubscribe),
        mergeMap((data) => this.forceLoad(data))
      )
      .subscribe((data) => {
        this.categories = this.transformData(data._embedded.categories);
        setTimeout(() => {
          this.onUpdateData();
          this.refreshDragAndDrop();
        }, 1);
      });
    /*
        this.reloadChannel
          .asObservable()
          .takeUntil(this.unsubscribe)
          .subscribe(reload => {
            this.doReload(false);
            this._changeDetectorRef.markForCheck();
          });

        this.clearChannel
          .asObservable()
          .takeUntil(this.unsubscribe)
          .subscribe(reset => {
            localStorage.removeItem(this.localstorageSelectedCategory);
            this.doReload(true);
            this._changeDetectorRef.markForCheck();
          });

        this.resetChannel
          .asObservable()
          .subscribe(reset => {
            localStorage.removeItem(this.localstorageSelectedCategory);
            this.doReload(true);
            this._changeDetectorRef.markForCheck();

          });

          */

    this.categoryService
      .getOrderedResults(this.colorListIdentifier)
      .subscribe((data) => {
        this.orderedResults = data;
        this._changeDetectorRef.markForCheck();
      });

    this.outputUri
      .pipe(
        switchMap((uri) => {
          this.categoryService.setCurrentDimensionUri(
            this.colorListIdentifier,
            uri
          );
          return this.categoryService.loadOrderedResults(uri);
        })
      )
      .subscribe((data) => {
        this.orderedResults = data;
        this.categoryService.setOrderedResults(this.colorListIdentifier, data);
        this._progressbarService.requestFinished();
        this._changeDetectorRef.markForCheck();
      });
  }

  private forceLoad(data: any): Observable<any> {
    const localStorageValue = this.localstorageSelectedCategoryEntry.value;
    if (localStorageValue) {
      const path = JSON.parse(localStorageValue);
      //If the path is > 1 we need to load the children
      const categories = data._embedded.categories;
      if (path.length > 1) {
        return this.forceLoadChildren(categories, path).pipe(mapTo(data));
      }
      return observableOf(data);
    } else {
      return observableOf(data);
    }
  }

  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(
          tap(
            (data) =>
              (category.children = this.transformData(
                data._embedded.categories
              ))
          )
        );
      } else {
        return this.loadChildren(category._links.children.href).pipe(
          mergeMap((data) => {
            category.children = this.transformData(data._embedded.categories);
            return this.forceLoadChildren(category.children, path).pipe(
              mapTo(data)
            );
          })
        );
      }
    } else {
      return observableOf([]);
    }
  }

  private doReload(withReset) {
    this.currentLevel = 0;
    this.categoryService
      .getRootCategories(this.rootCategoryLink)
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => {
        this.categories = this.transformData(data._embedded.categories);
        this.refreshDragAndDrop();
        if (withReset) {
          this.tree.treeModel.collapseAll();
          this.currentLevel = 0;
          let activeNode = this.tree.treeModel.getActiveNode();
          if (activeNode) {
            activeNode.toggleActivated();
          }
        }
      });
  }

  refreshDragAndDrop(): void {
    let treeModel = this.tree.treeModel;
    let halService = this.halService;
    let self = this;
    this.zone.runOutsideAngular(() => {
      setTimeout(() => {
        jQuery(".custom-node").droppable({
          tolerance: "pointer",
          accept: ".ui-widget-content", // prevent drops from details-popup
          scroll: true,
          greedy: true,
          // Add .hoverClass whenever #draggable is being hovered over #droppable
          over: function (event, ui) {
            $(".ui-draggable-dragging").addClass("hoverClass");
          },
          // Remove .hoverClass whenever #draggable is no longer hovered over #droppable
          out: function (event, ui) {
            $(".ui-draggable-dragging").removeClass("hoverClass");
          },
          // Add .dropClass whenever #draggable is dropped on #droppable
          drop: (event, ui) => {
            let nodeId = jQuery(event.target).attr("id");
            let node = treeModel.getNodeById(nodeId);

            let action = <ActionRequest>(
              node.data._actions["my-shopmd-attach-dimensions"]
            );

            let dragged = self.dragAndDrop.completeDrag();
            let identifiers = dragged.data.map((item) => item.identifier);

            action.payload = {};
            action.payload.identifiers = identifiers;

            this.zone.run(() => {
              halService
                .execute("my-shopmd-attach-dimensions", action)
                // reload the current category dimensions list
                .subscribe((response) => {
                  node.setActiveAndVisible(false);

                  let uri = node.data._links["dimensions"];
                  if (!!uri) {
                    this.outputUri.next(uri.href);
                  }
                });
            });
            (ui.helper || ui.item).remove();
          },
        });
      }, 1);
    });
    this._changeDetectorRef.markForCheck();
  }

  trim(arr, key) {
    var values = {};
    return arr.filter(function (item) {
      var val = item[key];
      var exists = values[val];
      values[val] = true;
      return !exists;
    });
  }

  transformData(data: any[]): any[] {
    if (!data) {
      return;
    }

    for (var item of data) {
      item.label = item.description;
      item.data = item.identifier;
      item.name = item.description;

      if (item.properties != null) {
        item._actions["details"] = {
          type: "event",
          href: item._links.products.href,
        };

        item._actions["properties"] = {
          type: "event",
          href: item._links.products.href,
          payload: { identifier: item.identifier },
        };
      }

      item.isOnline = item["is-published"];
      item.expandedIcon = "fa-folder-open";
      item.collapsedIcon = "fa-folder";
      item.allowDrag = false;
      if (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;
      }
    }
    this._changeDetectorRef.markForCheck();
    return data;
  }
}
<div slot="content" class="nm-widgetframe__content" style="margin-top: 25px">
  <tree-root
    #tree
    [nodes]="categories"
    [options]="customTemplateStringOptions"
    (activate)="onActivatTreeNode($event)"
    (updateData)="onUpdateData()"
  >
    <ng-template #loadingTemplate>{{
      "placeholder.loading" | translate
    }}</ng-template>
    <ng-template #treeNodeTemplate let-node let-index="index">
      <div
        class="custom-node"
        [id]="node.data.identifier"
        [class.disabled]="!node.data.isOnline"
      >
        <div class="custom-node-content-wrapper">{{ node.data.name }}</div>
        <div
          class="custom-node-action-wrapper nm-actions"
          style="padding-right: 5px"
        >
          <span *ngFor="let act of node.data._actions | iterable">
            <nm-action-icon
              [ngClass]="act.key"
              *ngIf="
                act.key != 'my-shopmd-save-properties' &&
                act.key != 'my-shopmd-attach-dimensions' &&
                act.key != 'my-shopmd-remove-dimensions'
              "
              [action]="act.value"
              [name]="act.key"
            >
            </nm-action-icon>
          </span>
        </div>
      </div>
    </ng-template>
  </tree-root>
</div>
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""