Showing
22 changed files
with
363 additions
and
580 deletions
... | ... | @@ -100,6 +100,7 @@ export default class AliasController { |
100 | 100 | aliasCtrl.resolvedAliasesToStateEntities[aliasId] = |
101 | 101 | aliasCtrl.stateController.getStateParams().entityId; |
102 | 102 | } |
103 | + aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId); | |
103 | 104 | deferred.resolve(aliasInfo); |
104 | 105 | }, |
105 | 106 | function fail() { |
... | ... | @@ -239,6 +240,9 @@ export default class AliasController { |
239 | 240 | datasource.name = name; |
240 | 241 | datasource.aliasName = name; |
241 | 242 | datasource.entityName = name; |
243 | + } else if (datasource.unresolvedStateEntity) { | |
244 | + datasource.name = "Unresolved"; | |
245 | + datasource.entityName = "Unresolved"; | |
242 | 246 | } |
243 | 247 | datasource.dataKeys.forEach(function(dataKey) { |
244 | 248 | if (datasource.generated) { | ... | ... |
... | ... | @@ -73,6 +73,15 @@ export default class Subscription { |
73 | 73 | |
74 | 74 | this.datasources = this.ctx.utils.validateDatasources(options.datasources); |
75 | 75 | this.datasourceListeners = []; |
76 | + | |
77 | + /* | |
78 | + * data = array of datasourceData | |
79 | + * datasourceData = { | |
80 | + * tbDatasource, | |
81 | + * dataKey, { name, config } | |
82 | + * data = array of [time, value] | |
83 | + * } | |
84 | + */ | |
76 | 85 | this.data = []; |
77 | 86 | this.hiddenData = []; |
78 | 87 | this.originalTimewindow = null; |
... | ... | @@ -543,39 +552,6 @@ export default class Subscription { |
543 | 552 | var datasource = this.datasources[i]; |
544 | 553 | if (angular.isFunction(datasource)) |
545 | 554 | continue; |
546 | - /* var entityId = null; | |
547 | - var entityType = null; | |
548 | - if (datasource.type === this.ctx.types.datasourceType.entity) { | |
549 | - var aliasName = null; | |
550 | - var entityName = null; | |
551 | - if (datasource.entityId) { | |
552 | - entityId = datasource.entityId; | |
553 | - entityType = datasource.entityType; | |
554 | - datasource.name = datasource.entityName; | |
555 | - aliasName = datasource.entityName; | |
556 | - entityName = datasource.entityName; | |
557 | - } else if (datasource.entityAliasId) { | |
558 | - if (this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId]) { | |
559 | - entityId = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].entityId; | |
560 | - entityType = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].entityType; | |
561 | - datasource.name = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].alias; | |
562 | - aliasName = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].alias; | |
563 | - entityName = ''; | |
564 | - var entitiesInfo = this.ctx.aliasesInfo.entityAliasesInfo[datasource.entityAliasId]; | |
565 | - for (var d = 0; d < entitiesInfo.length; d++) { | |
566 | - if (entitiesInfo[d].id === entityId) { | |
567 | - entityName = entitiesInfo[d].name; | |
568 | - break; | |
569 | - } | |
570 | - } | |
571 | - } | |
572 | - } | |
573 | - } else { | |
574 | - datasource.name = datasource.name || this.ctx.types.datasourceType.function; | |
575 | - } | |
576 | - for (var dk = 0; dk < datasource.dataKeys.length; dk++) { | |
577 | - updateDataKeyLabel(datasource.dataKeys[dk], datasource.name, entityName, aliasName); | |
578 | - }*/ | |
579 | 555 | |
580 | 556 | var subscription = this; |
581 | 557 | |
... | ... | @@ -606,6 +582,10 @@ export default class Subscription { |
606 | 582 | |
607 | 583 | this.datasourceListeners.push(listener); |
608 | 584 | this.ctx.datasourceService.subscribeToDatasource(listener); |
585 | + if (datasource.unresolvedStateEntity) { | |
586 | + this.notifyDataLoaded(); | |
587 | + this.onDataUpdated(); | |
588 | + } | |
609 | 589 | } |
610 | 590 | } |
611 | 591 | |
... | ... | @@ -625,19 +605,6 @@ export default class Subscription { |
625 | 605 | } else { |
626 | 606 | return false; |
627 | 607 | } |
628 | - /*var deviceId = null; | |
629 | - if (this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId]) { | |
630 | - deviceId = this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId].entityId; | |
631 | - } | |
632 | - if (!angular.equals(deviceId, this.targetDeviceId)) { | |
633 | - this.targetDeviceId = deviceId; | |
634 | - if (this.targetDeviceId) { | |
635 | - this.rpcEnabled = true; | |
636 | - } else { | |
637 | - this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false; | |
638 | - } | |
639 | - this.callbacks.rpcStateChanged(this); | |
640 | - }*/ | |
641 | 608 | } |
642 | 609 | |
643 | 610 | checkSubscriptions(aliasIds) { |
... | ... | @@ -650,29 +617,8 @@ export default class Subscription { |
650 | 617 | break; |
651 | 618 | } |
652 | 619 | } |
653 | - /*var entityId = null; | |
654 | - var entityType = null; | |
655 | - var aliasName = null; | |
656 | - if (listener.datasource.type === this.ctx.types.datasourceType.entity) { | |
657 | - if (listener.datasource.entityAliasId && | |
658 | - this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId]) { | |
659 | - entityId = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].entityId; | |
660 | - entityType = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].entityType; | |
661 | - aliasName = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].alias; | |
662 | - } | |
663 | - if (!angular.equals(entityId, listener.entityId) || | |
664 | - !angular.equals(entityType, listener.entityType) || | |
665 | - !angular.equals(aliasName, listener.datasource.name)) { | |
666 | - subscriptionsChanged = true; | |
667 | - break; | |
668 | - } | |
669 | - }*/ | |
670 | 620 | } |
671 | 621 | return subscriptionsChanged; |
672 | - /*if (subscriptionsChanged) { | |
673 | - this.unsubscribe(); | |
674 | - this.subscribe(); | |
675 | - }*/ | |
676 | 622 | } |
677 | 623 | |
678 | 624 | destroy() { |
... | ... | @@ -691,29 +637,6 @@ export default class Subscription { |
691 | 637 | |
692 | 638 | } |
693 | 639 | |
694 | -/*const varsRegex = /\$\{([^\}]*)\}/g; | |
695 | - | |
696 | -function updateDataKeyLabel(dataKey, dsName, entityName, aliasName) { | |
697 | - var pattern = dataKey.pattern; | |
698 | - var label = dataKey.pattern; | |
699 | - var match = varsRegex.exec(pattern); | |
700 | - while (match !== null) { | |
701 | - var variable = match[0]; | |
702 | - var variableName = match[1]; | |
703 | - if (variableName === 'dsName') { | |
704 | - label = label.split(variable).join(dsName); | |
705 | - } else if (variableName === 'entityName') { | |
706 | - label = label.split(variable).join(entityName); | |
707 | - } else if (variableName === 'deviceName') { | |
708 | - label = label.split(variable).join(entityName); | |
709 | - } else if (variableName === 'aliasName') { | |
710 | - label = label.split(variable).join(aliasName); | |
711 | - } | |
712 | - match = varsRegex.exec(pattern); | |
713 | - } | |
714 | - dataKey.label = label; | |
715 | -}*/ | |
716 | - | |
717 | 640 | function calculateMin(data) { |
718 | 641 | if (data.length > 0) { |
719 | 642 | var result = Number(data[0][1]); | ... | ... |
... | ... | @@ -23,8 +23,10 @@ function DashboardUtils(types, utils, timeService) { |
23 | 23 | |
24 | 24 | var service = { |
25 | 25 | validateAndUpdateDashboard: validateAndUpdateDashboard, |
26 | + validateAndUpdateWidget: validateAndUpdateWidget, | |
26 | 27 | getRootStateId: getRootStateId, |
27 | 28 | createSingleWidgetDashboard: createSingleWidgetDashboard, |
29 | + createSingleEntityFilter: createSingleEntityFilter, | |
28 | 30 | getStateLayoutsData: getStateLayoutsData, |
29 | 31 | createDefaultState: createDefaultState, |
30 | 32 | createDefaultLayoutData: createDefaultLayoutData, |
... | ... | @@ -39,7 +41,7 @@ function DashboardUtils(types, utils, timeService) { |
39 | 41 | |
40 | 42 | return service; |
41 | 43 | |
42 | - function validateAndUpdateEntityAliases(configuration) { | |
44 | + function validateAndUpdateEntityAliases(configuration, datasourcesByAliasId, targetDevicesByAliasId) { | |
43 | 45 | var aliasId, entityAlias; |
44 | 46 | if (angular.isUndefined(configuration.entityAliases)) { |
45 | 47 | configuration.entityAliases = {}; |
... | ... | @@ -47,8 +49,8 @@ function DashboardUtils(types, utils, timeService) { |
47 | 49 | var deviceAliases = configuration.deviceAliases; |
48 | 50 | for (aliasId in deviceAliases) { |
49 | 51 | var deviceAlias = deviceAliases[aliasId]; |
50 | - entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias); | |
51 | - configuration.entityAliases[aliasId] = entityAlias; | |
52 | + entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId); | |
53 | + configuration.entityAliases[entityAlias.id] = entityAlias; | |
52 | 54 | } |
53 | 55 | delete configuration.deviceAliases; |
54 | 56 | } |
... | ... | @@ -56,16 +58,43 @@ function DashboardUtils(types, utils, timeService) { |
56 | 58 | var entityAliases = configuration.entityAliases; |
57 | 59 | for (aliasId in entityAliases) { |
58 | 60 | entityAlias = entityAliases[aliasId]; |
59 | - entityAliases[aliasId] = validateAndUpdateEntityAlias(entityAlias); | |
60 | - if (!entityAliases[aliasId].id) { | |
61 | - entityAliases[aliasId].id = aliasId; | |
61 | + entityAlias = validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId); | |
62 | + if (aliasId != entityAlias.id) { | |
63 | + delete entityAliases[aliasId]; | |
62 | 64 | } |
65 | + entityAliases[entityAlias.id] = entityAlias; | |
63 | 66 | } |
64 | 67 | } |
65 | 68 | return configuration; |
66 | 69 | } |
67 | 70 | |
68 | - function validateAndUpdateDeviceAlias(aliasId, deviceAlias) { | |
71 | + function validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId) { | |
72 | + if (!aliasId || !angular.isString(aliasId) || aliasId.length != 36) { | |
73 | + var newAliasId = utils.guid(); | |
74 | + var aliasDatasources = datasourcesByAliasId[aliasId]; | |
75 | + if (aliasDatasources) { | |
76 | + aliasDatasources.forEach( | |
77 | + function(datasource) { | |
78 | + datasource.entityAliasId = newAliasId; | |
79 | + } | |
80 | + ); | |
81 | + } | |
82 | + var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId]; | |
83 | + if (targetDeviceAliasIdsList) { | |
84 | + targetDeviceAliasIdsList.forEach( | |
85 | + function(targetDeviceAliasIds) { | |
86 | + targetDeviceAliasIds[0] = newAliasId; | |
87 | + } | |
88 | + ); | |
89 | + } | |
90 | + return newAliasId; | |
91 | + } else { | |
92 | + return aliasId; | |
93 | + } | |
94 | + } | |
95 | + | |
96 | + function validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId) { | |
97 | + aliasId = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId); | |
69 | 98 | var alias = deviceAlias.alias; |
70 | 99 | var entityAlias = { |
71 | 100 | id: aliasId, |
... | ... | @@ -93,7 +122,8 @@ function DashboardUtils(types, utils, timeService) { |
93 | 122 | return entityAlias; |
94 | 123 | } |
95 | 124 | |
96 | - function validateAndUpdateEntityAlias(entityAlias) { | |
125 | + function validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId) { | |
126 | + entityAlias.id = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId); | |
97 | 127 | if (!entityAlias.filter) { |
98 | 128 | entityAlias.filter = { |
99 | 129 | type: entityAlias.entityFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value, |
... | ... | @@ -206,12 +236,33 @@ function DashboardUtils(types, utils, timeService) { |
206 | 236 | } |
207 | 237 | } |
208 | 238 | |
209 | - /* var datasources = {}; | |
239 | + var datasourcesByAliasId = {}; | |
240 | + var targetDevicesByAliasId = {}; | |
210 | 241 | for (var widgetId in dashboard.configuration.widgets) { |
242 | + widget = dashboard.configuration.widgets[widgetId]; | |
243 | + widget.config.datasources.forEach(function (datasource) { | |
244 | + if (datasource.entityAliasId) { | |
245 | + var aliasId = datasource.entityAliasId; | |
246 | + var aliasDatasources = datasourcesByAliasId[aliasId]; | |
247 | + if (!aliasDatasources) { | |
248 | + aliasDatasources = []; | |
249 | + datasourcesByAliasId[aliasId] = aliasDatasources; | |
250 | + } | |
251 | + aliasDatasources.push(datasource); | |
252 | + } | |
253 | + }); | |
254 | + if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length) { | |
255 | + var aliasId = widget.config.targetDeviceAliasIds[0]; | |
256 | + var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId]; | |
257 | + if (!targetDeviceAliasIdsList) { | |
258 | + targetDeviceAliasIdsList = []; | |
259 | + targetDevicesByAliasId[aliasId] = targetDeviceAliasIdsList; | |
260 | + } | |
261 | + targetDeviceAliasIdsList.push(widget.config.targetDeviceAliasIds); | |
262 | + } | |
263 | + } | |
211 | 264 | |
212 | - }*/ | |
213 | - | |
214 | - dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration); | |
265 | + dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration, datasourcesByAliasId, targetDevicesByAliasId); | |
215 | 266 | |
216 | 267 | if (angular.isUndefined(dashboard.configuration.timewindow)) { |
217 | 268 | dashboard.configuration.timewindow = timeService.defaultTimewindow(); |
... | ... | @@ -288,6 +339,16 @@ function DashboardUtils(types, utils, timeService) { |
288 | 339 | return dashboard; |
289 | 340 | } |
290 | 341 | |
342 | + function createSingleEntityFilter(entityType, entityId) { | |
343 | + return { | |
344 | + type: types.aliasFilterType.entityList.value, | |
345 | + stateEntity: false, | |
346 | + entityList: [entityId], | |
347 | + entityType: entityType, | |
348 | + resolveMultiple: false | |
349 | + }; | |
350 | + } | |
351 | + | |
291 | 352 | function getStateLayoutsData(dashboard, targetState) { |
292 | 353 | var dashboardConfiguration = dashboard.configuration; |
293 | 354 | var states = dashboardConfiguration.states; | ... | ... |
... | ... | @@ -106,6 +106,7 @@ function Utils($mdColorPalette, $rootScope, $window, types) { |
106 | 106 | isDescriptorSchemaNotEmpty: isDescriptorSchemaNotEmpty, |
107 | 107 | filterSearchTextEntities: filterSearchTextEntities, |
108 | 108 | guid: guid, |
109 | + cleanCopy: cleanCopy, | |
109 | 110 | isLocalUrl: isLocalUrl, |
110 | 111 | validateDatasources: validateDatasources, |
111 | 112 | createKey: createKey, |
... | ... | @@ -291,6 +292,16 @@ function Utils($mdColorPalette, $rootScope, $window, types) { |
291 | 292 | s4() + '-' + s4() + s4() + s4(); |
292 | 293 | } |
293 | 294 | |
295 | + function cleanCopy(object) { | |
296 | + var copy = angular.copy(object); | |
297 | + for (var prop in copy) { | |
298 | + if (prop && prop.startsWith('$$')) { | |
299 | + delete copy[prop]; | |
300 | + } | |
301 | + } | |
302 | + return copy; | |
303 | + } | |
304 | + | |
294 | 305 | function genNextColor(datasources) { |
295 | 306 | var index = 0; |
296 | 307 | if (datasources) { | ... | ... |
... | ... | @@ -85,7 +85,7 @@ function Dashboard() { |
85 | 85 | } |
86 | 86 | |
87 | 87 | /*@ngInject*/ |
88 | -function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, timeService, types, utils) { | |
88 | +function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $mdUtil, timeService, types, utils) { | |
89 | 89 | |
90 | 90 | var highlightedMode = false; |
91 | 91 | var highlightedWidget = null; |
... | ... | @@ -792,7 +792,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t |
792 | 792 | } |
793 | 793 | |
794 | 794 | function dashboardLoaded() { |
795 | - $timeout(function () { | |
795 | + $mdUtil.nextTick(function () { | |
796 | 796 | if (vm.dashboardTimewindowWatch) { |
797 | 797 | vm.dashboardTimewindowWatch(); |
798 | 798 | vm.dashboardTimewindowWatch = null; |
... | ... | @@ -802,14 +802,27 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t |
802 | 802 | }, true); |
803 | 803 | adoptMaxRows(); |
804 | 804 | vm.dashboardLoading = false; |
805 | - $timeout(function () { | |
806 | - var gridsterScope = gridsterElement.scope(); | |
807 | - vm.gridster = gridsterScope.gridster; | |
808 | - if (vm.onInit) { | |
809 | - vm.onInit({dashboard: vm}); | |
805 | + if ($scope.gridsterScopeWatcher) { | |
806 | + $scope.gridsterScopeWatcher(); | |
807 | + } | |
808 | + $scope.gridsterScopeWatcher = $scope.$watch( | |
809 | + function() { | |
810 | + var hasScope = gridsterElement.scope() ? true : false; | |
811 | + return hasScope; | |
812 | + }, | |
813 | + function(hasScope) { | |
814 | + if (hasScope) { | |
815 | + $scope.gridsterScopeWatcher(); | |
816 | + $scope.gridsterScopeWatcher = null; | |
817 | + var gridsterScope = gridsterElement.scope(); | |
818 | + vm.gridster = gridsterScope.gridster; | |
819 | + if (vm.onInit) { | |
820 | + vm.onInit({dashboard: vm}); | |
821 | + } | |
822 | + } | |
810 | 823 | } |
811 | - }, 0, false); | |
812 | - }, 0, false); | |
824 | + ); | |
825 | + }); | |
813 | 826 | } |
814 | 827 | |
815 | 828 | function loading() { | ... | ... |
... | ... | @@ -20,9 +20,9 @@ import Subscription from '../api/subscription'; |
20 | 20 | /* eslint-disable angular/angularelement */ |
21 | 21 | |
22 | 22 | /*@ngInject*/ |
23 | -export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, tbRaf, types, utils, timeService, | |
23 | +export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService, | |
24 | 24 | datasourceService, entityService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow, |
25 | - dashboardTimewindowApi, widget, aliasController, stateController, widgetType) { | |
25 | + dashboardTimewindowApi, widget, aliasController, stateController, widgetInfo, widgetType) { | |
26 | 26 | |
27 | 27 | var vm = this; |
28 | 28 | |
... | ... | @@ -42,20 +42,11 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
42 | 42 | |
43 | 43 | var cafs = {}; |
44 | 44 | |
45 | - /* | |
46 | - * data = array of datasourceData | |
47 | - * datasourceData = { | |
48 | - * tbDatasource, | |
49 | - * dataKey, { name, config } | |
50 | - * data = array of [time, value] | |
51 | - * } | |
52 | - */ | |
53 | - | |
54 | 45 | var widgetContext = { |
55 | 46 | inited: false, |
56 | 47 | $scope: $scope, |
57 | - $container: $('#container', $element), | |
58 | - $containerParent: $($element), | |
48 | + $container: null, | |
49 | + $containerParent: null, | |
59 | 50 | width: 0, |
60 | 51 | height: 0, |
61 | 52 | isEdit: isEdit, |
... | ... | @@ -82,30 +73,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
82 | 73 | createSubscription: function(options, subscribe) { |
83 | 74 | return createSubscription(options, subscribe); |
84 | 75 | }, |
85 | - | |
86 | - | |
87 | - // type: "timeseries" or "latest" or "rpc" | |
88 | - /* subscriptionInfo = [ | |
89 | - { | |
90 | - entityType: "" | |
91 | - entityId: "" | |
92 | - entityName: "" | |
93 | - timeseries: [{ name: "", label: "" }, ..] | |
94 | - attributes: [{ name: "", label: "" }, ..] | |
95 | - } | |
96 | - .. | |
97 | - ]*/ | |
98 | - | |
99 | - // options = { | |
100 | - // timeWindowConfig, | |
101 | - // useDashboardTimewindow, | |
102 | - // legendConfig, | |
103 | - // decimals, | |
104 | - // units, | |
105 | - // callbacks [ onDataUpdated(subscription, apply) ] | |
106 | - // } | |
107 | - // | |
108 | - | |
109 | 76 | createSubscriptionFromInfo: function (type, subscriptionsInfo, options, useDefaultComponents, subscribe) { |
110 | 77 | return createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe); |
111 | 78 | }, |
... | ... | @@ -211,21 +178,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
211 | 178 | } |
212 | 179 | ); |
213 | 180 | |
214 | - /* | |
215 | - options = { | |
216 | - type, | |
217 | - targetDeviceAliasIds, // RPC | |
218 | - targetDeviceIds, // RPC | |
219 | - datasources, | |
220 | - timeWindowConfig, | |
221 | - useDashboardTimewindow, | |
222 | - legendConfig, | |
223 | - decimals, | |
224 | - units, | |
225 | - callbacks | |
226 | - } | |
227 | - */ | |
228 | - | |
229 | 181 | function createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe) { |
230 | 182 | var deferred = $q.defer(); |
231 | 183 | options.type = type; |
... | ... | @@ -400,6 +352,101 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
400 | 352 | return deferred.promise; |
401 | 353 | } |
402 | 354 | |
355 | + function configureWidgetElement() { | |
356 | + | |
357 | + $scope.displayLegend = angular.isDefined(widget.config.showLegend) ? | |
358 | + widget.config.showLegend : widget.type === types.widgetType.timeseries.value; | |
359 | + | |
360 | + if ($scope.displayLegend) { | |
361 | + $scope.legendConfig = widget.config.legendConfig || | |
362 | + { | |
363 | + position: types.position.bottom.value, | |
364 | + showMin: false, | |
365 | + showMax: false, | |
366 | + showAvg: widget.type === types.widgetType.timeseries.value, | |
367 | + showTotal: false | |
368 | + }; | |
369 | + $scope.legendData = { | |
370 | + keys: [], | |
371 | + data: [] | |
372 | + }; | |
373 | + } | |
374 | + | |
375 | + var html = '<div class="tb-absolute-fill tb-widget-error" ng-if="widgetErrorData">' + | |
376 | + '<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' + | |
377 | + '</div>' + | |
378 | + '<div class="tb-absolute-fill tb-widget-loading" ng-show="loadingData" layout="column" layout-align="center center">' + | |
379 | + '<md-progress-circular md-mode="indeterminate" ng-disabled="!loadingData" class="md-accent" md-diameter="40"></md-progress-circular>' + | |
380 | + '</div>'; | |
381 | + | |
382 | + var containerHtml = '<div id="container">' + widgetInfo.templateHtml + '</div>'; | |
383 | + if ($scope.displayLegend) { | |
384 | + var layoutType; | |
385 | + if ($scope.legendConfig.position === types.position.top.value || | |
386 | + $scope.legendConfig.position === types.position.bottom.value) { | |
387 | + layoutType = 'column'; | |
388 | + } else { | |
389 | + layoutType = 'row'; | |
390 | + } | |
391 | + | |
392 | + var legendStyle; | |
393 | + switch($scope.legendConfig.position) { | |
394 | + case types.position.top.value: | |
395 | + legendStyle = 'padding-bottom: 8px;'; | |
396 | + break; | |
397 | + case types.position.bottom.value: | |
398 | + legendStyle = 'padding-top: 8px;'; | |
399 | + break; | |
400 | + case types.position.left.value: | |
401 | + legendStyle = 'padding-right: 0px;'; | |
402 | + break; | |
403 | + case types.position.right.value: | |
404 | + legendStyle = 'padding-left: 0px;'; | |
405 | + break; | |
406 | + } | |
407 | + | |
408 | + var legendHtml = '<tb-legend style="'+legendStyle+'" legend-config="legendConfig" legend-data="legendData"></tb-legend>'; | |
409 | + containerHtml = '<div flex id="widget-container">' + containerHtml + '</div>'; | |
410 | + html += '<div class="tb-absolute-fill" layout="'+layoutType+'">'; | |
411 | + if ($scope.legendConfig.position === types.position.top.value || | |
412 | + $scope.legendConfig.position === types.position.left.value) { | |
413 | + html += legendHtml; | |
414 | + html += containerHtml; | |
415 | + } else { | |
416 | + html += containerHtml; | |
417 | + html += legendHtml; | |
418 | + } | |
419 | + html += '</div>'; | |
420 | + } else { | |
421 | + html += containerHtml; | |
422 | + } | |
423 | + | |
424 | + //TODO: | |
425 | + /*if (progressElement) { | |
426 | + progressScope.$destroy(); | |
427 | + progressScope = null; | |
428 | + | |
429 | + progressElement.remove(); | |
430 | + progressElement = null; | |
431 | + }*/ | |
432 | + | |
433 | + $element.html(html); | |
434 | + | |
435 | + var containerElement = $scope.displayLegend ? angular.element($element[0].querySelector('#widget-container')) : $element; | |
436 | + widgetContext.$container = $('#container', containerElement); | |
437 | + widgetContext.$containerParent = $(containerElement); | |
438 | + | |
439 | + $compile($element.contents())($scope); | |
440 | + | |
441 | + addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef | |
442 | + } | |
443 | + | |
444 | + function destroyWidgetElement() { | |
445 | + removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef | |
446 | + $element.html(''); | |
447 | + widgetContext.$container = null; | |
448 | + widgetContext.$containerParent = null; | |
449 | + } | |
403 | 450 | |
404 | 451 | function initialize() { |
405 | 452 | |
... | ... | @@ -407,8 +454,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
407 | 454 | onEditModeChanged(isEdit); |
408 | 455 | }); |
409 | 456 | |
410 | - addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef | |
411 | - | |
412 | 457 | $scope.$watch(function () { |
413 | 458 | return widget.row + ',' + widget.col + ',' + widget.config.mobileOrder; |
414 | 459 | }, function () { |
... | ... | @@ -439,10 +484,10 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
439 | 484 | }); |
440 | 485 | |
441 | 486 | $scope.$on("$destroy", function () { |
442 | - removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef | |
443 | 487 | onDestroy(); |
444 | 488 | }); |
445 | 489 | |
490 | + configureWidgetElement(); | |
446 | 491 | var deferred = $q.defer(); |
447 | 492 | if (!vm.useCustomDatasources) { |
448 | 493 | createDefaultSubscription().then( |
... | ... | @@ -465,6 +510,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
465 | 510 | |
466 | 511 | function reInit() { |
467 | 512 | onDestroy(); |
513 | + configureWidgetElement(); | |
468 | 514 | if (!vm.useCustomDatasources) { |
469 | 515 | createDefaultSubscription().then( |
470 | 516 | function success() { |
... | ... | @@ -636,6 +682,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
636 | 682 | handleWidgetException(e); |
637 | 683 | } |
638 | 684 | } |
685 | + destroyWidgetElement(); | |
639 | 686 | } |
640 | 687 | |
641 | 688 | //TODO: widgets visibility | ... | ... |
... | ... | @@ -28,7 +28,7 @@ export default angular.module('thingsboard.directives.widget', [thingsboardLegen |
28 | 28 | .name; |
29 | 29 | |
30 | 30 | /*@ngInject*/ |
31 | -function Widget($controller, $compile, types, widgetService) { | |
31 | +function Widget($controller, widgetService) { | |
32 | 32 | return { |
33 | 33 | scope: true, |
34 | 34 | link: function (scope, elem, attrs) { |
... | ... | @@ -81,90 +81,9 @@ function Widget($controller, $compile, types, widgetService) { |
81 | 81 | |
82 | 82 | elem.addClass(widgetNamespace); |
83 | 83 | |
84 | - var html = '<div class="tb-absolute-fill tb-widget-error" ng-if="widgetErrorData">' + | |
85 | - '<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' + | |
86 | - '</div>' + | |
87 | - '<div class="tb-absolute-fill tb-widget-loading" ng-show="loadingData" layout="column" layout-align="center center">' + | |
88 | - '<md-progress-circular md-mode="indeterminate" ng-disabled="!loadingData" class="md-accent" md-diameter="40"></md-progress-circular>' + | |
89 | - '</div>'; | |
90 | - | |
91 | - scope.displayLegend = angular.isDefined(widget.config.showLegend) ? | |
92 | - widget.config.showLegend : widget.type === types.widgetType.timeseries.value; | |
93 | - | |
94 | - | |
95 | - var containerHtml = '<div id="container">' + widgetInfo.templateHtml + '</div>'; | |
96 | - if (scope.displayLegend) { | |
97 | - scope.legendConfig = widget.config.legendConfig || | |
98 | - { | |
99 | - position: types.position.bottom.value, | |
100 | - showMin: false, | |
101 | - showMax: false, | |
102 | - showAvg: widget.type === types.widgetType.timeseries.value, | |
103 | - showTotal: false | |
104 | - }; | |
105 | - scope.legendData = { | |
106 | - keys: [], | |
107 | - data: [] | |
108 | - }; | |
109 | - | |
110 | - var layoutType; | |
111 | - if (scope.legendConfig.position === types.position.top.value || | |
112 | - scope.legendConfig.position === types.position.bottom.value) { | |
113 | - layoutType = 'column'; | |
114 | - } else { | |
115 | - layoutType = 'row'; | |
116 | - } | |
117 | - | |
118 | - var legendStyle; | |
119 | - switch(scope.legendConfig.position) { | |
120 | - case types.position.top.value: | |
121 | - legendStyle = 'padding-bottom: 8px;'; | |
122 | - break; | |
123 | - case types.position.bottom.value: | |
124 | - legendStyle = 'padding-top: 8px;'; | |
125 | - break; | |
126 | - case types.position.left.value: | |
127 | - legendStyle = 'padding-right: 0px;'; | |
128 | - break; | |
129 | - case types.position.right.value: | |
130 | - legendStyle = 'padding-left: 0px;'; | |
131 | - break; | |
132 | - } | |
133 | - | |
134 | - var legendHtml = '<tb-legend style="'+legendStyle+'" legend-config="legendConfig" legend-data="legendData"></tb-legend>'; | |
135 | - containerHtml = '<div flex id="widget-container">' + containerHtml + '</div>'; | |
136 | - html += '<div class="tb-absolute-fill" layout="'+layoutType+'">'; | |
137 | - if (scope.legendConfig.position === types.position.top.value || | |
138 | - scope.legendConfig.position === types.position.left.value) { | |
139 | - html += legendHtml; | |
140 | - html += containerHtml; | |
141 | - } else { | |
142 | - html += containerHtml; | |
143 | - html += legendHtml; | |
144 | - } | |
145 | - html += '</div>'; | |
146 | - } else { | |
147 | - html += containerHtml; | |
148 | - } | |
149 | - | |
150 | - //TODO: | |
151 | - /*if (progressElement) { | |
152 | - progressScope.$destroy(); | |
153 | - progressScope = null; | |
154 | - | |
155 | - progressElement.remove(); | |
156 | - progressElement = null; | |
157 | - }*/ | |
158 | - | |
159 | - elem.html(html); | |
160 | - | |
161 | - var containerElement = scope.displayLegend ? angular.element(elem[0].querySelector('#widget-container')) : elem; | |
162 | - | |
163 | - $compile(elem.contents())(scope); | |
164 | - | |
165 | 84 | var widgetType = widgetService.getWidgetTypeFunction(widget.bundleAlias, widget.typeAlias, widget.isSystemType); |
166 | 85 | |
167 | - angular.extend(locals, {$scope: scope, $element: containerElement, widgetType: widgetType}); | |
86 | + angular.extend(locals, {$scope: scope, $element: elem, widgetInfo: widgetInfo, widgetType: widgetType}); | |
168 | 87 | |
169 | 88 | widgetController = $controller('WidgetController', locals); |
170 | 89 | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | */ |
16 | 16 | |
17 | 17 | /*@ngInject*/ |
18 | -export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, types, aliasController, onEntityAliasesUpdate) { | |
18 | +export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, $filter, types, aliasController, onEntityAliasesUpdate) { | |
19 | 19 | |
20 | 20 | var vm = this; |
21 | 21 | vm._mdPanelRef = mdPanelRef; |
... | ... | @@ -31,13 +31,18 @@ export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, t |
31 | 31 | var aliasInfo = vm.aliasController.getInstantAliasInfo(aliasId); |
32 | 32 | if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity) { |
33 | 33 | vm.entityAliasesInfo[aliasId] = angular.copy(aliasInfo); |
34 | + vm.entityAliasesInfo[aliasId].selectedId = aliasInfo.currentEntity.id; | |
34 | 35 | } |
35 | 36 | } |
36 | 37 | |
37 | - function currentAliasEntityChanged(aliasId, currentEntity) { | |
38 | - vm.aliasController.updateCurrentAliasEntity(aliasId, currentEntity); | |
39 | - if (onEntityAliasesUpdate) { | |
40 | - onEntityAliasesUpdate(); | |
38 | + function currentAliasEntityChanged(aliasId, selectedId) { | |
39 | + var resolvedEntities = vm.entityAliasesInfo[aliasId].resolvedEntities; | |
40 | + var selected = $filter('filter')(resolvedEntities, {id: selectedId}); | |
41 | + if (selected && selected.length) { | |
42 | + vm.aliasController.updateCurrentAliasEntity(aliasId, selected[0]); | |
43 | + if (onEntityAliasesUpdate) { | |
44 | + onEntityAliasesUpdate(); | |
45 | + } | |
41 | 46 | } |
42 | 47 | } |
43 | 48 | ... | ... |
... | ... | @@ -21,8 +21,8 @@ |
21 | 21 | <div flex layout="row" ng-repeat="(aliasId, entityAliasInfo) in vm.entityAliasesInfo"> |
22 | 22 | <md-input-container flex> |
23 | 23 | <label>{{entityAliasInfo.alias}}</label> |
24 | - <md-select ng-model="entityAliasInfo.currentEntity" ng-change="vm.currentAliasEntityChanged(aliasId, entityAliasInfo.currentEntity)"> | |
25 | - <md-option ng-repeat="resolvedEntity in entityAliasInfo.resolvedEntities" ng-value="resolvedEntity"> | |
24 | + <md-select ng-model="entityAliasInfo.selectedId" ng-change="vm.currentAliasEntityChanged(aliasId, entityAliasInfo.selectedId)"> | |
25 | + <md-option ng-repeat="resolvedEntity in entityAliasInfo.resolvedEntities" ng-value="resolvedEntity.id"> | |
26 | 26 | {{resolvedEntity.name}} |
27 | 27 | </md-option> |
28 | 28 | </md-select> | ... | ... |
... | ... | @@ -94,6 +94,14 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $ |
94 | 94 | $mdPanel.open(config); |
95 | 95 | } |
96 | 96 | |
97 | + scope.$on('entityAliasesChanged', function() { | |
98 | + scope.updateView(); | |
99 | + }); | |
100 | + | |
101 | + scope.$on('entityAliasResolved', function() { | |
102 | + scope.updateView(); | |
103 | + }); | |
104 | + | |
97 | 105 | scope.updateView = function () { |
98 | 106 | updateDisplayValue(); |
99 | 107 | } | ... | ... |
... | ... | @@ -22,7 +22,7 @@ import selectTargetLayoutTemplate from '../../dashboard/layouts/select-target-la |
22 | 22 | /* eslint-enable import/no-unresolved, import/default */ |
23 | 23 | |
24 | 24 | /*@ngInject*/ |
25 | -export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document, | |
25 | +export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document, dashboardUtils, | |
26 | 26 | types, itembuffer, dashboardService, entityId, entityType, entityName, widget) { |
27 | 27 | |
28 | 28 | var vm = this; |
... | ... | @@ -126,15 +126,8 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog, |
126 | 126 | targetDeviceAliases: {} |
127 | 127 | }; |
128 | 128 | aliasesInfo.datasourceAliases[0] = { |
129 | - id: 1, | |
130 | 129 | alias: entityName, |
131 | - filter: { | |
132 | - type: types.aliasFilterType.entityList.value, | |
133 | - stateEntity: false, | |
134 | - entityList: [entityId], | |
135 | - entityType: entityType, | |
136 | - resolveMultiple: false | |
137 | - } | |
130 | + filter: dashboardUtils.createSingleEntityFilter(entityType, entityId) | |
138 | 131 | }; |
139 | 132 | itembuffer.addWidgetToDashboard(theDashboard, targetState, targetLayout, vm.widget, aliasesInfo, null, 48, null, -1, -1).then( |
140 | 133 | function(theDashboard) { | ... | ... |
... | ... | @@ -26,10 +26,12 @@ import editAttributeValueTemplate from './edit-attribute-value.tpl.html'; |
26 | 26 | /* eslint-enable import/no-unresolved, import/default */ |
27 | 27 | |
28 | 28 | import EditAttributeValueController from './edit-attribute-value.controller'; |
29 | +import AliasController from '../../api/alias-controller'; | |
29 | 30 | |
30 | 31 | /*@ngInject*/ |
31 | 32 | export default function AttributeTableDirective($compile, $templateCache, $rootScope, $q, $mdEditDialog, $mdDialog, |
32 | - $document, $translate, $filter, utils, types, dashboardService, attributeService, widgetService) { | |
33 | + $mdUtil, $document, $translate, $filter, utils, types, dashboardUtils, | |
34 | + dashboardService, entityService, attributeService, widgetService) { | |
33 | 35 | |
34 | 36 | var linker = function (scope, element, attrs) { |
35 | 37 | |
... | ... | @@ -246,15 +248,19 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS |
246 | 248 | } |
247 | 249 | |
248 | 250 | scope.nextWidget = function() { |
249 | - if (scope.widgetsCarousel.index < scope.widgetsList.length-1) { | |
250 | - scope.widgetsCarousel.index++; | |
251 | - } | |
251 | + $mdUtil.nextTick(function () { | |
252 | + if (scope.widgetsCarousel.index < scope.widgetsList.length - 1) { | |
253 | + scope.widgetsCarousel.index++; | |
254 | + } | |
255 | + }); | |
252 | 256 | } |
253 | 257 | |
254 | 258 | scope.prevWidget = function() { |
255 | - if (scope.widgetsCarousel.index > 0) { | |
256 | - scope.widgetsCarousel.index--; | |
257 | - } | |
259 | + $mdUtil.nextTick(function () { | |
260 | + if (scope.widgetsCarousel.index > 0) { | |
261 | + scope.widgetsCarousel.index--; | |
262 | + } | |
263 | + }); | |
258 | 264 | } |
259 | 265 | |
260 | 266 | scope.enterWidgetMode = function() { |
... | ... | @@ -281,23 +287,28 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS |
281 | 287 | scope.firstBundle = true; |
282 | 288 | scope.selectedWidgetsBundleAlias = types.systemBundleAlias.cards; |
283 | 289 | |
284 | - scope.aliasesInfo = { | |
285 | - entityAliases: { | |
286 | - '1': {alias: scope.entityName, entityType: scope.entityType, entityId: scope.entityId} | |
287 | - }, | |
288 | - entityAliasesInfo: { | |
289 | - '1': [ | |
290 | - {name: scope.entityName, entityType: scope.entityType, id: scope.entityId} | |
291 | - ] | |
290 | + var entityAlias = { | |
291 | + id: utils.guid(), | |
292 | + alias: scope.entityName, | |
293 | + filter: dashboardUtils.createSingleEntityFilter(scope.entityType, scope.entityId) | |
294 | + }; | |
295 | + var entitiAliases = {}; | |
296 | + entitiAliases[entityAlias.id] = entityAlias; | |
297 | + | |
298 | + var stateController = { | |
299 | + getStateParams: function() { | |
300 | + return {}; | |
292 | 301 | } |
293 | 302 | }; |
303 | + scope.aliasController = new AliasController(scope, $q, $filter, utils, | |
304 | + types, entityService, stateController, entitiAliases); | |
294 | 305 | |
295 | 306 | var dataKeyType = scope.attributeScope === types.latestTelemetry ? |
296 | 307 | types.dataKeyType.timeseries : types.dataKeyType.attribute; |
297 | 308 | |
298 | 309 | var datasource = { |
299 | 310 | type: types.datasourceType.entity, |
300 | - entityAliasId: '1', | |
311 | + entityAliasId: entityAlias.id, | |
301 | 312 | dataKeys: [] |
302 | 313 | } |
303 | 314 | var i = 0; | ... | ... |
... | ... | @@ -156,7 +156,7 @@ |
156 | 156 | rn-swipe-disabled="true"> |
157 | 157 | <li ng-repeat="widgets in widgetsList"> |
158 | 158 | <tb-dashboard |
159 | - aliases-info="aliasesInfo" | |
159 | + alias-controller="aliasController" | |
160 | 160 | widgets="widgets" |
161 | 161 | get-st-diff="getServerTimeDiff()" |
162 | 162 | columns="20" | ... | ... |
... | ... | @@ -113,12 +113,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc |
113 | 113 | } |
114 | 114 | |
115 | 115 | function addAlias() { |
116 | - var aliasId = 0; | |
117 | - for (var a in vm.entityAliases) { | |
118 | - aliasId = Math.max(vm.entityAliases[a].id, aliasId); | |
119 | - } | |
120 | - aliasId++; | |
121 | - var entityAlias = {id: aliasId, alias: '', filter: {}, changed: false}; | |
116 | + var entityAlias = {id: utils.guid(), alias: '', filter: {}, changed: false}; | |
122 | 117 | vm.entityAliases.push(entityAlias); |
123 | 118 | } |
124 | 119 | |
... | ... | @@ -162,23 +157,21 @@ export default function EntityAliasesController(utils, entityService, toast, $sc |
162 | 157 | var uniqueAliasList = {}; |
163 | 158 | |
164 | 159 | var valid = true; |
165 | - var aliasId, maxAliasId; | |
160 | + var aliasId; | |
166 | 161 | var alias; |
167 | 162 | var i; |
168 | 163 | |
169 | 164 | if (vm.isSingleEntityAlias) { |
170 | - maxAliasId = 0; | |
165 | + if (!vm.singleEntityAlias.id) { | |
166 | + vm.singleEntityAlias.id = utils.guid(); | |
167 | + } | |
171 | 168 | for (i = 0; i < vm.entityAliases.length; i ++) { |
172 | - aliasId = vm.entityAliases[i].id; | |
173 | 169 | alias = vm.entityAliases[i].alias; |
174 | 170 | if (alias === vm.singleEntityAlias.alias) { |
175 | 171 | valid = false; |
176 | 172 | break; |
177 | 173 | } |
178 | - maxAliasId = Math.max(aliasId, maxAliasId); | |
179 | 174 | } |
180 | - maxAliasId++; | |
181 | - vm.singleEntityAlias.id = maxAliasId; | |
182 | 175 | } else { |
183 | 176 | for (i = 0; i < vm.entityAliases.length; i++) { |
184 | 177 | aliasId = vm.entityAliases[i].id; | ... | ... |
... | ... | @@ -18,11 +18,20 @@ |
18 | 18 | .md-dialog-content { |
19 | 19 | padding-bottom: 0px; |
20 | 20 | } |
21 | + .tb-aliases-header { | |
22 | + min-height: 40px; | |
23 | + padding: 0 34px 0 34px; | |
24 | + margin: 5px; | |
25 | + .tb-header-label { | |
26 | + font-size: 14px; | |
27 | + color: rgba(0, 0, 0, 0.570588); | |
28 | + } | |
29 | + } | |
21 | 30 | .tb-alias { |
22 | - padding: 10px 0 0 10px; | |
31 | + padding: 0 0 0 10px; | |
23 | 32 | margin: 5px; |
24 | - md-select.tb-entity-type-select { | |
25 | - padding-bottom: 24px; | |
33 | + md-input-container { | |
34 | + margin: 0px; | |
26 | 35 | } |
27 | 36 | } |
28 | 37 | } | ... | ... |
... | ... | @@ -28,23 +28,28 @@ |
28 | 28 | </md-toolbar> |
29 | 29 | <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> |
30 | 30 | <span style="min-height: 5px;" flex="" ng-show="!loading"></span> |
31 | + <div class="tb-aliases-header" ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center"> | |
32 | + <span flex="5"></span> | |
33 | + <div flex layout="row" layout-align="start center"> | |
34 | + <span class="tb-header-label" translate flex="20" style="min-width: 150px;">entity.alias</span> | |
35 | + <div flex="80" layout="row" layout-align="start center" style="min-width: 240px; padding-left: 10px;"> | |
36 | + <span class="tb-header-label" translate flex="70">alias.entity-filter</span> | |
37 | + <span class="tb-header-label" translate flex="30" style="padding-left: 10px;" >alias.resolve-multiple</span> | |
38 | + </div> | |
39 | + <span style="min-width: 40px; margin: 0 6px;"></span> | |
40 | + </div> | |
41 | + </div> | |
42 | + <md-divider ng-show="!vm.isSingleEntityAlias"></md-divider> | |
31 | 43 | <md-dialog-content> |
32 | 44 | <div class="md-dialog-content"> |
33 | 45 | <fieldset ng-disabled="loading"> |
34 | 46 | <div ng-show="vm.isSingleEntityAlias" layout="row"> |
35 | - <tb-entity-filter flex allowed-entity-types="vm.allowedEntityTypes" ng-model="vm.singleEntityAlias.filter"> | |
47 | + <tb-entity-filter flex | |
48 | + allowed-entity-types="vm.allowedEntityTypes" | |
49 | + ng-model="vm.singleEntityAlias.filter"> | |
36 | 50 | </tb-entity-filter> |
37 | 51 | </div> |
38 | - <div ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center"> | |
39 | - <span flex="5"></span> | |
40 | - <div flex layout="row" layout-align="start center" | |
41 | - style="padding: 0 0 0 10px; margin: 5px;"> | |
42 | - <span translate flex="20" style="min-width: 150px;">entity.alias</span> | |
43 | - <span translate flex="80" style="min-width: 240px; padding-left: 10px;">alias.entity-filter</span> | |
44 | - <span style="min-width: 40px;"></span> | |
45 | - </div> | |
46 | - </div> | |
47 | - <div ng-show="!vm.isSingleEntityAlias" style="max-height: 500px; overflow: auto; padding-bottom: 20px;"> | |
52 | + <div ng-show="!vm.isSingleEntityAlias" style="height: 100%; overflow: auto; padding-bottom: 20px;"> | |
48 | 53 | <div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index"> |
49 | 54 | <span flex="5">{{$index + 1}}.</span> |
50 | 55 | <div class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center"> |
... | ... | @@ -54,13 +59,12 @@ |
54 | 59 | <div translate ng-message="required">entity.alias-required</div> |
55 | 60 | </div> |
56 | 61 | </md-input-container> |
57 | - <section flex="80" layout="column"> | |
58 | - <tb-entity-filter style="padding-left: 10px;" | |
59 | - allowed-entity-types="vm.allowedEntityTypes" | |
60 | - ng-model="entityAlias.filter" | |
61 | - on-matching-entity-change="vm.onFilterEntityChanged(entity, stateEntity, entityAlias)"> | |
62 | - </tb-entity-filter> | |
63 | - </section> | |
62 | + <tb-entity-filter flex="80" style="min-width: 240px; padding-left: 10px;" | |
63 | + hide-labels | |
64 | + allowed-entity-types="vm.allowedEntityTypes" | |
65 | + ng-model="entityAlias.filter" | |
66 | + on-matching-entity-change="vm.onFilterEntityChanged(entity, stateEntity, entityAlias)"> | |
67 | + </tb-entity-filter> | |
64 | 68 | <md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;" |
65 | 69 | ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}"> |
66 | 70 | <md-tooltip md-direction="top"> |
... | ... | @@ -73,18 +77,18 @@ |
73 | 77 | </div> |
74 | 78 | </div> |
75 | 79 | </div> |
76 | - <div ng-show="!vm.isSingleEntityAlias && !vm.disableAdd" style="padding-bottom: 10px;"> | |
77 | - <md-button ng-disabled="loading" class="md-primary md-raised" ng-click="vm.addAlias($event)" aria-label="{{ 'action.add' | translate }}"> | |
78 | - <md-tooltip md-direction="top"> | |
79 | - {{ 'entity.add-alias' | translate }} | |
80 | - </md-tooltip> | |
81 | - <span translate>action.add</span> | |
82 | - </md-button> | |
83 | - </div> | |
84 | 80 | </fieldset> |
85 | 81 | </div> |
86 | 82 | </md-dialog-content> |
87 | 83 | <md-dialog-actions layout="row"> |
84 | + <md-button ng-show="!vm.isSingleEntityAlias && !vm.disableAdd" ng-disabled="loading" class="md-primary md-raised" | |
85 | + ng-click="vm.addAlias($event)" | |
86 | + aria-label="{{ 'action.add' | translate }}"> | |
87 | + <md-tooltip md-direction="top"> | |
88 | + {{ 'entity.add-alias' | translate }} | |
89 | + </md-tooltip> | |
90 | + <span translate>action.add</span> | |
91 | + </md-button> | |
88 | 92 | <span flex></span> |
89 | 93 | <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> |
90 | 94 | {{ 'action.save' | translate }} | ... | ... |
... | ... | @@ -35,46 +35,16 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc |
35 | 35 | |
36 | 36 | scope.ngModelCtrl = ngModelCtrl; |
37 | 37 | scope.types = types; |
38 | - | |
39 | - /* scope.fetchEntities = function(searchText, limit) { | |
40 | - var deferred = $q.defer(); | |
41 | - entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(function success(result) { | |
42 | - if (result) { | |
43 | - deferred.resolve(result); | |
44 | - } else { | |
45 | - deferred.resolve([]); | |
46 | - } | |
47 | - }, function fail() { | |
48 | - deferred.reject(); | |
49 | - }); | |
50 | - return deferred.promise; | |
51 | - }*/ | |
38 | + scope.hideLabels = angular.isDefined(attrs.hideLabels); | |
52 | 39 | |
53 | 40 | scope.updateValidity = function() { |
54 | 41 | if (ngModelCtrl.$viewValue) { |
55 | 42 | var value = ngModelCtrl.$viewValue; |
56 | 43 | ngModelCtrl.$setValidity('filter', value.type ? true : false); |
57 | - /*if (value.useFilter) { | |
58 | - ngModelCtrl.$setValidity('entityList', true); | |
59 | - if (angular.isDefined(value.entityNameFilter) && value.entityNameFilter.length > 0) { | |
60 | - ngModelCtrl.$setValidity('entityNameFilter', true); | |
61 | - valid = angular.isDefined(scope.model.matchingFilterEntity) && scope.model.matchingFilterEntity != null; | |
62 | - ngModelCtrl.$setValidity('entityNameFilterEntityMatch', valid); | |
63 | - } else { | |
64 | - ngModelCtrl.$setValidity('entityNameFilter', false); | |
65 | - } | |
66 | - } else { | |
67 | - ngModelCtrl.$setValidity('entityNameFilter', true); | |
68 | - ngModelCtrl.$setValidity('entityNameFilterDeviceMatch', true); | |
69 | - valid = angular.isDefined(value.entityList) && value.entityList.length > 0; | |
70 | - ngModelCtrl.$setValidity('entityList', valid); | |
71 | - }*/ | |
72 | - | |
73 | 44 | } |
74 | 45 | } |
75 | 46 | |
76 | 47 | ngModelCtrl.$render = function () { |
77 | - //destroyWatchers(); | |
78 | 48 | if (ngModelCtrl.$viewValue) { |
79 | 49 | scope.model = angular.copy(ngModelCtrl.$viewValue); |
80 | 50 | } else { |
... | ... | @@ -83,28 +53,6 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc |
83 | 53 | resolveMultiple: false |
84 | 54 | } |
85 | 55 | } |
86 | - /* if (ngModelCtrl.$viewValue) { | |
87 | - var value = ngModelCtrl.$viewValue; | |
88 | - var model = scope.model; | |
89 | - model.useFilter = value.useFilter === true ? true: false; | |
90 | - model.entityList = []; | |
91 | - model.entityNameFilter = value.entityNameFilter || ''; | |
92 | - processEntityNameFilter(model.entityNameFilter).then( | |
93 | - function(entity) { | |
94 | - scope.model.matchingFilterEntity = entity; | |
95 | - if (value.entityList && value.entityList.length > 0) { | |
96 | - entityService.getEntities(scope.entityType, value.entityList).then(function (entities) { | |
97 | - model.entityList = entities; | |
98 | - updateMatchingEntity(); | |
99 | - initWatchers(); | |
100 | - }); | |
101 | - } else { | |
102 | - updateMatchingEntity(); | |
103 | - initWatchers(); | |
104 | - } | |
105 | - } | |
106 | - ) | |
107 | - }*/ | |
108 | 56 | } |
109 | 57 | |
110 | 58 | scope.$watch('model.resolveMultiple', function () { |
... | ... | @@ -149,115 +97,6 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc |
149 | 97 | }); |
150 | 98 | } |
151 | 99 | |
152 | - /* function updateMatchingEntity() { | |
153 | - if (scope.model.useFilter) { | |
154 | - scope.model.matchingEntity = scope.model.matchingFilterEntity; | |
155 | - } else { | |
156 | - if (scope.model.entityList && scope.model.entityList.length > 0) { | |
157 | - scope.model.matchingEntity = scope.model.entityList[0]; | |
158 | - } else { | |
159 | - scope.model.matchingEntity = null; | |
160 | - } | |
161 | - } | |
162 | - } | |
163 | - | |
164 | - function processEntityNameFilter(entityNameFilter) { | |
165 | - var deferred = $q.defer(); | |
166 | - if (angular.isDefined(entityNameFilter) && entityNameFilter.length > 0) { | |
167 | - scope.fetchEntities(entityNameFilter, 1).then(function (entities) { | |
168 | - if (entities && entities.length > 0) { | |
169 | - deferred.resolve(entities[0]); | |
170 | - } else { | |
171 | - deferred.resolve(null); | |
172 | - } | |
173 | - }); | |
174 | - } else { | |
175 | - deferred.resolve(null); | |
176 | - } | |
177 | - return deferred.promise; | |
178 | - } | |
179 | - | |
180 | - function destroyWatchers() { | |
181 | - if (scope.entityTypeDeregistration) { | |
182 | - scope.entityTypeDeregistration(); | |
183 | - scope.entityTypeDeregistration = null; | |
184 | - } | |
185 | - if (scope.entityListDeregistration) { | |
186 | - scope.entityListDeregistration(); | |
187 | - scope.entityListDeregistration = null; | |
188 | - } | |
189 | - if (scope.useFilterDeregistration) { | |
190 | - scope.useFilterDeregistration(); | |
191 | - scope.useFilterDeregistration = null; | |
192 | - } | |
193 | - if (scope.entityNameFilterDeregistration) { | |
194 | - scope.entityNameFilterDeregistration(); | |
195 | - scope.entityNameFilterDeregistration = null; | |
196 | - } | |
197 | - if (scope.matchingEntityDeregistration) { | |
198 | - scope.matchingEntityDeregistration(); | |
199 | - scope.matchingEntityDeregistration = null; | |
200 | - } | |
201 | - } | |
202 | - | |
203 | - function initWatchers() { | |
204 | - | |
205 | - scope.entityTypeDeregistration = scope.$watch('entityType', function (newEntityType, prevEntityType) { | |
206 | - if (!angular.equals(newEntityType, prevEntityType)) { | |
207 | - scope.model.entityList = []; | |
208 | - scope.model.entityNameFilter = ''; | |
209 | - } | |
210 | - }); | |
211 | - | |
212 | - scope.entityListDeregistration = scope.$watch('model.entityList', function () { | |
213 | - if (ngModelCtrl.$viewValue) { | |
214 | - var value = ngModelCtrl.$viewValue; | |
215 | - value.entityList = []; | |
216 | - if (scope.model.entityList && scope.model.entityList.length > 0) { | |
217 | - for (var i=0;i<scope.model.entityList.length;i++) { | |
218 | - value.entityList.push(scope.model.entityList[i].id.id); | |
219 | - } | |
220 | - } | |
221 | - updateMatchingEntity(); | |
222 | - ngModelCtrl.$setViewValue(value); | |
223 | - scope.updateValidity(); | |
224 | - } | |
225 | - }, true); | |
226 | - scope.useFilterDeregistration = scope.$watch('model.useFilter', function () { | |
227 | - if (ngModelCtrl.$viewValue) { | |
228 | - var value = ngModelCtrl.$viewValue; | |
229 | - value.useFilter = scope.model.useFilter; | |
230 | - updateMatchingEntity(); | |
231 | - ngModelCtrl.$setViewValue(value); | |
232 | - scope.updateValidity(); | |
233 | - } | |
234 | - }); | |
235 | - scope.entityNameFilterDeregistration = scope.$watch('model.entityNameFilter', function (newNameFilter, prevNameFilter) { | |
236 | - if (ngModelCtrl.$viewValue) { | |
237 | - if (!angular.equals(newNameFilter, prevNameFilter)) { | |
238 | - var value = ngModelCtrl.$viewValue; | |
239 | - value.entityNameFilter = scope.model.entityNameFilter; | |
240 | - processEntityNameFilter(value.entityNameFilter).then( | |
241 | - function(entity) { | |
242 | - scope.model.matchingFilterEntity = entity; | |
243 | - updateMatchingEntity(); | |
244 | - ngModelCtrl.$setViewValue(value); | |
245 | - scope.updateValidity(); | |
246 | - } | |
247 | - ); | |
248 | - } | |
249 | - } | |
250 | - }); | |
251 | - | |
252 | - scope.matchingEntityDeregistration = scope.$watch('model.matchingEntity', function (newMatchingEntity, prevMatchingEntity) { | |
253 | - if (!angular.equals(newMatchingEntity, prevMatchingEntity)) { | |
254 | - if (scope.onMatchingEntityChange) { | |
255 | - scope.onMatchingEntityChange({entity: newMatchingEntity}); | |
256 | - } | |
257 | - } | |
258 | - }); | |
259 | - }*/ | |
260 | - | |
261 | 100 | $compile(element.contents())(scope); |
262 | 101 | |
263 | 102 | } | ... | ... |
... | ... | @@ -14,18 +14,6 @@ |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 16 | .tb-entity-filter { |
17 | - #entity_list_chips { | |
18 | - .md-chips { | |
19 | - padding-bottom: 1px; | |
20 | - } | |
21 | - } | |
22 | - .entity-name-filter-input { | |
23 | - margin-top: 10px; | |
24 | - margin-bottom: 0px; | |
25 | - .md-errors-spacer { | |
26 | - min-height: 0px; | |
27 | - } | |
28 | - } | |
29 | 17 | .tb-filter-switch { |
30 | 18 | padding-left: 10px; |
31 | 19 | .filter-switch { |
... | ... | @@ -39,7 +27,8 @@ |
39 | 27 | margin-top: -11px; |
40 | 28 | height: 35px; |
41 | 29 | .tb-error-message { |
42 | - padding-left: 1px; | |
30 | + padding-left: 8px; | |
31 | + padding-top: 14px; | |
43 | 32 | } |
44 | 33 | } |
45 | 34 | } |
\ No newline at end of file | ... | ... |
... | ... | @@ -15,80 +15,37 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<section layout='column' class="tb-entity-filter"> | |
19 | - <section layout='row'> | |
20 | - <!--section layout="column" flex ng-show="!model.useFilter"> | |
21 | - <md-chips flex | |
22 | - id="entity_list_chips" | |
23 | - ng-required="!useFilter" | |
24 | - ng-model="model.entityList" md-autocomplete-snap | |
25 | - md-require-match="true"> | |
26 | - <md-autocomplete | |
27 | - md-no-cache="true" | |
28 | - id="entity" | |
29 | - md-selected-item="selectedEntity" | |
30 | - md-search-text="entitySearchText" | |
31 | - md-items="item in fetchEntities(entitySearchText, 10)" | |
32 | - md-item-text="item.name" | |
33 | - md-min-length="0" | |
34 | - placeholder="{{ 'entity.entity-list' | translate }}"> | |
35 | - <md-item-template> | |
36 | - <span md-highlight-text="entitySearchText" md-highlight-flags="^i">{{item.name}}</span> | |
37 | - </md-item-template> | |
38 | - <md-not-found> | |
39 | - <span translate translate-values='{ entity: entitySearchText }'>entity.no-entities-matching</span> | |
40 | - </md-not-found> | |
41 | - </md-autocomplete> | |
42 | - <md-chip-template> | |
43 | - <span> | |
44 | - <strong>{{$chip.name}}</strong> | |
45 | - </span> | |
46 | - </md-chip-template> | |
47 | - </md-chips> | |
48 | - </section> | |
49 | - <section layout="row" flex ng-show="model.useFilter"> | |
50 | - <md-input-container flex class="entity-name-filter-input"> | |
51 | - <label translate>entity.name-starts-with</label> | |
52 | - <input ng-model="model.entityNameFilter" aria-label="{{ 'entity.name-starts-with' | translate }}"> | |
53 | - </md-input-container> | |
54 | - </section--> | |
55 | - <section layout="row" flex layout-align="start center"> | |
56 | - <div flex ng-if="model.type">{{ types.aliasFilterType[model.type].name | translate }}</div> | |
57 | - <md-button ng-if="model.type" ng-disabled="loading" class="md-icon-button md-primary" | |
58 | - style="min-width: 40px;" | |
59 | - ng-click="editFilter($event)" | |
60 | - aria-label="{{ 'alias.edit-entity-filter' | translate }}"> | |
61 | - <md-tooltip md-direction="top"> | |
62 | - {{ 'alias.edit-entity-filter' | translate }} | |
63 | - </md-tooltip> | |
64 | - <md-icon aria-label="{{ 'alias.edit-entity-filter' | translate }}" | |
18 | +<section layout='row' class="tb-entity-filter"> | |
19 | + <section layout="row" flex="70"> | |
20 | + <section flex layout="column" layout-align="center start"> | |
21 | + <div ng-if="model.type">{{ types.aliasFilterType[model.type].name | translate }}</div> | |
22 | + <md-button ng-if="!model.type" | |
23 | + ng-disabled="loading" class="md-primary" | |
24 | + ng-click="createFilter($event)" | |
25 | + aria-label="{{ 'alias.create-entity-filter' | translate }}"> | |
26 | + <md-icon aria-label="{{ 'alias.create-entity-filter' | translate }}" | |
65 | 27 | class="material-icons"> |
66 | - edit | |
28 | + add | |
67 | 29 | </md-icon> |
30 | + {{ 'alias.create-entity-filter' | translate }} | |
68 | 31 | </md-button> |
69 | - <div ng-if="!model.type" layout="row" layout-align="center start"> | |
70 | - <md-button ng-disabled="loading" class="md-primary md-raised" | |
71 | - ng-click="createFilter($event)" | |
72 | - aria-label="{{ 'alias.create-entity-filter' | translate }}"> | |
73 | - <md-icon aria-label="{{ 'alias.create-entity-filter' | translate }}" | |
74 | - class="material-icons"> | |
75 | - add | |
76 | - </md-icon> | |
77 | - {{ 'alias.create-entity-filter' | translate }} | |
78 | - </md-button> | |
79 | - </div> | |
80 | - </section> | |
81 | - <section class="tb-filter-switch" layout="column" layout-align="center center"> | |
82 | - <label class="tb-small filter-label" translate>alias.resolve-multiple</label> | |
83 | - <md-switch class="filter-switch" ng-model="model.resolveMultiple" aria-label="resolve-multiple-switcher"> | |
84 | - </md-switch> | |
85 | 32 | </section> |
33 | + <md-button ng-if="model.type" ng-disabled="loading" class="md-icon-button md-primary" | |
34 | + style="min-width: 40px;" | |
35 | + ng-click="editFilter($event)" | |
36 | + aria-label="{{ 'alias.edit-entity-filter' | translate }}"> | |
37 | + <md-tooltip md-direction="top"> | |
38 | + {{ 'alias.edit-entity-filter' | translate }} | |
39 | + </md-tooltip> | |
40 | + <md-icon aria-label="{{ 'alias.edit-entity-filter' | translate }}" | |
41 | + class="material-icons"> | |
42 | + edit | |
43 | + </md-icon> | |
44 | + </md-button> | |
45 | + </section> | |
46 | + <section class="tb-filter-switch" layout="column" flex="30" layout-align="center center"> | |
47 | + <label ng-if="!hideLabels" class="tb-small filter-label" translate>alias.resolve-multiple</label> | |
48 | + <md-switch class="filter-switch" ng-model="model.resolveMultiple" aria-label="resolve-multiple-switcher"> | |
49 | + </md-switch> | |
86 | 50 | </section> |
87 | - <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert"> | |
88 | - <div translate ng-message="filter" class="tb-error-message">alias.entity-filter-required</div> | |
89 | - <!--div translate ng-message="entityList" class="tb-error-message">entity.entity-list-empty</div> | |
90 | - <div translate ng-message="entityNameFilter" class="tb-error-message">entity.entity-name-filter-required</div> | |
91 | - <div translate translate-values='{ entity: model.entityNameFilter }' ng-message="entityNameFilterEntityMatch" | |
92 | - class="tb-error-message">entity.entity-name-filter-no-entity-matched</div--> | |
93 | - </div> | |
94 | -</section> | |
\ No newline at end of file | ||
51 | +</section> | ... | ... |
... | ... | @@ -24,7 +24,7 @@ import entityAliasesTemplate from '../entity/entity-aliases.tpl.html'; |
24 | 24 | /* eslint-disable no-undef, angular/window-service, angular/document-service */ |
25 | 25 | |
26 | 26 | /*@ngInject*/ |
27 | -export default function ImportExport($log, $translate, $q, $mdDialog, $document, itembuffer, types, dashboardUtils, | |
27 | +export default function ImportExport($log, $translate, $q, $mdDialog, $document, itembuffer, utils, types, dashboardUtils, | |
28 | 28 | entityService, dashboardService, pluginService, ruleService, widgetService, toast) { |
29 | 29 | |
30 | 30 | |
... | ... | @@ -415,6 +415,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
415 | 415 | deferred.reject(); |
416 | 416 | } else { |
417 | 417 | var widget = widgetItem.widget; |
418 | + widget = dashboardUtils.validateAndUpdateWidget(widget); | |
418 | 419 | var aliasesInfo = prepareAliasesInfo(widgetItem.aliasesInfo); |
419 | 420 | var originalColumns = widgetItem.originalColumns; |
420 | 421 | var originalSize = widgetItem.originalSize; |
... | ... | @@ -425,22 +426,22 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
425 | 426 | var entityAliases = {}; |
426 | 427 | var datasourceAliasesMap = {}; |
427 | 428 | var targetDeviceAliasesMap = {}; |
428 | - var aliasId = 1; | |
429 | + var aliasId; | |
429 | 430 | var datasourceIndex; |
430 | 431 | if (datasourceAliases) { |
431 | 432 | for (datasourceIndex in datasourceAliases) { |
433 | + aliasId = utils.guid(); | |
432 | 434 | datasourceAliasesMap[aliasId] = datasourceIndex; |
433 | 435 | entityAliases[aliasId] = datasourceAliases[datasourceIndex]; |
434 | 436 | entityAliases[aliasId].id = aliasId; |
435 | - aliasId++; | |
436 | 437 | } |
437 | 438 | } |
438 | 439 | if (targetDeviceAliases) { |
439 | 440 | for (datasourceIndex in targetDeviceAliases) { |
441 | + aliasId = utils.guid(); | |
440 | 442 | targetDeviceAliasesMap[aliasId] = datasourceIndex; |
441 | 443 | entityAliases[aliasId] = targetDeviceAliases[datasourceIndex]; |
442 | 444 | entityAliases[aliasId].id = aliasId; |
443 | - aliasId++; | |
444 | 445 | } |
445 | 446 | } |
446 | 447 | ... | ... |
... | ... | @@ -124,7 +124,7 @@ export default angular.module('thingsboard.locale', []) |
124 | 124 | "create-entity-filter": "Create entity filter", |
125 | 125 | "edit-entity-filter": "Edit entity filter", |
126 | 126 | "entity-filter-required": "Entity filter is required.", |
127 | - "resolve-multiple": "Multiple", | |
127 | + "resolve-multiple": "Resolve as multiple entities", | |
128 | 128 | "filter-type": "Filter type", |
129 | 129 | "filter-type-required": "Filter type is required.", |
130 | 130 | "use-state-entity": "Use state entity", | ... | ... |
... | ... | @@ -298,11 +298,7 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) { |
298 | 298 | } |
299 | 299 | if (!newAliasId) { |
300 | 300 | var newAliasName = createEntityAliasName(entityAliases, aliasInfo.alias); |
301 | - newAliasId = 0; | |
302 | - for (aliasId in entityAliases) { | |
303 | - newAliasId = Math.max(newAliasId, aliasId); | |
304 | - } | |
305 | - newAliasId++; | |
301 | + newAliasId = utils.guid(); | |
306 | 302 | entityAliases[newAliasId] = {id: newAliasId, alias: newAliasName, filter: aliasInfo.filter}; |
307 | 303 | } |
308 | 304 | return newAliasId; | ... | ... |