From 43b23cef4feb81697eb7807670b9ad5adb2edd0f Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 8 Jun 2017 21:15:47 +0300 Subject: [PATCH] TB-61: Improve Alias list editor. --- ui/src/app/api/alias-controller.js | 4 + ui/src/app/api/subscription.js | 103 ++--------- ui/src/app/common/dashboard-utils.service.js | 85 +++++++-- ui/src/app/common/utils.service.js | 11 ++ ui/src/app/components/dashboard.directive.js | 31 +++- ui/src/app/components/widget.controller.js | 157 +++++++++++------ ui/src/app/components/widget.directive.js | 85 +-------- .../aliases-entity-select-panel.controller.js | 15 +- .../aliases-entity-select-panel.tpl.html | 4 +- .../entity/aliases-entity-select.directive.js | 8 + ...d-widget-to-dashboard-dialog.controller.js | 11 +- .../attribute/attribute-table.directive.js | 43 +++-- .../entity/attribute/attribute-table.tpl.html | 2 +- .../app/entity/entity-aliases.controller.js | 17 +- ui/src/app/entity/entity-aliases.scss | 15 +- ui/src/app/entity/entity-aliases.tpl.html | 56 +++--- ui/src/app/entity/entity-filter.directive.js | 163 +----------------- ui/src/app/entity/entity-filter.scss | 15 +- ui/src/app/entity/entity-filter.tpl.html | 101 ++++------- .../import-export/import-export.service.js | 9 +- ui/src/app/locale/locale.constant.js | 2 +- ui/src/app/services/item-buffer.service.js | 6 +- 22 files changed, 363 insertions(+), 580 deletions(-) diff --git a/ui/src/app/api/alias-controller.js b/ui/src/app/api/alias-controller.js index 49eb8fe40e..b6d29a9599 100644 --- a/ui/src/app/api/alias-controller.js +++ b/ui/src/app/api/alias-controller.js @@ -100,6 +100,7 @@ export default class AliasController { aliasCtrl.resolvedAliasesToStateEntities[aliasId] = aliasCtrl.stateController.getStateParams().entityId; } + aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId); deferred.resolve(aliasInfo); }, function fail() { @@ -239,6 +240,9 @@ export default class AliasController { datasource.name = name; datasource.aliasName = name; datasource.entityName = name; + } else if (datasource.unresolvedStateEntity) { + datasource.name = "Unresolved"; + datasource.entityName = "Unresolved"; } datasource.dataKeys.forEach(function(dataKey) { if (datasource.generated) { diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js index dd518c7ea5..4cb382ceab 100644 --- a/ui/src/app/api/subscription.js +++ b/ui/src/app/api/subscription.js @@ -73,6 +73,15 @@ export default class Subscription { this.datasources = this.ctx.utils.validateDatasources(options.datasources); this.datasourceListeners = []; + + /* + * data = array of datasourceData + * datasourceData = { + * tbDatasource, + * dataKey, { name, config } + * data = array of [time, value] + * } + */ this.data = []; this.hiddenData = []; this.originalTimewindow = null; @@ -543,39 +552,6 @@ export default class Subscription { var datasource = this.datasources[i]; if (angular.isFunction(datasource)) continue; - /* var entityId = null; - var entityType = null; - if (datasource.type === this.ctx.types.datasourceType.entity) { - var aliasName = null; - var entityName = null; - if (datasource.entityId) { - entityId = datasource.entityId; - entityType = datasource.entityType; - datasource.name = datasource.entityName; - aliasName = datasource.entityName; - entityName = datasource.entityName; - } else if (datasource.entityAliasId) { - if (this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId]) { - entityId = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].entityId; - entityType = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].entityType; - datasource.name = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].alias; - aliasName = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].alias; - entityName = ''; - var entitiesInfo = this.ctx.aliasesInfo.entityAliasesInfo[datasource.entityAliasId]; - for (var d = 0; d < entitiesInfo.length; d++) { - if (entitiesInfo[d].id === entityId) { - entityName = entitiesInfo[d].name; - break; - } - } - } - } - } else { - datasource.name = datasource.name || this.ctx.types.datasourceType.function; - } - for (var dk = 0; dk < datasource.dataKeys.length; dk++) { - updateDataKeyLabel(datasource.dataKeys[dk], datasource.name, entityName, aliasName); - }*/ var subscription = this; @@ -606,6 +582,10 @@ export default class Subscription { this.datasourceListeners.push(listener); this.ctx.datasourceService.subscribeToDatasource(listener); + if (datasource.unresolvedStateEntity) { + this.notifyDataLoaded(); + this.onDataUpdated(); + } } } @@ -625,19 +605,6 @@ export default class Subscription { } else { return false; } - /*var deviceId = null; - if (this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId]) { - deviceId = this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId].entityId; - } - if (!angular.equals(deviceId, this.targetDeviceId)) { - this.targetDeviceId = deviceId; - if (this.targetDeviceId) { - this.rpcEnabled = true; - } else { - this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false; - } - this.callbacks.rpcStateChanged(this); - }*/ } checkSubscriptions(aliasIds) { @@ -650,29 +617,8 @@ export default class Subscription { break; } } - /*var entityId = null; - var entityType = null; - var aliasName = null; - if (listener.datasource.type === this.ctx.types.datasourceType.entity) { - if (listener.datasource.entityAliasId && - this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId]) { - entityId = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].entityId; - entityType = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].entityType; - aliasName = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].alias; - } - if (!angular.equals(entityId, listener.entityId) || - !angular.equals(entityType, listener.entityType) || - !angular.equals(aliasName, listener.datasource.name)) { - subscriptionsChanged = true; - break; - } - }*/ } return subscriptionsChanged; - /*if (subscriptionsChanged) { - this.unsubscribe(); - this.subscribe(); - }*/ } destroy() { @@ -691,29 +637,6 @@ export default class Subscription { } -/*const varsRegex = /\$\{([^\}]*)\}/g; - -function updateDataKeyLabel(dataKey, dsName, entityName, aliasName) { - var pattern = dataKey.pattern; - var label = dataKey.pattern; - var match = varsRegex.exec(pattern); - while (match !== null) { - var variable = match[0]; - var variableName = match[1]; - if (variableName === 'dsName') { - label = label.split(variable).join(dsName); - } else if (variableName === 'entityName') { - label = label.split(variable).join(entityName); - } else if (variableName === 'deviceName') { - label = label.split(variable).join(entityName); - } else if (variableName === 'aliasName') { - label = label.split(variable).join(aliasName); - } - match = varsRegex.exec(pattern); - } - dataKey.label = label; -}*/ - function calculateMin(data) { if (data.length > 0) { var result = Number(data[0][1]); diff --git a/ui/src/app/common/dashboard-utils.service.js b/ui/src/app/common/dashboard-utils.service.js index 58d1dbde7f..fe8aa93f7b 100644 --- a/ui/src/app/common/dashboard-utils.service.js +++ b/ui/src/app/common/dashboard-utils.service.js @@ -23,8 +23,10 @@ function DashboardUtils(types, utils, timeService) { var service = { validateAndUpdateDashboard: validateAndUpdateDashboard, + validateAndUpdateWidget: validateAndUpdateWidget, getRootStateId: getRootStateId, createSingleWidgetDashboard: createSingleWidgetDashboard, + createSingleEntityFilter: createSingleEntityFilter, getStateLayoutsData: getStateLayoutsData, createDefaultState: createDefaultState, createDefaultLayoutData: createDefaultLayoutData, @@ -39,7 +41,7 @@ function DashboardUtils(types, utils, timeService) { return service; - function validateAndUpdateEntityAliases(configuration) { + function validateAndUpdateEntityAliases(configuration, datasourcesByAliasId, targetDevicesByAliasId) { var aliasId, entityAlias; if (angular.isUndefined(configuration.entityAliases)) { configuration.entityAliases = {}; @@ -47,8 +49,8 @@ function DashboardUtils(types, utils, timeService) { var deviceAliases = configuration.deviceAliases; for (aliasId in deviceAliases) { var deviceAlias = deviceAliases[aliasId]; - entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias); - configuration.entityAliases[aliasId] = entityAlias; + entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId); + configuration.entityAliases[entityAlias.id] = entityAlias; } delete configuration.deviceAliases; } @@ -56,16 +58,43 @@ function DashboardUtils(types, utils, timeService) { var entityAliases = configuration.entityAliases; for (aliasId in entityAliases) { entityAlias = entityAliases[aliasId]; - entityAliases[aliasId] = validateAndUpdateEntityAlias(entityAlias); - if (!entityAliases[aliasId].id) { - entityAliases[aliasId].id = aliasId; + entityAlias = validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId); + if (aliasId != entityAlias.id) { + delete entityAliases[aliasId]; } + entityAliases[entityAlias.id] = entityAlias; } } return configuration; } - function validateAndUpdateDeviceAlias(aliasId, deviceAlias) { + function validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId) { + if (!aliasId || !angular.isString(aliasId) || aliasId.length != 36) { + var newAliasId = utils.guid(); + var aliasDatasources = datasourcesByAliasId[aliasId]; + if (aliasDatasources) { + aliasDatasources.forEach( + function(datasource) { + datasource.entityAliasId = newAliasId; + } + ); + } + var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId]; + if (targetDeviceAliasIdsList) { + targetDeviceAliasIdsList.forEach( + function(targetDeviceAliasIds) { + targetDeviceAliasIds[0] = newAliasId; + } + ); + } + return newAliasId; + } else { + return aliasId; + } + } + + function validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId) { + aliasId = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId); var alias = deviceAlias.alias; var entityAlias = { id: aliasId, @@ -93,7 +122,8 @@ function DashboardUtils(types, utils, timeService) { return entityAlias; } - function validateAndUpdateEntityAlias(entityAlias) { + function validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId) { + entityAlias.id = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId); if (!entityAlias.filter) { entityAlias.filter = { type: entityAlias.entityFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value, @@ -206,12 +236,33 @@ function DashboardUtils(types, utils, timeService) { } } - /* var datasources = {}; + var datasourcesByAliasId = {}; + var targetDevicesByAliasId = {}; for (var widgetId in dashboard.configuration.widgets) { + widget = dashboard.configuration.widgets[widgetId]; + widget.config.datasources.forEach(function (datasource) { + if (datasource.entityAliasId) { + var aliasId = datasource.entityAliasId; + var aliasDatasources = datasourcesByAliasId[aliasId]; + if (!aliasDatasources) { + aliasDatasources = []; + datasourcesByAliasId[aliasId] = aliasDatasources; + } + aliasDatasources.push(datasource); + } + }); + if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length) { + var aliasId = widget.config.targetDeviceAliasIds[0]; + var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId]; + if (!targetDeviceAliasIdsList) { + targetDeviceAliasIdsList = []; + targetDevicesByAliasId[aliasId] = targetDeviceAliasIdsList; + } + targetDeviceAliasIdsList.push(widget.config.targetDeviceAliasIds); + } + } - }*/ - - dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration); + dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration, datasourcesByAliasId, targetDevicesByAliasId); if (angular.isUndefined(dashboard.configuration.timewindow)) { dashboard.configuration.timewindow = timeService.defaultTimewindow(); @@ -288,6 +339,16 @@ function DashboardUtils(types, utils, timeService) { return dashboard; } + function createSingleEntityFilter(entityType, entityId) { + return { + type: types.aliasFilterType.entityList.value, + stateEntity: false, + entityList: [entityId], + entityType: entityType, + resolveMultiple: false + }; + } + function getStateLayoutsData(dashboard, targetState) { var dashboardConfiguration = dashboard.configuration; var states = dashboardConfiguration.states; diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js index 7b4d65ef78..8bf76c4f0f 100644 --- a/ui/src/app/common/utils.service.js +++ b/ui/src/app/common/utils.service.js @@ -106,6 +106,7 @@ function Utils($mdColorPalette, $rootScope, $window, types) { isDescriptorSchemaNotEmpty: isDescriptorSchemaNotEmpty, filterSearchTextEntities: filterSearchTextEntities, guid: guid, + cleanCopy: cleanCopy, isLocalUrl: isLocalUrl, validateDatasources: validateDatasources, createKey: createKey, @@ -291,6 +292,16 @@ function Utils($mdColorPalette, $rootScope, $window, types) { s4() + '-' + s4() + s4() + s4(); } + function cleanCopy(object) { + var copy = angular.copy(object); + for (var prop in copy) { + if (prop && prop.startsWith('$$')) { + delete copy[prop]; + } + } + return copy; + } + function genNextColor(datasources) { var index = 0; if (datasources) { diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js index 965b83854f..a88e656fe2 100644 --- a/ui/src/app/components/dashboard.directive.js +++ b/ui/src/app/components/dashboard.directive.js @@ -85,7 +85,7 @@ function Dashboard() { } /*@ngInject*/ -function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, timeService, types, utils) { +function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $mdUtil, timeService, types, utils) { var highlightedMode = false; var highlightedWidget = null; @@ -792,7 +792,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t } function dashboardLoaded() { - $timeout(function () { + $mdUtil.nextTick(function () { if (vm.dashboardTimewindowWatch) { vm.dashboardTimewindowWatch(); vm.dashboardTimewindowWatch = null; @@ -802,14 +802,27 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t }, true); adoptMaxRows(); vm.dashboardLoading = false; - $timeout(function () { - var gridsterScope = gridsterElement.scope(); - vm.gridster = gridsterScope.gridster; - if (vm.onInit) { - vm.onInit({dashboard: vm}); + if ($scope.gridsterScopeWatcher) { + $scope.gridsterScopeWatcher(); + } + $scope.gridsterScopeWatcher = $scope.$watch( + function() { + var hasScope = gridsterElement.scope() ? true : false; + return hasScope; + }, + function(hasScope) { + if (hasScope) { + $scope.gridsterScopeWatcher(); + $scope.gridsterScopeWatcher = null; + var gridsterScope = gridsterElement.scope(); + vm.gridster = gridsterScope.gridster; + if (vm.onInit) { + vm.onInit({dashboard: vm}); + } + } } - }, 0, false); - }, 0, false); + ); + }); } function loading() { diff --git a/ui/src/app/components/widget.controller.js b/ui/src/app/components/widget.controller.js index 4407381491..08885b841e 100644 --- a/ui/src/app/components/widget.controller.js +++ b/ui/src/app/components/widget.controller.js @@ -20,9 +20,9 @@ import Subscription from '../api/subscription'; /* eslint-disable angular/angularelement */ /*@ngInject*/ -export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, tbRaf, types, utils, timeService, +export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService, datasourceService, entityService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow, - dashboardTimewindowApi, widget, aliasController, stateController, widgetType) { + dashboardTimewindowApi, widget, aliasController, stateController, widgetInfo, widgetType) { var vm = this; @@ -42,20 +42,11 @@ export default function WidgetController($scope, $timeout, $window, $element, $q var cafs = {}; - /* - * data = array of datasourceData - * datasourceData = { - * tbDatasource, - * dataKey, { name, config } - * data = array of [time, value] - * } - */ - var widgetContext = { inited: false, $scope: $scope, - $container: $('#container', $element), - $containerParent: $($element), + $container: null, + $containerParent: null, width: 0, height: 0, isEdit: isEdit, @@ -82,30 +73,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q createSubscription: function(options, subscribe) { return createSubscription(options, subscribe); }, - - - // type: "timeseries" or "latest" or "rpc" - /* subscriptionInfo = [ - { - entityType: "" - entityId: "" - entityName: "" - timeseries: [{ name: "", label: "" }, ..] - attributes: [{ name: "", label: "" }, ..] - } - .. - ]*/ - - // options = { - // timeWindowConfig, - // useDashboardTimewindow, - // legendConfig, - // decimals, - // units, - // callbacks [ onDataUpdated(subscription, apply) ] - // } - // - createSubscriptionFromInfo: function (type, subscriptionsInfo, options, useDefaultComponents, subscribe) { return createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe); }, @@ -211,21 +178,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q } ); - /* - options = { - type, - targetDeviceAliasIds, // RPC - targetDeviceIds, // RPC - datasources, - timeWindowConfig, - useDashboardTimewindow, - legendConfig, - decimals, - units, - callbacks - } - */ - function createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe) { var deferred = $q.defer(); options.type = type; @@ -400,6 +352,101 @@ export default function WidgetController($scope, $timeout, $window, $element, $q return deferred.promise; } + function configureWidgetElement() { + + $scope.displayLegend = angular.isDefined(widget.config.showLegend) ? + widget.config.showLegend : widget.type === types.widgetType.timeseries.value; + + if ($scope.displayLegend) { + $scope.legendConfig = widget.config.legendConfig || + { + position: types.position.bottom.value, + showMin: false, + showMax: false, + showAvg: widget.type === types.widgetType.timeseries.value, + showTotal: false + }; + $scope.legendData = { + keys: [], + data: [] + }; + } + + var html = '
' + + 'Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}' + + '
' + + '
' + + '' + + '
'; + + var containerHtml = '
' + widgetInfo.templateHtml + '
'; + if ($scope.displayLegend) { + var layoutType; + if ($scope.legendConfig.position === types.position.top.value || + $scope.legendConfig.position === types.position.bottom.value) { + layoutType = 'column'; + } else { + layoutType = 'row'; + } + + var legendStyle; + switch($scope.legendConfig.position) { + case types.position.top.value: + legendStyle = 'padding-bottom: 8px;'; + break; + case types.position.bottom.value: + legendStyle = 'padding-top: 8px;'; + break; + case types.position.left.value: + legendStyle = 'padding-right: 0px;'; + break; + case types.position.right.value: + legendStyle = 'padding-left: 0px;'; + break; + } + + var legendHtml = ''; + containerHtml = '
' + containerHtml + '
'; + html += '
'; + if ($scope.legendConfig.position === types.position.top.value || + $scope.legendConfig.position === types.position.left.value) { + html += legendHtml; + html += containerHtml; + } else { + html += containerHtml; + html += legendHtml; + } + html += '
'; + } else { + html += containerHtml; + } + + //TODO: + /*if (progressElement) { + progressScope.$destroy(); + progressScope = null; + + progressElement.remove(); + progressElement = null; + }*/ + + $element.html(html); + + var containerElement = $scope.displayLegend ? angular.element($element[0].querySelector('#widget-container')) : $element; + widgetContext.$container = $('#container', containerElement); + widgetContext.$containerParent = $(containerElement); + + $compile($element.contents())($scope); + + addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef + } + + function destroyWidgetElement() { + removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef + $element.html(''); + widgetContext.$container = null; + widgetContext.$containerParent = null; + } function initialize() { @@ -407,8 +454,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q onEditModeChanged(isEdit); }); - addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef - $scope.$watch(function () { return widget.row + ',' + widget.col + ',' + widget.config.mobileOrder; }, function () { @@ -439,10 +484,10 @@ export default function WidgetController($scope, $timeout, $window, $element, $q }); $scope.$on("$destroy", function () { - removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef onDestroy(); }); + configureWidgetElement(); var deferred = $q.defer(); if (!vm.useCustomDatasources) { createDefaultSubscription().then( @@ -465,6 +510,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q function reInit() { onDestroy(); + configureWidgetElement(); if (!vm.useCustomDatasources) { createDefaultSubscription().then( function success() { @@ -636,6 +682,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q handleWidgetException(e); } } + destroyWidgetElement(); } //TODO: widgets visibility diff --git a/ui/src/app/components/widget.directive.js b/ui/src/app/components/widget.directive.js index c92e5443bc..a0f1b83c48 100644 --- a/ui/src/app/components/widget.directive.js +++ b/ui/src/app/components/widget.directive.js @@ -28,7 +28,7 @@ export default angular.module('thingsboard.directives.widget', [thingsboardLegen .name; /*@ngInject*/ -function Widget($controller, $compile, types, widgetService) { +function Widget($controller, widgetService) { return { scope: true, link: function (scope, elem, attrs) { @@ -81,90 +81,9 @@ function Widget($controller, $compile, types, widgetService) { elem.addClass(widgetNamespace); - var html = '
' + - 'Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}' + - '
' + - '
' + - '' + - '
'; - - scope.displayLegend = angular.isDefined(widget.config.showLegend) ? - widget.config.showLegend : widget.type === types.widgetType.timeseries.value; - - - var containerHtml = '
' + widgetInfo.templateHtml + '
'; - if (scope.displayLegend) { - scope.legendConfig = widget.config.legendConfig || - { - position: types.position.bottom.value, - showMin: false, - showMax: false, - showAvg: widget.type === types.widgetType.timeseries.value, - showTotal: false - }; - scope.legendData = { - keys: [], - data: [] - }; - - var layoutType; - if (scope.legendConfig.position === types.position.top.value || - scope.legendConfig.position === types.position.bottom.value) { - layoutType = 'column'; - } else { - layoutType = 'row'; - } - - var legendStyle; - switch(scope.legendConfig.position) { - case types.position.top.value: - legendStyle = 'padding-bottom: 8px;'; - break; - case types.position.bottom.value: - legendStyle = 'padding-top: 8px;'; - break; - case types.position.left.value: - legendStyle = 'padding-right: 0px;'; - break; - case types.position.right.value: - legendStyle = 'padding-left: 0px;'; - break; - } - - var legendHtml = ''; - containerHtml = '
' + containerHtml + '
'; - html += '
'; - if (scope.legendConfig.position === types.position.top.value || - scope.legendConfig.position === types.position.left.value) { - html += legendHtml; - html += containerHtml; - } else { - html += containerHtml; - html += legendHtml; - } - html += '
'; - } else { - html += containerHtml; - } - - //TODO: - /*if (progressElement) { - progressScope.$destroy(); - progressScope = null; - - progressElement.remove(); - progressElement = null; - }*/ - - elem.html(html); - - var containerElement = scope.displayLegend ? angular.element(elem[0].querySelector('#widget-container')) : elem; - - $compile(elem.contents())(scope); - var widgetType = widgetService.getWidgetTypeFunction(widget.bundleAlias, widget.typeAlias, widget.isSystemType); - angular.extend(locals, {$scope: scope, $element: containerElement, widgetType: widgetType}); + angular.extend(locals, {$scope: scope, $element: elem, widgetInfo: widgetInfo, widgetType: widgetType}); widgetController = $controller('WidgetController', locals); diff --git a/ui/src/app/entity/aliases-entity-select-panel.controller.js b/ui/src/app/entity/aliases-entity-select-panel.controller.js index 8439269c1e..5365db74be 100644 --- a/ui/src/app/entity/aliases-entity-select-panel.controller.js +++ b/ui/src/app/entity/aliases-entity-select-panel.controller.js @@ -15,7 +15,7 @@ */ /*@ngInject*/ -export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, types, aliasController, onEntityAliasesUpdate) { +export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, $filter, types, aliasController, onEntityAliasesUpdate) { var vm = this; vm._mdPanelRef = mdPanelRef; @@ -31,13 +31,18 @@ export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, t var aliasInfo = vm.aliasController.getInstantAliasInfo(aliasId); if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity) { vm.entityAliasesInfo[aliasId] = angular.copy(aliasInfo); + vm.entityAliasesInfo[aliasId].selectedId = aliasInfo.currentEntity.id; } } - function currentAliasEntityChanged(aliasId, currentEntity) { - vm.aliasController.updateCurrentAliasEntity(aliasId, currentEntity); - if (onEntityAliasesUpdate) { - onEntityAliasesUpdate(); + function currentAliasEntityChanged(aliasId, selectedId) { + var resolvedEntities = vm.entityAliasesInfo[aliasId].resolvedEntities; + var selected = $filter('filter')(resolvedEntities, {id: selectedId}); + if (selected && selected.length) { + vm.aliasController.updateCurrentAliasEntity(aliasId, selected[0]); + if (onEntityAliasesUpdate) { + onEntityAliasesUpdate(); + } } } diff --git a/ui/src/app/entity/aliases-entity-select-panel.tpl.html b/ui/src/app/entity/aliases-entity-select-panel.tpl.html index edc8e5ade9..d197d0bd9c 100644 --- a/ui/src/app/entity/aliases-entity-select-panel.tpl.html +++ b/ui/src/app/entity/aliases-entity-select-panel.tpl.html @@ -21,8 +21,8 @@
- - + + {{resolvedEntity.name}} diff --git a/ui/src/app/entity/aliases-entity-select.directive.js b/ui/src/app/entity/aliases-entity-select.directive.js index 7e174ef0d7..9a61311b1e 100644 --- a/ui/src/app/entity/aliases-entity-select.directive.js +++ b/ui/src/app/entity/aliases-entity-select.directive.js @@ -94,6 +94,14 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $ $mdPanel.open(config); } + scope.$on('entityAliasesChanged', function() { + scope.updateView(); + }); + + scope.$on('entityAliasResolved', function() { + scope.updateView(); + }); + scope.updateView = function () { updateDisplayValue(); } diff --git a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js index b28a46bc48..bcf37dd825 100644 --- a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js +++ b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js @@ -22,7 +22,7 @@ import selectTargetLayoutTemplate from '../../dashboard/layouts/select-target-la /* eslint-enable import/no-unresolved, import/default */ /*@ngInject*/ -export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document, +export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document, dashboardUtils, types, itembuffer, dashboardService, entityId, entityType, entityName, widget) { var vm = this; @@ -126,15 +126,8 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog, targetDeviceAliases: {} }; aliasesInfo.datasourceAliases[0] = { - id: 1, alias: entityName, - filter: { - type: types.aliasFilterType.entityList.value, - stateEntity: false, - entityList: [entityId], - entityType: entityType, - resolveMultiple: false - } + filter: dashboardUtils.createSingleEntityFilter(entityType, entityId) }; itembuffer.addWidgetToDashboard(theDashboard, targetState, targetLayout, vm.widget, aliasesInfo, null, 48, null, -1, -1).then( function(theDashboard) { diff --git a/ui/src/app/entity/attribute/attribute-table.directive.js b/ui/src/app/entity/attribute/attribute-table.directive.js index da7697d5d1..62d2081853 100644 --- a/ui/src/app/entity/attribute/attribute-table.directive.js +++ b/ui/src/app/entity/attribute/attribute-table.directive.js @@ -26,10 +26,12 @@ import editAttributeValueTemplate from './edit-attribute-value.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ import EditAttributeValueController from './edit-attribute-value.controller'; +import AliasController from '../../api/alias-controller'; /*@ngInject*/ export default function AttributeTableDirective($compile, $templateCache, $rootScope, $q, $mdEditDialog, $mdDialog, - $document, $translate, $filter, utils, types, dashboardService, attributeService, widgetService) { + $mdUtil, $document, $translate, $filter, utils, types, dashboardUtils, + dashboardService, entityService, attributeService, widgetService) { var linker = function (scope, element, attrs) { @@ -246,15 +248,19 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS } scope.nextWidget = function() { - if (scope.widgetsCarousel.index < scope.widgetsList.length-1) { - scope.widgetsCarousel.index++; - } + $mdUtil.nextTick(function () { + if (scope.widgetsCarousel.index < scope.widgetsList.length - 1) { + scope.widgetsCarousel.index++; + } + }); } scope.prevWidget = function() { - if (scope.widgetsCarousel.index > 0) { - scope.widgetsCarousel.index--; - } + $mdUtil.nextTick(function () { + if (scope.widgetsCarousel.index > 0) { + scope.widgetsCarousel.index--; + } + }); } scope.enterWidgetMode = function() { @@ -281,23 +287,28 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS scope.firstBundle = true; scope.selectedWidgetsBundleAlias = types.systemBundleAlias.cards; - scope.aliasesInfo = { - entityAliases: { - '1': {alias: scope.entityName, entityType: scope.entityType, entityId: scope.entityId} - }, - entityAliasesInfo: { - '1': [ - {name: scope.entityName, entityType: scope.entityType, id: scope.entityId} - ] + var entityAlias = { + id: utils.guid(), + alias: scope.entityName, + filter: dashboardUtils.createSingleEntityFilter(scope.entityType, scope.entityId) + }; + var entitiAliases = {}; + entitiAliases[entityAlias.id] = entityAlias; + + var stateController = { + getStateParams: function() { + return {}; } }; + scope.aliasController = new AliasController(scope, $q, $filter, utils, + types, entityService, stateController, entitiAliases); var dataKeyType = scope.attributeScope === types.latestTelemetry ? types.dataKeyType.timeseries : types.dataKeyType.attribute; var datasource = { type: types.datasourceType.entity, - entityAliasId: '1', + entityAliasId: entityAlias.id, dataKeys: [] } var i = 0; diff --git a/ui/src/app/entity/attribute/attribute-table.tpl.html b/ui/src/app/entity/attribute/attribute-table.tpl.html index 1afd1bb452..2566a08161 100644 --- a/ui/src/app/entity/attribute/attribute-table.tpl.html +++ b/ui/src/app/entity/attribute/attribute-table.tpl.html @@ -156,7 +156,7 @@ rn-swipe-disabled="true">
  • +
    + +
    + entity.alias +
    + alias.entity-filter + alias.resolve-multiple +
    + +
    +
    +
    - +
    -
    - -
    - entity.alias - alias.entity-filter - -
    -
    -
    +
    {{$index + 1}}.
    @@ -54,13 +59,12 @@
    entity.alias-required
    -
    - - -
    + + @@ -73,18 +77,18 @@
    -
    - - - {{ 'entity.add-alias' | translate }} - - action.add - -
    + + + {{ 'entity.add-alias' | translate }} + + action.add + {{ 'action.save' | translate }} diff --git a/ui/src/app/entity/entity-filter.directive.js b/ui/src/app/entity/entity-filter.directive.js index beded8a162..0f9cf5c896 100644 --- a/ui/src/app/entity/entity-filter.directive.js +++ b/ui/src/app/entity/entity-filter.directive.js @@ -35,46 +35,16 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc scope.ngModelCtrl = ngModelCtrl; scope.types = types; - - /* scope.fetchEntities = function(searchText, limit) { - var deferred = $q.defer(); - entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(function success(result) { - if (result) { - deferred.resolve(result); - } else { - deferred.resolve([]); - } - }, function fail() { - deferred.reject(); - }); - return deferred.promise; - }*/ + scope.hideLabels = angular.isDefined(attrs.hideLabels); scope.updateValidity = function() { if (ngModelCtrl.$viewValue) { var value = ngModelCtrl.$viewValue; ngModelCtrl.$setValidity('filter', value.type ? true : false); - /*if (value.useFilter) { - ngModelCtrl.$setValidity('entityList', true); - if (angular.isDefined(value.entityNameFilter) && value.entityNameFilter.length > 0) { - ngModelCtrl.$setValidity('entityNameFilter', true); - valid = angular.isDefined(scope.model.matchingFilterEntity) && scope.model.matchingFilterEntity != null; - ngModelCtrl.$setValidity('entityNameFilterEntityMatch', valid); - } else { - ngModelCtrl.$setValidity('entityNameFilter', false); - } - } else { - ngModelCtrl.$setValidity('entityNameFilter', true); - ngModelCtrl.$setValidity('entityNameFilterDeviceMatch', true); - valid = angular.isDefined(value.entityList) && value.entityList.length > 0; - ngModelCtrl.$setValidity('entityList', valid); - }*/ - } } ngModelCtrl.$render = function () { - //destroyWatchers(); if (ngModelCtrl.$viewValue) { scope.model = angular.copy(ngModelCtrl.$viewValue); } else { @@ -83,28 +53,6 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc resolveMultiple: false } } - /* if (ngModelCtrl.$viewValue) { - var value = ngModelCtrl.$viewValue; - var model = scope.model; - model.useFilter = value.useFilter === true ? true: false; - model.entityList = []; - model.entityNameFilter = value.entityNameFilter || ''; - processEntityNameFilter(model.entityNameFilter).then( - function(entity) { - scope.model.matchingFilterEntity = entity; - if (value.entityList && value.entityList.length > 0) { - entityService.getEntities(scope.entityType, value.entityList).then(function (entities) { - model.entityList = entities; - updateMatchingEntity(); - initWatchers(); - }); - } else { - updateMatchingEntity(); - initWatchers(); - } - } - ) - }*/ } scope.$watch('model.resolveMultiple', function () { @@ -149,115 +97,6 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc }); } - /* function updateMatchingEntity() { - if (scope.model.useFilter) { - scope.model.matchingEntity = scope.model.matchingFilterEntity; - } else { - if (scope.model.entityList && scope.model.entityList.length > 0) { - scope.model.matchingEntity = scope.model.entityList[0]; - } else { - scope.model.matchingEntity = null; - } - } - } - - function processEntityNameFilter(entityNameFilter) { - var deferred = $q.defer(); - if (angular.isDefined(entityNameFilter) && entityNameFilter.length > 0) { - scope.fetchEntities(entityNameFilter, 1).then(function (entities) { - if (entities && entities.length > 0) { - deferred.resolve(entities[0]); - } else { - deferred.resolve(null); - } - }); - } else { - deferred.resolve(null); - } - return deferred.promise; - } - - function destroyWatchers() { - if (scope.entityTypeDeregistration) { - scope.entityTypeDeregistration(); - scope.entityTypeDeregistration = null; - } - if (scope.entityListDeregistration) { - scope.entityListDeregistration(); - scope.entityListDeregistration = null; - } - if (scope.useFilterDeregistration) { - scope.useFilterDeregistration(); - scope.useFilterDeregistration = null; - } - if (scope.entityNameFilterDeregistration) { - scope.entityNameFilterDeregistration(); - scope.entityNameFilterDeregistration = null; - } - if (scope.matchingEntityDeregistration) { - scope.matchingEntityDeregistration(); - scope.matchingEntityDeregistration = null; - } - } - - function initWatchers() { - - scope.entityTypeDeregistration = scope.$watch('entityType', function (newEntityType, prevEntityType) { - if (!angular.equals(newEntityType, prevEntityType)) { - scope.model.entityList = []; - scope.model.entityNameFilter = ''; - } - }); - - scope.entityListDeregistration = scope.$watch('model.entityList', function () { - if (ngModelCtrl.$viewValue) { - var value = ngModelCtrl.$viewValue; - value.entityList = []; - if (scope.model.entityList && scope.model.entityList.length > 0) { - for (var i=0;i -
    -
    - -
    -
    {{ types.aliasFilterType[model.type].name | translate }}
    - - - {{ 'alias.edit-entity-filter' | translate }} - - +
    +
    +
    {{ types.aliasFilterType[model.type].name | translate }}
    + + - edit + add + {{ 'alias.create-entity-filter' | translate }} -
    - - - add - - {{ 'alias.create-entity-filter' | translate }} - -
    -
    -
    - - -
    + + + {{ 'alias.edit-entity-filter' | translate }} + + + edit + + +
    +
    + + +
    - -
    \ No newline at end of file +
    diff --git a/ui/src/app/import-export/import-export.service.js b/ui/src/app/import-export/import-export.service.js index 7f7f774dff..9b36c38d46 100644 --- a/ui/src/app/import-export/import-export.service.js +++ b/ui/src/app/import-export/import-export.service.js @@ -24,7 +24,7 @@ import entityAliasesTemplate from '../entity/entity-aliases.tpl.html'; /* eslint-disable no-undef, angular/window-service, angular/document-service */ /*@ngInject*/ -export default function ImportExport($log, $translate, $q, $mdDialog, $document, itembuffer, types, dashboardUtils, +export default function ImportExport($log, $translate, $q, $mdDialog, $document, itembuffer, utils, types, dashboardUtils, entityService, dashboardService, pluginService, ruleService, widgetService, toast) { @@ -415,6 +415,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, deferred.reject(); } else { var widget = widgetItem.widget; + widget = dashboardUtils.validateAndUpdateWidget(widget); var aliasesInfo = prepareAliasesInfo(widgetItem.aliasesInfo); var originalColumns = widgetItem.originalColumns; var originalSize = widgetItem.originalSize; @@ -425,22 +426,22 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, var entityAliases = {}; var datasourceAliasesMap = {}; var targetDeviceAliasesMap = {}; - var aliasId = 1; + var aliasId; var datasourceIndex; if (datasourceAliases) { for (datasourceIndex in datasourceAliases) { + aliasId = utils.guid(); datasourceAliasesMap[aliasId] = datasourceIndex; entityAliases[aliasId] = datasourceAliases[datasourceIndex]; entityAliases[aliasId].id = aliasId; - aliasId++; } } if (targetDeviceAliases) { for (datasourceIndex in targetDeviceAliases) { + aliasId = utils.guid(); targetDeviceAliasesMap[aliasId] = datasourceIndex; entityAliases[aliasId] = targetDeviceAliases[datasourceIndex]; entityAliases[aliasId].id = aliasId; - aliasId++; } } diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js index 84c7097ed3..9c6695b96c 100644 --- a/ui/src/app/locale/locale.constant.js +++ b/ui/src/app/locale/locale.constant.js @@ -124,7 +124,7 @@ export default angular.module('thingsboard.locale', []) "create-entity-filter": "Create entity filter", "edit-entity-filter": "Edit entity filter", "entity-filter-required": "Entity filter is required.", - "resolve-multiple": "Multiple", + "resolve-multiple": "Resolve as multiple entities", "filter-type": "Filter type", "filter-type-required": "Filter type is required.", "use-state-entity": "Use state entity", diff --git a/ui/src/app/services/item-buffer.service.js b/ui/src/app/services/item-buffer.service.js index 309e53e59c..d29ad18cb0 100644 --- a/ui/src/app/services/item-buffer.service.js +++ b/ui/src/app/services/item-buffer.service.js @@ -298,11 +298,7 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) { } if (!newAliasId) { var newAliasName = createEntityAliasName(entityAliases, aliasInfo.alias); - newAliasId = 0; - for (aliasId in entityAliases) { - newAliasId = Math.max(newAliasId, aliasId); - } - newAliasId++; + newAliasId = utils.guid(); entityAliases[newAliasId] = {id: newAliasId, alias: newAliasName, filter: aliasInfo.filter}; } return newAliasId;