Commit 43b23cef4feb81697eb7807670b9ad5adb2edd0f

Authored by Igor Kulikov
1 parent 0a82a830

TB-61: Improve Alias list editor.

... ... @@ -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;
... ...