| /******************************************************************************** |
| * Copyright (c) 2015-2020 Contributors to the Eclipse Foundation |
| * |
| * See the NOTICE file(s) distributed with this work for additional |
| * information regarding copyright ownership. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| ********************************************************************************/ |
| |
| |
| import {Component, ViewChild, OnInit, Output, EventEmitter, OnDestroy} from '@angular/core'; |
| |
| import {map} from 'rxjs/operators'; |
| import {View, ViewColumn} from './tableview.service'; |
| import {NodeService} from '../navigator/node.service'; |
| import {Node} from '../navigator/node'; |
| import {SearchService, SearchAttribute} from '../search/search.service'; |
| import {SearchattributeTreeComponent} from '../searchattribute-tree/searchattribute-tree.component'; |
| import {MDMNotificationService} from '../core/mdm-notification.service'; |
| import {TreeNode} from 'primeng/primeng'; |
| import {ModalDirective} from 'ngx-bootstrap'; |
| import {TypeaheadMatch} from 'ngx-bootstrap/typeahead'; |
| import {classToClass} from 'class-transformer'; |
| import { TranslateService } from '@ngx-translate/core'; |
| import { MdmLocalizationService } from '@localization/mdm-localization.service'; |
| import { MdmTranslatePipe } from '@localization/mdm-translate.pipe'; |
| import { Subscription } from 'rxjs'; |
| |
| @Component({ |
| selector: 'edit-view', |
| templateUrl: './editview.component.html', |
| styles: ['.remove {color:black; cursor: pointer; float: right}', '.icon { cursor: pointer; margin: 0px 5px; }'] |
| }) |
| export class EditViewComponent implements OnInit, OnDestroy { |
| |
| @ViewChild('lgModal') public childModal: ModalDirective; |
| @ViewChild(SearchattributeTreeComponent) tree: SearchattributeTreeComponent; |
| |
| environments: Node[] = []; |
| isReadOnly = false; |
| currentView: View = new View(); |
| searchAttributes: { [env: string]: SearchAttribute[] } = {}; |
| typeAheadValues: {label: string, group: string, attribute: SearchAttribute }[] = []; |
| |
| selectedAttribute: SearchAttribute; |
| |
| @Output() |
| coloumnsSubmitted = new EventEmitter<View>(); |
| private allUniqueBoTypeTranslations: any[] = []; |
| private allAttribueTranslations: any[] = []; |
| private subscription = new Subscription(); |
| |
| constructor(private nodeService: NodeService, |
| private searchService: SearchService, |
| private notificationService: MDMNotificationService, |
| private translateService: TranslateService, |
| private localizationService: MdmLocalizationService) { } |
| |
| ngOnInit() { |
| this.subscription.add( |
| this.tree.onNodeSelect$.subscribe( |
| node => this.selectNode(node), |
| error => this.notificationService.notifyError(this.translateService.instant('tableview.editview.err-cannot-select-node'), error) |
| ) |
| ); |
| |
| this.subscription.add( |
| this.nodeService.getNodes() |
| .subscribe( |
| envs => { |
| this.searchService.loadSearchAttributesStructured(envs.map(e => e.sourceName)).pipe( |
| map(attrs => attrs['measurements'])) |
| .subscribe( |
| attrs => this.refreshValues(attrs, envs), |
| error => this.notificationService.notifyError( |
| this.translateService.instant('tableview.editview.err-cannot-load-search-attributes'), error) |
| ); |
| }, |
| error => this.notificationService.notifyError(this.translateService.instant('tableview.editview.err-cannot-load-nodes'), error) |
| ) |
| ); |
| this.subscription.add( |
| this.translateService.onLangChange.subscribe(() => this.onLanguageChanged()) |
| ); |
| } |
| |
| ngOnDestroy() { |
| this.subscription.unsubscribe(); |
| } |
| |
| refreshValues(searchAttributes: { [env: string]: SearchAttribute[] }, environments: Node[]) { |
| |
| this.searchAttributes = searchAttributes; |
| this.environments = environments; |
| |
| this.onLanguageChanged(); |
| } |
| |
| private onLanguageChanged() { |
| |
| this.allUniqueBoTypeTranslations = []; |
| } |
| |
| asyncTranslateBoTypesAndAttributes() { |
| |
| if (this.allUniqueBoTypeTranslations.length === 0) { |
| |
| let allAttributes: SearchAttribute[] = |
| Object.keys(this.searchAttributes).map(env => this.searchAttributes[env]) |
| .reduce((acc, value) => acc.concat(value), <SearchAttribute[]> []); |
| |
| this.allAttribueTranslations = []; |
| |
| let allBoTypes: any[] = allAttributes.map(sa => { return { 'key': sa.source + '.' + sa.boType, 'boType': sa.boType, 'label': sa.boType }; }); |
| allBoTypes.sort((bo1, bo2) => bo1['key'].localeCompare(bo2['key'])); |
| let allUniqueBoTypes: any[] = []; |
| allBoTypes.forEach(bo1 => {if (allUniqueBoTypes.findIndex(bo2 => bo1['key'].localeCompare(bo2['key']) === 0) === -1) {allUniqueBoTypes.push(bo1);}}); |
| |
| let translatePipe: MdmTranslatePipe = new MdmTranslatePipe(this.localizationService); |
| |
| allUniqueBoTypes.forEach((bt, i) => { |
| translatePipe.transform(bt['boType'], bt['source']) |
| .subscribe(result => { |
| // for some keys we have 3 or more different translations |
| // and thus 3 or more iterations inside the subscription |
| // e.g. 'Channel' = 'Channel'; 'Channel' = 'Measurement quantity' |
| // take the last one like in attribute tree using Translate pipe |
| // to get the same displayed results |
| bt['label'] = result; |
| if (this.allUniqueBoTypeTranslations.length < allUniqueBoTypes.length) { |
| delete(bt['boType']); |
| this.allUniqueBoTypeTranslations.push(bt); |
| } |
| if (this.allUniqueBoTypeTranslations.length === allUniqueBoTypes.length && (allUniqueBoTypes.length - 1) === i) { |
| this.onTranslationDone(); |
| } |
| }).unsubscribe(); |
| }); |
| |
| allAttributes.forEach((attr, i) => { |
| translatePipe.transform(attr.boType, attr.source, attr.attrName) |
| .subscribe(result => { |
| if (this.allAttribueTranslations.length <= i) { |
| let item = { |
| 'key': attr.source + '.' + attr.boType + '.' + attr.attrName |
| , 'label': result }; |
| this.allAttribueTranslations.push(item); |
| if ((allAttributes.length - 1) === i) { |
| this.onTranslationDone(); |
| } |
| } |
| }).unsubscribe(); |
| }); |
| } |
| } |
| |
| /** |
| * this is called, when all subscriptions finished updating the |
| * asynchronous translation result for bo types or attributes |
| */ |
| private onTranslationDone() { |
| |
| if (this.allUniqueBoTypeTranslations.length > 0 && this.allAttribueTranslations.length > 0) { |
| |
| let allAttributes: SearchAttribute[] = |
| Object.keys(this.searchAttributes).map(env => this.searchAttributes[env]) |
| .reduce((acc, value) => acc.concat(value), <SearchAttribute[]> []); |
| |
| // space will not work with type-ahead values |
| let ar = allAttributes.map(sa => { |
| return { 'label': this.findBoTypeTranslation(sa) + '.' + this.findAttributeTranslation(sa), |
| 'group': sa.boType, 'attribute': sa }; |
| }); |
| |
| this.typeAheadValues = this.uniqueBy(ar, p => p.label); |
| } |
| } |
| |
| private findBoTypeTranslation(attr: SearchAttribute): string { |
| let key = attr.source + '.' + attr.boType; |
| let retVal = this.allUniqueBoTypeTranslations.find(bt => bt['key'].localeCompare(key) === 0); |
| return (retVal === undefined) ? attr.boType : retVal['label']; |
| } |
| |
| private findAttributeTranslation(attr: SearchAttribute): string { |
| let key = attr.source + '.' + attr.boType + '.' + attr.attrName; |
| let retVal = this.allAttribueTranslations.find(a => a['key'].localeCompare(key) === 0); |
| return (retVal === undefined) ? attr.attrName : retVal['label']; |
| } |
| |
| showDialog(currentView: View) { |
| this.asyncTranslateBoTypesAndAttributes(); |
| |
| this.currentView = classToClass(currentView); |
| this.isNameReadOnly(); |
| this.childModal.show(); |
| return this.coloumnsSubmitted; |
| } |
| |
| closeDialog() { |
| this.childModal.hide(); |
| } |
| |
| remove(col: ViewColumn) { |
| this.currentView.columns = this.currentView.columns.filter(c => c !== col); |
| } |
| |
| isLast(col: ViewColumn) { |
| return this.currentView.columns.indexOf(col) === this.currentView.columns.length - 1; |
| } |
| |
| isFirst(col: ViewColumn) { |
| return this.currentView.columns.indexOf(col) === 0; |
| } |
| |
| moveUp(col: ViewColumn) { |
| if (!this.isFirst(col)) { |
| let oldIndex = this.currentView.columns.indexOf(col); |
| let otherCol = this.currentView.columns[oldIndex - 1]; |
| this.currentView.columns[oldIndex] = otherCol; |
| this.currentView.columns[oldIndex - 1] = col; |
| } |
| } |
| |
| moveDown(col: ViewColumn) { |
| if (!this.isLast(col)) { |
| let oldIndex = this.currentView.columns.indexOf(col); |
| let otherCol = this.currentView.columns[oldIndex + 1]; |
| this.currentView.columns[oldIndex] = otherCol; |
| this.currentView.columns[oldIndex + 1] = col; |
| } |
| } |
| |
| selectNode(node: TreeNode) { |
| if (node.type !== 'attribute') { |
| return; |
| } |
| this.pushViewCol(new ViewColumn(node.parent.label, node.label)); |
| } |
| |
| pushViewCol(viewCol: ViewColumn) { |
| if (viewCol && this.currentView.columns.findIndex(c => viewCol.equals(c)) === -1 ) { |
| this.currentView.columns.push(viewCol); |
| } else { |
| this.notificationService.notifyInfo(this.translateService.instant('tableview.editview.attribute-already-selected'), 'Info'); |
| } |
| } |
| |
| isAsc(col: ViewColumn) { |
| return col.sortOrder === 1; |
| } |
| isDesc(col: ViewColumn) { |
| return col.sortOrder === -1; |
| } |
| isNone(col: ViewColumn) { |
| return col.sortOrder === null; |
| } |
| |
| toggleSort(col: ViewColumn) { |
| if (col.sortOrder === null) { |
| this.currentView.setSortOrder(col.type, col.name, 1); |
| } else if (col.sortOrder === 1) { |
| this.currentView.setSortOrder(col.type, col.name, -1); |
| } else if (col.sortOrder === -1) { |
| this.currentView.setSortOrder(col.type, col.name, null); |
| } |
| } |
| |
| private uniqueBy<T>(a: T[], key: (T) => any) { |
| let seen = {}; |
| return a.filter(function(item) { |
| let k = key(item); |
| return seen.hasOwnProperty(k) ? false : (seen[k] = true); |
| }); |
| } |
| |
| public typeaheadOnSelect(match: TypeaheadMatch) { |
| this.pushViewCol(new ViewColumn(match.item.attribute.boType, match.item.attribute.attrName)); |
| this.selectedAttribute = undefined; |
| } |
| |
| private isNameReadOnly() { |
| return this.isReadOnly = (this.currentView.name === '') ? false : true; |
| } |
| |
| applyChanges() { |
| this.coloumnsSubmitted.emit(this.currentView); |
| this.closeDialog(); |
| } |
| } |