thingsboard-memoizeit

TB-61: Improve Alias list editor.

6/8/2017 3:15:47 PM

Details

diff --git a/ui/src/app/api/alias-controller.js b/ui/src/app/api/alias-controller.js
index 49eb8fe..b6d29a9 100644
--- a/ui/src/app/api/alias-controller.js
+++ b/ui/src/app/api/alias-controller.js
@@ -100,6 +100,7 @@ export default class AliasController {
                             aliasCtrl.resolvedAliasesToStateEntities[aliasId] =
                                 aliasCtrl.stateController.getStateParams().entityId;
                         }
+                        aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId);
                         deferred.resolve(aliasInfo);
                     },
                     function fail() {
@@ -239,6 +240,9 @@ export default class AliasController {
                         datasource.name = name;
                         datasource.aliasName = name;
                         datasource.entityName = name;
+                     } else if (datasource.unresolvedStateEntity) {
+                        datasource.name = "Unresolved";
+                        datasource.entityName = "Unresolved";
                      }
                      datasource.dataKeys.forEach(function(dataKey) {
                          if (datasource.generated) {
diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js
index dd518c7..4cb382c 100644
--- a/ui/src/app/api/subscription.js
+++ b/ui/src/app/api/subscription.js
@@ -73,6 +73,15 @@ export default class Subscription {
 
             this.datasources = this.ctx.utils.validateDatasources(options.datasources);
             this.datasourceListeners = [];
+
+            /*
+             *   data = array of datasourceData
+             *   datasourceData = {
+             *   			tbDatasource,
+             *   			dataKey,     { name, config }
+             *   			data = array of [time, value]
+             *   }
+             */
             this.data = [];
             this.hiddenData = [];
             this.originalTimewindow = null;
@@ -543,39 +552,6 @@ export default class Subscription {
             var datasource = this.datasources[i];
             if (angular.isFunction(datasource))
                 continue;
-           /* var entityId = null;
-            var entityType = null;
-            if (datasource.type === this.ctx.types.datasourceType.entity) {
-                var aliasName = null;
-                var entityName = null;
-                if (datasource.entityId) {
-                    entityId = datasource.entityId;
-                    entityType = datasource.entityType;
-                    datasource.name = datasource.entityName;
-                    aliasName = datasource.entityName;
-                    entityName = datasource.entityName;
-                } else if (datasource.entityAliasId) {
-                    if (this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId]) {
-                        entityId = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].entityId;
-                        entityType = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].entityType;
-                        datasource.name = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].alias;
-                        aliasName = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].alias;
-                        entityName = '';
-                        var entitiesInfo = this.ctx.aliasesInfo.entityAliasesInfo[datasource.entityAliasId];
-                        for (var d = 0; d < entitiesInfo.length; d++) {
-                            if (entitiesInfo[d].id === entityId) {
-                                entityName = entitiesInfo[d].name;
-                                break;
-                            }
-                        }
-                    }
-                }
-            } else {
-                datasource.name = datasource.name || this.ctx.types.datasourceType.function;
-            }
-            for (var dk = 0; dk < datasource.dataKeys.length; dk++) {
-                updateDataKeyLabel(datasource.dataKeys[dk], datasource.name, entityName, aliasName);
-            }*/
 
             var subscription = this;
 
@@ -606,6 +582,10 @@ export default class Subscription {
 
             this.datasourceListeners.push(listener);
             this.ctx.datasourceService.subscribeToDatasource(listener);
+            if (datasource.unresolvedStateEntity) {
+                this.notifyDataLoaded();
+                this.onDataUpdated();
+            }
         }
     }
 
@@ -625,19 +605,6 @@ export default class Subscription {
         } else {
             return false;
         }
-        /*var deviceId = null;
-        if (this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId]) {
-            deviceId = this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId].entityId;
-        }
-        if (!angular.equals(deviceId, this.targetDeviceId)) {
-            this.targetDeviceId = deviceId;
-            if (this.targetDeviceId) {
-                this.rpcEnabled = true;
-            } else {
-                this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false;
-            }
-            this.callbacks.rpcStateChanged(this);
-        }*/
     }
 
     checkSubscriptions(aliasIds) {
@@ -650,29 +617,8 @@ export default class Subscription {
                     break;
                 }
             }
-            /*var entityId = null;
-            var entityType = null;
-            var aliasName = null;
-            if (listener.datasource.type === this.ctx.types.datasourceType.entity) {
-                if (listener.datasource.entityAliasId &&
-                    this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId]) {
-                    entityId = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].entityId;
-                    entityType = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].entityType;
-                    aliasName = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].alias;
-                }
-                if (!angular.equals(entityId, listener.entityId) ||
-                    !angular.equals(entityType, listener.entityType) ||
-                    !angular.equals(aliasName, listener.datasource.name)) {
-                    subscriptionsChanged = true;
-                    break;
-                }
-            }*/
         }
         return subscriptionsChanged;
-        /*if (subscriptionsChanged) {
-            this.unsubscribe();
-            this.subscribe();
-        }*/
     }
 
     destroy() {
@@ -691,29 +637,6 @@ export default class Subscription {
 
 }
 
-/*const varsRegex = /\$\{([^\}]*)\}/g;
-
-function updateDataKeyLabel(dataKey, dsName, entityName, aliasName) {
-    var pattern = dataKey.pattern;
-    var label = dataKey.pattern;
-    var match = varsRegex.exec(pattern);
-    while (match !== null) {
-        var variable = match[0];
-        var variableName = match[1];
-        if (variableName === 'dsName') {
-            label = label.split(variable).join(dsName);
-        } else if (variableName === 'entityName') {
-            label = label.split(variable).join(entityName);
-        } else if (variableName === 'deviceName') {
-            label = label.split(variable).join(entityName);
-        } else if (variableName === 'aliasName') {
-            label = label.split(variable).join(aliasName);
-        }
-        match = varsRegex.exec(pattern);
-    }
-    dataKey.label = label;
-}*/
-
 function calculateMin(data) {
     if (data.length > 0) {
         var result = Number(data[0][1]);
diff --git a/ui/src/app/common/dashboard-utils.service.js b/ui/src/app/common/dashboard-utils.service.js
index 58d1dbd..fe8aa93 100644
--- a/ui/src/app/common/dashboard-utils.service.js
+++ b/ui/src/app/common/dashboard-utils.service.js
@@ -23,8 +23,10 @@ function DashboardUtils(types, utils, timeService) {
 
     var service = {
         validateAndUpdateDashboard: validateAndUpdateDashboard,
+        validateAndUpdateWidget: validateAndUpdateWidget,
         getRootStateId: getRootStateId,
         createSingleWidgetDashboard: createSingleWidgetDashboard,
+        createSingleEntityFilter: createSingleEntityFilter,
         getStateLayoutsData: getStateLayoutsData,
         createDefaultState: createDefaultState,
         createDefaultLayoutData: createDefaultLayoutData,
@@ -39,7 +41,7 @@ function DashboardUtils(types, utils, timeService) {
 
     return service;
 
-    function validateAndUpdateEntityAliases(configuration) {
+    function validateAndUpdateEntityAliases(configuration, datasourcesByAliasId, targetDevicesByAliasId) {
         var aliasId, entityAlias;
         if (angular.isUndefined(configuration.entityAliases)) {
             configuration.entityAliases = {};
@@ -47,8 +49,8 @@ function DashboardUtils(types, utils, timeService) {
                 var deviceAliases = configuration.deviceAliases;
                 for (aliasId in deviceAliases) {
                     var deviceAlias = deviceAliases[aliasId];
-                    entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias);
-                    configuration.entityAliases[aliasId] = entityAlias;
+                    entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId);
+                    configuration.entityAliases[entityAlias.id] = entityAlias;
                 }
                 delete configuration.deviceAliases;
             }
@@ -56,16 +58,43 @@ function DashboardUtils(types, utils, timeService) {
             var entityAliases = configuration.entityAliases;
             for (aliasId in entityAliases) {
                 entityAlias = entityAliases[aliasId];
-                entityAliases[aliasId] = validateAndUpdateEntityAlias(entityAlias);
-                if (!entityAliases[aliasId].id) {
-                    entityAliases[aliasId].id = aliasId;
+                entityAlias = validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId);
+                if (aliasId != entityAlias.id) {
+                    delete entityAliases[aliasId];
                 }
+                entityAliases[entityAlias.id] = entityAlias;
             }
         }
         return configuration;
     }
 
-    function validateAndUpdateDeviceAlias(aliasId, deviceAlias) {
+    function validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId) {
+        if (!aliasId || !angular.isString(aliasId) || aliasId.length != 36) {
+            var newAliasId = utils.guid();
+            var aliasDatasources = datasourcesByAliasId[aliasId];
+            if (aliasDatasources) {
+                aliasDatasources.forEach(
+                      function(datasource) {
+                          datasource.entityAliasId = newAliasId;
+                      }
+                );
+            }
+            var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId];
+            if (targetDeviceAliasIdsList) {
+                targetDeviceAliasIdsList.forEach(
+                    function(targetDeviceAliasIds) {
+                        targetDeviceAliasIds[0] = newAliasId;
+                    }
+                );
+            }
+            return newAliasId;
+        } else {
+            return aliasId;
+        }
+    }
+
+    function validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId) {
+        aliasId = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId);
         var alias = deviceAlias.alias;
         var entityAlias = {
             id: aliasId,
@@ -93,7 +122,8 @@ function DashboardUtils(types, utils, timeService) {
         return entityAlias;
     }
 
-    function validateAndUpdateEntityAlias(entityAlias) {
+    function validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId) {
+        entityAlias.id = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId);
         if (!entityAlias.filter) {
             entityAlias.filter = {
                 type: entityAlias.entityFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value,
@@ -206,12 +236,33 @@ function DashboardUtils(types, utils, timeService) {
             }
         }
 
-       /* var datasources = {};
+        var datasourcesByAliasId = {};
+        var targetDevicesByAliasId = {};
         for (var widgetId in dashboard.configuration.widgets) {
+            widget = dashboard.configuration.widgets[widgetId];
+            widget.config.datasources.forEach(function (datasource) {
+               if (datasource.entityAliasId) {
+                   var aliasId = datasource.entityAliasId;
+                   var aliasDatasources = datasourcesByAliasId[aliasId];
+                   if (!aliasDatasources) {
+                       aliasDatasources = [];
+                       datasourcesByAliasId[aliasId] = aliasDatasources;
+                   }
+                   aliasDatasources.push(datasource);
+               }
+            });
+            if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length) {
+                var aliasId = widget.config.targetDeviceAliasIds[0];
+                var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId];
+                if (!targetDeviceAliasIdsList) {
+                    targetDeviceAliasIdsList = [];
+                    targetDevicesByAliasId[aliasId] = targetDeviceAliasIdsList;
+                }
+                targetDeviceAliasIdsList.push(widget.config.targetDeviceAliasIds);
+            }
+        }
 
-        }*/
-
-        dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration);
+        dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration, datasourcesByAliasId, targetDevicesByAliasId);
 
         if (angular.isUndefined(dashboard.configuration.timewindow)) {
             dashboard.configuration.timewindow = timeService.defaultTimewindow();
@@ -288,6 +339,16 @@ function DashboardUtils(types, utils, timeService) {
         return dashboard;
     }
 
+    function createSingleEntityFilter(entityType, entityId) {
+        return {
+            type: types.aliasFilterType.entityList.value,
+            stateEntity: false,
+            entityList: [entityId],
+            entityType: entityType,
+            resolveMultiple: false
+        };
+    }
+
     function getStateLayoutsData(dashboard, targetState) {
         var dashboardConfiguration = dashboard.configuration;
         var states = dashboardConfiguration.states;
diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js
index 7b4d65e..8bf76c4 100644
--- a/ui/src/app/common/utils.service.js
+++ b/ui/src/app/common/utils.service.js
@@ -106,6 +106,7 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
         isDescriptorSchemaNotEmpty: isDescriptorSchemaNotEmpty,
         filterSearchTextEntities: filterSearchTextEntities,
         guid: guid,
+        cleanCopy: cleanCopy,
         isLocalUrl: isLocalUrl,
         validateDatasources: validateDatasources,
         createKey: createKey,
@@ -291,6 +292,16 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
             s4() + '-' + s4() + s4() + s4();
     }
 
+    function cleanCopy(object) {
+        var copy = angular.copy(object);
+        for (var prop in copy) {
+            if (prop && prop.startsWith('$$')) {
+                delete copy[prop];
+            }
+        }
+        return copy;
+    }
+
     function genNextColor(datasources) {
         var index = 0;
         if (datasources) {
diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js
index 965b838..a88e656 100644
--- a/ui/src/app/components/dashboard.directive.js
+++ b/ui/src/app/components/dashboard.directive.js
@@ -85,7 +85,7 @@ function Dashboard() {
 }
 
 /*@ngInject*/
-function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, timeService, types, utils) {
+function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $mdUtil, timeService, types, utils) {
 
     var highlightedMode = false;
     var highlightedWidget = null;
@@ -792,7 +792,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t
     }
 
     function dashboardLoaded() {
-        $timeout(function () {
+        $mdUtil.nextTick(function () {
             if (vm.dashboardTimewindowWatch) {
                 vm.dashboardTimewindowWatch();
                 vm.dashboardTimewindowWatch = null;
@@ -802,14 +802,27 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t
             }, true);
             adoptMaxRows();
             vm.dashboardLoading = false;
-            $timeout(function () {
-                var gridsterScope = gridsterElement.scope();
-                vm.gridster = gridsterScope.gridster;
-                if (vm.onInit) {
-                    vm.onInit({dashboard: vm});
+            if ($scope.gridsterScopeWatcher) {
+                $scope.gridsterScopeWatcher();
+            }
+            $scope.gridsterScopeWatcher = $scope.$watch(
+                function() {
+                    var hasScope = gridsterElement.scope() ? true : false;
+                    return hasScope;
+                },
+                function(hasScope) {
+                    if (hasScope) {
+                        $scope.gridsterScopeWatcher();
+                        $scope.gridsterScopeWatcher = null;
+                        var gridsterScope = gridsterElement.scope();
+                        vm.gridster = gridsterScope.gridster;
+                        if (vm.onInit) {
+                            vm.onInit({dashboard: vm});
+                        }
+                    }
                 }
-            }, 0, false);
-        }, 0, false);
+            );
+        });
     }
 
     function loading() {
diff --git a/ui/src/app/components/widget.controller.js b/ui/src/app/components/widget.controller.js
index 4407381..08885b8 100644
--- a/ui/src/app/components/widget.controller.js
+++ b/ui/src/app/components/widget.controller.js
@@ -20,9 +20,9 @@ import Subscription from '../api/subscription';
 /* eslint-disable angular/angularelement */
 
 /*@ngInject*/
-export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, tbRaf, types, utils, timeService,
+export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService,
                                          datasourceService, entityService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow,
-                                         dashboardTimewindowApi, widget, aliasController, stateController, widgetType) {
+                                         dashboardTimewindowApi, widget, aliasController, stateController, widgetInfo, widgetType) {
 
     var vm = this;
 
@@ -42,20 +42,11 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
 
     var cafs = {};
 
-    /*
-     *   data = array of datasourceData
-     *   datasourceData = {
-     *   			tbDatasource,
-     *   			dataKey,     { name, config }
-     *   			data = array of [time, value]
-     *   }
-     */
-
     var widgetContext = {
         inited: false,
         $scope: $scope,
-        $container: $('#container', $element),
-        $containerParent: $($element),
+        $container: null,
+        $containerParent: null,
         width: 0,
         height: 0,
         isEdit: isEdit,
@@ -82,30 +73,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
             createSubscription: function(options, subscribe) {
                 return createSubscription(options, subscribe);
             },
-
-
-    //      type: "timeseries" or "latest" or "rpc"
-    /*      subscriptionInfo = [
-            {
-                entityType: ""
-                entityId:   ""
-                entityName: ""
-                timeseries: [{ name: "", label: "" }, ..]
-                attributes: [{ name: "", label: "" }, ..]
-            }
-    ..
-    ]*/
-
-    //      options = {
-    //        timeWindowConfig,
-    //        useDashboardTimewindow,
-    //        legendConfig,
-    //        decimals,
-    //        units,
-    //        callbacks [ onDataUpdated(subscription, apply) ]
-    //      }
-    //
-
             createSubscriptionFromInfo: function (type, subscriptionsInfo, options, useDefaultComponents, subscribe) {
                 return createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe);
             },
@@ -211,21 +178,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
         }
     );
 
-    /*
-            options = {
-                 type,
-                 targetDeviceAliasIds,  // RPC
-                 targetDeviceIds,       // RPC
-                 datasources,
-                 timeWindowConfig,
-                 useDashboardTimewindow,
-                 legendConfig,
-                 decimals,
-                 units,
-                 callbacks
-            }
-     */
-
     function createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe) {
         var deferred = $q.defer();
         options.type = type;
@@ -400,6 +352,101 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
         return deferred.promise;
     }
 
+    function configureWidgetElement() {
+
+        $scope.displayLegend = angular.isDefined(widget.config.showLegend) ?
+            widget.config.showLegend : widget.type === types.widgetType.timeseries.value;
+
+        if ($scope.displayLegend) {
+            $scope.legendConfig = widget.config.legendConfig ||
+                {
+                    position: types.position.bottom.value,
+                    showMin: false,
+                    showMax: false,
+                    showAvg: widget.type === types.widgetType.timeseries.value,
+                    showTotal: false
+                };
+            $scope.legendData = {
+                keys: [],
+                data: []
+            };
+        }
+
+        var html = '<div class="tb-absolute-fill tb-widget-error" ng-if="widgetErrorData">' +
+            '<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' +
+            '</div>' +
+            '<div class="tb-absolute-fill tb-widget-loading" ng-show="loadingData" layout="column" layout-align="center center">' +
+            '<md-progress-circular md-mode="indeterminate" ng-disabled="!loadingData" class="md-accent" md-diameter="40"></md-progress-circular>' +
+            '</div>';
+
+        var containerHtml = '<div id="container">' + widgetInfo.templateHtml + '</div>';
+        if ($scope.displayLegend) {
+            var layoutType;
+            if ($scope.legendConfig.position === types.position.top.value ||
+                $scope.legendConfig.position === types.position.bottom.value) {
+                layoutType = 'column';
+            } else {
+                layoutType = 'row';
+            }
+
+            var legendStyle;
+            switch($scope.legendConfig.position) {
+                case types.position.top.value:
+                    legendStyle = 'padding-bottom: 8px;';
+                    break;
+                case types.position.bottom.value:
+                    legendStyle = 'padding-top: 8px;';
+                    break;
+                case types.position.left.value:
+                    legendStyle = 'padding-right: 0px;';
+                    break;
+                case types.position.right.value:
+                    legendStyle = 'padding-left: 0px;';
+                    break;
+            }
+
+            var legendHtml = '<tb-legend style="'+legendStyle+'" legend-config="legendConfig" legend-data="legendData"></tb-legend>';
+            containerHtml = '<div flex id="widget-container">' + containerHtml + '</div>';
+            html += '<div class="tb-absolute-fill" layout="'+layoutType+'">';
+            if ($scope.legendConfig.position === types.position.top.value ||
+                $scope.legendConfig.position === types.position.left.value) {
+                html += legendHtml;
+                html += containerHtml;
+            } else {
+                html += containerHtml;
+                html += legendHtml;
+            }
+            html += '</div>';
+        } else {
+            html += containerHtml;
+        }
+
+        //TODO:
+        /*if (progressElement) {
+         progressScope.$destroy();
+         progressScope = null;
+
+         progressElement.remove();
+         progressElement = null;
+         }*/
+
+        $element.html(html);
+
+        var containerElement = $scope.displayLegend ? angular.element($element[0].querySelector('#widget-container')) : $element;
+        widgetContext.$container = $('#container', containerElement);
+        widgetContext.$containerParent = $(containerElement);
+
+        $compile($element.contents())($scope);
+
+        addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
+    }
+
+    function destroyWidgetElement() {
+        removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
+        $element.html('');
+        widgetContext.$container = null;
+        widgetContext.$containerParent = null;
+    }
 
     function initialize() {
 
@@ -407,8 +454,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
             onEditModeChanged(isEdit);
         });
 
-        addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
-
         $scope.$watch(function () {
             return widget.row + ',' + widget.col + ',' + widget.config.mobileOrder;
         }, function () {
@@ -439,10 +484,10 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
         });
 
         $scope.$on("$destroy", function () {
-            removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
             onDestroy();
         });
 
+        configureWidgetElement();
         var deferred = $q.defer();
         if (!vm.useCustomDatasources) {
             createDefaultSubscription().then(
@@ -465,6 +510,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
 
     function reInit() {
         onDestroy();
+        configureWidgetElement();
         if (!vm.useCustomDatasources) {
             createDefaultSubscription().then(
                 function success() {
@@ -636,6 +682,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
                 handleWidgetException(e);
             }
         }
+        destroyWidgetElement();
     }
 
     //TODO: widgets visibility
diff --git a/ui/src/app/components/widget.directive.js b/ui/src/app/components/widget.directive.js
index c92e544..a0f1b83 100644
--- a/ui/src/app/components/widget.directive.js
+++ b/ui/src/app/components/widget.directive.js
@@ -28,7 +28,7 @@ export default angular.module('thingsboard.directives.widget', [thingsboardLegen
     .name;
 
 /*@ngInject*/
-function Widget($controller, $compile, types, widgetService) {
+function Widget($controller, widgetService) {
     return {
         scope: true,
         link: function (scope, elem, attrs) {
@@ -81,90 +81,9 @@ function Widget($controller, $compile, types, widgetService) {
 
                 elem.addClass(widgetNamespace);
 
-                var html = '<div class="tb-absolute-fill tb-widget-error" ng-if="widgetErrorData">' +
-                    '<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' +
-                    '</div>' +
-                    '<div class="tb-absolute-fill tb-widget-loading" ng-show="loadingData" layout="column" layout-align="center center">' +
-                    '<md-progress-circular md-mode="indeterminate" ng-disabled="!loadingData" class="md-accent" md-diameter="40"></md-progress-circular>' +
-                    '</div>';
-
-                scope.displayLegend = angular.isDefined(widget.config.showLegend) ?
-                    widget.config.showLegend : widget.type === types.widgetType.timeseries.value;
-
-
-                var containerHtml = '<div id="container">' + widgetInfo.templateHtml + '</div>';
-                if (scope.displayLegend) {
-                    scope.legendConfig = widget.config.legendConfig ||
-                        {
-                            position: types.position.bottom.value,
-                            showMin: false,
-                            showMax: false,
-                            showAvg: widget.type === types.widgetType.timeseries.value,
-                            showTotal: false
-                        };
-                    scope.legendData = {
-                        keys: [],
-                        data: []
-                    };
-
-                    var layoutType;
-                    if (scope.legendConfig.position === types.position.top.value ||
-                        scope.legendConfig.position === types.position.bottom.value) {
-                        layoutType = 'column';
-                    } else {
-                        layoutType = 'row';
-                    }
-
-                    var legendStyle;
-                    switch(scope.legendConfig.position) {
-                        case types.position.top.value:
-                            legendStyle = 'padding-bottom: 8px;';
-                            break;
-                        case types.position.bottom.value:
-                            legendStyle = 'padding-top: 8px;';
-                            break;
-                        case types.position.left.value:
-                            legendStyle = 'padding-right: 0px;';
-                            break;
-                        case types.position.right.value:
-                            legendStyle = 'padding-left: 0px;';
-                            break;
-                    }
-
-                    var legendHtml = '<tb-legend style="'+legendStyle+'" legend-config="legendConfig" legend-data="legendData"></tb-legend>';
-                    containerHtml = '<div flex id="widget-container">' + containerHtml + '</div>';
-                    html += '<div class="tb-absolute-fill" layout="'+layoutType+'">';
-                    if (scope.legendConfig.position === types.position.top.value ||
-                        scope.legendConfig.position === types.position.left.value) {
-                        html += legendHtml;
-                        html += containerHtml;
-                    } else {
-                        html += containerHtml;
-                        html += legendHtml;
-                    }
-                    html += '</div>';
-                } else {
-                    html += containerHtml;
-                }
-
-                //TODO:
-                /*if (progressElement) {
-                    progressScope.$destroy();
-                    progressScope = null;
-
-                    progressElement.remove();
-                    progressElement = null;
-                }*/
-
-                elem.html(html);
-
-                var containerElement = scope.displayLegend ? angular.element(elem[0].querySelector('#widget-container')) : elem;
-
-                $compile(elem.contents())(scope);
-
                 var widgetType = widgetService.getWidgetTypeFunction(widget.bundleAlias, widget.typeAlias, widget.isSystemType);
 
-                angular.extend(locals, {$scope: scope, $element: containerElement, widgetType: widgetType});
+                angular.extend(locals, {$scope: scope, $element: elem, widgetInfo: widgetInfo, widgetType: widgetType});
 
                 widgetController = $controller('WidgetController', locals);
 
diff --git a/ui/src/app/entity/aliases-entity-select.directive.js b/ui/src/app/entity/aliases-entity-select.directive.js
index 7e174ef..9a61311 100644
--- a/ui/src/app/entity/aliases-entity-select.directive.js
+++ b/ui/src/app/entity/aliases-entity-select.directive.js
@@ -94,6 +94,14 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $
             $mdPanel.open(config);
         }
 
+        scope.$on('entityAliasesChanged', function() {
+            scope.updateView();
+        });
+
+        scope.$on('entityAliasResolved', function() {
+            scope.updateView();
+        });
+
         scope.updateView = function () {
             updateDisplayValue();
         }
diff --git a/ui/src/app/entity/aliases-entity-select-panel.controller.js b/ui/src/app/entity/aliases-entity-select-panel.controller.js
index 8439269..5365db7 100644
--- a/ui/src/app/entity/aliases-entity-select-panel.controller.js
+++ b/ui/src/app/entity/aliases-entity-select-panel.controller.js
@@ -15,7 +15,7 @@
  */
 
 /*@ngInject*/
-export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, types, aliasController, onEntityAliasesUpdate) {
+export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, $filter, types, aliasController, onEntityAliasesUpdate) {
 
     var vm = this;
     vm._mdPanelRef = mdPanelRef;
@@ -31,13 +31,18 @@ export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, t
         var aliasInfo = vm.aliasController.getInstantAliasInfo(aliasId);
         if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity) {
             vm.entityAliasesInfo[aliasId] = angular.copy(aliasInfo);
+            vm.entityAliasesInfo[aliasId].selectedId = aliasInfo.currentEntity.id;
         }
     }
 
-    function currentAliasEntityChanged(aliasId, currentEntity) {
-        vm.aliasController.updateCurrentAliasEntity(aliasId, currentEntity);
-        if (onEntityAliasesUpdate) {
-            onEntityAliasesUpdate();
+    function currentAliasEntityChanged(aliasId, selectedId) {
+        var resolvedEntities = vm.entityAliasesInfo[aliasId].resolvedEntities;
+        var selected = $filter('filter')(resolvedEntities, {id: selectedId});
+        if (selected && selected.length) {
+            vm.aliasController.updateCurrentAliasEntity(aliasId, selected[0]);
+            if (onEntityAliasesUpdate) {
+                onEntityAliasesUpdate();
+            }
         }
     }
 
diff --git a/ui/src/app/entity/aliases-entity-select-panel.tpl.html b/ui/src/app/entity/aliases-entity-select-panel.tpl.html
index edc8e5a..d197d0b 100644
--- a/ui/src/app/entity/aliases-entity-select-panel.tpl.html
+++ b/ui/src/app/entity/aliases-entity-select-panel.tpl.html
@@ -21,8 +21,8 @@
             <div flex layout="row" ng-repeat="(aliasId, entityAliasInfo) in vm.entityAliasesInfo">
                 <md-input-container flex>
                     <label>{{entityAliasInfo.alias}}</label>
-                    <md-select ng-model="entityAliasInfo.currentEntity" ng-change="vm.currentAliasEntityChanged(aliasId, entityAliasInfo.currentEntity)">
-                        <md-option ng-repeat="resolvedEntity in entityAliasInfo.resolvedEntities" ng-value="resolvedEntity">
+                    <md-select ng-model="entityAliasInfo.selectedId" ng-change="vm.currentAliasEntityChanged(aliasId, entityAliasInfo.selectedId)">
+                        <md-option ng-repeat="resolvedEntity in entityAliasInfo.resolvedEntities" ng-value="resolvedEntity.id">
                             {{resolvedEntity.name}}
                         </md-option>
                     </md-select>
diff --git a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
index b28a46b..bcf37dd 100644
--- a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
+++ b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
@@ -22,7 +22,7 @@ import selectTargetLayoutTemplate from '../../dashboard/layouts/select-target-la
 /* eslint-enable import/no-unresolved, import/default */
 
 /*@ngInject*/
-export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document,
+export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document, dashboardUtils,
                                                              types, itembuffer, dashboardService, entityId, entityType, entityName, widget) {
 
     var vm = this;
@@ -126,15 +126,8 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog, 
             targetDeviceAliases: {}
         };
         aliasesInfo.datasourceAliases[0] = {
-            id: 1,
             alias: entityName,
-            filter: {
-                type: types.aliasFilterType.entityList.value,
-                stateEntity: false,
-                entityList: [entityId],
-                entityType: entityType,
-                resolveMultiple: false
-            }
+            filter: dashboardUtils.createSingleEntityFilter(entityType, entityId)
         };
         itembuffer.addWidgetToDashboard(theDashboard, targetState, targetLayout, vm.widget, aliasesInfo, null, 48, null, -1, -1).then(
             function(theDashboard) {
diff --git a/ui/src/app/entity/attribute/attribute-table.directive.js b/ui/src/app/entity/attribute/attribute-table.directive.js
index da7697d..62d2081 100644
--- a/ui/src/app/entity/attribute/attribute-table.directive.js
+++ b/ui/src/app/entity/attribute/attribute-table.directive.js
@@ -26,10 +26,12 @@ import editAttributeValueTemplate from './edit-attribute-value.tpl.html';
 /* eslint-enable import/no-unresolved, import/default */
 
 import EditAttributeValueController from './edit-attribute-value.controller';
+import AliasController from '../../api/alias-controller';
 
 /*@ngInject*/
 export default function AttributeTableDirective($compile, $templateCache, $rootScope, $q, $mdEditDialog, $mdDialog,
-                                                $document, $translate, $filter, utils, types, dashboardService, attributeService, widgetService) {
+                                                $mdUtil, $document, $translate, $filter, utils, types, dashboardUtils,
+                                                dashboardService, entityService, attributeService, widgetService) {
 
     var linker = function (scope, element, attrs) {
 
@@ -246,15 +248,19 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
         }
 
         scope.nextWidget = function() {
-            if (scope.widgetsCarousel.index < scope.widgetsList.length-1) {
-                scope.widgetsCarousel.index++;
-            }
+            $mdUtil.nextTick(function () {
+                if (scope.widgetsCarousel.index < scope.widgetsList.length - 1) {
+                    scope.widgetsCarousel.index++;
+                }
+            });
         }
 
         scope.prevWidget = function() {
-            if (scope.widgetsCarousel.index > 0) {
-                scope.widgetsCarousel.index--;
-            }
+            $mdUtil.nextTick(function () {
+                if (scope.widgetsCarousel.index > 0) {
+                    scope.widgetsCarousel.index--;
+                }
+            });
         }
 
         scope.enterWidgetMode = function() {
@@ -281,23 +287,28 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
             scope.firstBundle = true;
             scope.selectedWidgetsBundleAlias = types.systemBundleAlias.cards;
 
-            scope.aliasesInfo = {
-                entityAliases: {
-                    '1': {alias: scope.entityName, entityType: scope.entityType, entityId: scope.entityId}
-                },
-                entityAliasesInfo: {
-                    '1': [
-                        {name: scope.entityName, entityType: scope.entityType, id: scope.entityId}
-                    ]
+            var entityAlias = {
+                id: utils.guid(),
+                alias: scope.entityName,
+                filter: dashboardUtils.createSingleEntityFilter(scope.entityType, scope.entityId)
+            };
+            var entitiAliases = {};
+            entitiAliases[entityAlias.id] = entityAlias;
+
+            var stateController = {
+                getStateParams: function() {
+                    return {};
                 }
             };
+            scope.aliasController = new AliasController(scope, $q, $filter, utils,
+                types, entityService, stateController, entitiAliases);
 
             var dataKeyType = scope.attributeScope === types.latestTelemetry ?
                 types.dataKeyType.timeseries : types.dataKeyType.attribute;
 
             var datasource = {
                 type: types.datasourceType.entity,
-                entityAliasId: '1',
+                entityAliasId: entityAlias.id,
                 dataKeys: []
             }
             var i = 0;
diff --git a/ui/src/app/entity/attribute/attribute-table.tpl.html b/ui/src/app/entity/attribute/attribute-table.tpl.html
index 1afd1bb..2566a08 100644
--- a/ui/src/app/entity/attribute/attribute-table.tpl.html
+++ b/ui/src/app/entity/attribute/attribute-table.tpl.html
@@ -156,7 +156,7 @@
             rn-swipe-disabled="true">
             <li ng-repeat="widgets in widgetsList">
                 <tb-dashboard
-                        aliases-info="aliasesInfo"
+                        alias-controller="aliasController"
                         widgets="widgets"
                         get-st-diff="getServerTimeDiff()"
                         columns="20"
diff --git a/ui/src/app/entity/entity-aliases.controller.js b/ui/src/app/entity/entity-aliases.controller.js
index 088f8b9..50bbfd9 100644
--- a/ui/src/app/entity/entity-aliases.controller.js
+++ b/ui/src/app/entity/entity-aliases.controller.js
@@ -113,12 +113,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
     }
 
     function addAlias() {
-        var aliasId = 0;
-        for (var a in vm.entityAliases) {
-            aliasId = Math.max(vm.entityAliases[a].id, aliasId);
-        }
-        aliasId++;
-        var entityAlias = {id: aliasId, alias: '', filter: {}, changed: false};
+        var entityAlias = {id: utils.guid(), alias: '', filter: {}, changed: false};
         vm.entityAliases.push(entityAlias);
     }
 
@@ -162,23 +157,21 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
         var uniqueAliasList = {};
 
         var valid = true;
-        var aliasId, maxAliasId;
+        var aliasId;
         var alias;
         var i;
 
         if (vm.isSingleEntityAlias) {
-            maxAliasId = 0;
+            if (!vm.singleEntityAlias.id) {
+                vm.singleEntityAlias.id = utils.guid();
+            }
             for (i = 0; i < vm.entityAliases.length; i ++) {
-                aliasId = vm.entityAliases[i].id;
                 alias = vm.entityAliases[i].alias;
                 if (alias === vm.singleEntityAlias.alias) {
                     valid = false;
                     break;
                 }
-                maxAliasId = Math.max(aliasId, maxAliasId);
             }
-            maxAliasId++;
-            vm.singleEntityAlias.id = maxAliasId;
         } else {
             for (i = 0; i < vm.entityAliases.length; i++) {
                 aliasId = vm.entityAliases[i].id;
diff --git a/ui/src/app/entity/entity-aliases.scss b/ui/src/app/entity/entity-aliases.scss
index b108dc0..4180bce 100644
--- a/ui/src/app/entity/entity-aliases.scss
+++ b/ui/src/app/entity/entity-aliases.scss
@@ -18,11 +18,20 @@
   .md-dialog-content {
     padding-bottom: 0px;
   }
+  .tb-aliases-header {
+    min-height: 40px;
+    padding: 0 34px 0 34px;
+    margin: 5px;
+    .tb-header-label {
+      font-size: 14px;
+      color: rgba(0, 0, 0, 0.570588);
+    }
+  }
   .tb-alias {
-    padding: 10px 0 0 10px;
+    padding: 0 0 0 10px;
     margin: 5px;
-    md-select.tb-entity-type-select {
-      padding-bottom: 24px;
+    md-input-container {
+      margin: 0px;
     }
   }
 }
diff --git a/ui/src/app/entity/entity-aliases.tpl.html b/ui/src/app/entity/entity-aliases.tpl.html
index ef8b032..4fd33d9 100644
--- a/ui/src/app/entity/entity-aliases.tpl.html
+++ b/ui/src/app/entity/entity-aliases.tpl.html
@@ -28,23 +28,28 @@
 		</md-toolbar>
 		<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
 		<span style="min-height: 5px;" flex="" ng-show="!loading"></span>
+		<div class="tb-aliases-header" ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center">
+			<span flex="5"></span>
+			<div flex layout="row" layout-align="start center">
+				<span class="tb-header-label" translate flex="20" style="min-width: 150px;">entity.alias</span>
+				<div flex="80" layout="row" layout-align="start center" style="min-width: 240px; padding-left: 10px;">
+					<span class="tb-header-label" translate flex="70">alias.entity-filter</span>
+					<span class="tb-header-label" translate flex="30" style="padding-left: 10px;" >alias.resolve-multiple</span>
+				</div>
+				<span style="min-width: 40px; margin: 0 6px;"></span>
+			</div>
+		</div>
+		<md-divider ng-show="!vm.isSingleEntityAlias"></md-divider>
 		<md-dialog-content>
 			<div class="md-dialog-content">
 				<fieldset ng-disabled="loading">
 					<div ng-show="vm.isSingleEntityAlias" layout="row">
-						<tb-entity-filter flex allowed-entity-types="vm.allowedEntityTypes" ng-model="vm.singleEntityAlias.filter">
+						<tb-entity-filter flex
+										  allowed-entity-types="vm.allowedEntityTypes"
+										  ng-model="vm.singleEntityAlias.filter">
 						</tb-entity-filter>
 					</div>
-					<div ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center">
-						<span flex="5"></span>
-						<div flex layout="row" layout-align="start center"
-							 style="padding: 0 0 0 10px; margin: 5px;">
-							<span translate flex="20" style="min-width: 150px;">entity.alias</span>
-							<span translate flex="80" style="min-width: 240px; padding-left: 10px;">alias.entity-filter</span>
-							<span style="min-width: 40px;"></span>
-						</div>
-					</div>
-					<div ng-show="!vm.isSingleEntityAlias" style="max-height: 500px; overflow: auto; padding-bottom: 20px;">
+					<div ng-show="!vm.isSingleEntityAlias" style="height: 100%; overflow: auto; padding-bottom: 20px;">
 						<div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">
 							<span flex="5">{{$index + 1}}.</span>
 							<div class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center">
@@ -54,13 +59,12 @@
 										<div translate ng-message="required">entity.alias-required</div>
 									</div>
 								</md-input-container>
-								<section flex="80" layout="column">
-									<tb-entity-filter style="padding-left: 10px;"
-													  allowed-entity-types="vm.allowedEntityTypes"
-													  ng-model="entityAlias.filter"
-													  on-matching-entity-change="vm.onFilterEntityChanged(entity, stateEntity, entityAlias)">
-									</tb-entity-filter>
-								</section>
+								<tb-entity-filter flex="80" style="min-width: 240px; padding-left: 10px;"
+												  hide-labels
+												  allowed-entity-types="vm.allowedEntityTypes"
+												  ng-model="entityAlias.filter"
+												  on-matching-entity-change="vm.onFilterEntityChanged(entity, stateEntity, entityAlias)">
+								</tb-entity-filter>
 								<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
 										   ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}">
 									<md-tooltip md-direction="top">
@@ -73,18 +77,18 @@
 							</div>
 						</div>
 					</div>
-					<div ng-show="!vm.isSingleEntityAlias && !vm.disableAdd" style="padding-bottom: 10px;">
-						<md-button ng-disabled="loading" class="md-primary md-raised" ng-click="vm.addAlias($event)" aria-label="{{ 'action.add' | translate }}">
-							<md-tooltip md-direction="top">
-								{{ 'entity.add-alias' | translate }}
-							</md-tooltip>
-							<span translate>action.add</span>
-						</md-button>
-					</div>
 				</fieldset>
 			</div>
 		</md-dialog-content>
 		<md-dialog-actions layout="row">
+			<md-button ng-show="!vm.isSingleEntityAlias && !vm.disableAdd" ng-disabled="loading" class="md-primary md-raised"
+					   ng-click="vm.addAlias($event)"
+					   aria-label="{{ 'action.add' | translate }}">
+				<md-tooltip md-direction="top">
+					{{ 'entity.add-alias' | translate }}
+				</md-tooltip>
+				<span translate>action.add</span>
+			</md-button>
 			<span flex></span>
 			<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
 				{{ 'action.save' | translate }}
diff --git a/ui/src/app/entity/entity-filter.directive.js b/ui/src/app/entity/entity-filter.directive.js
index beded8a..0f9cf5c 100644
--- a/ui/src/app/entity/entity-filter.directive.js
+++ b/ui/src/app/entity/entity-filter.directive.js
@@ -35,46 +35,16 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
 
         scope.ngModelCtrl = ngModelCtrl;
         scope.types = types;
-
-     /*   scope.fetchEntities = function(searchText, limit) {
-            var deferred = $q.defer();
-            entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(function success(result) {
-                if (result) {
-                    deferred.resolve(result);
-                } else {
-                    deferred.resolve([]);
-                }
-            }, function fail() {
-                deferred.reject();
-            });
-            return deferred.promise;
-        }*/
+        scope.hideLabels = angular.isDefined(attrs.hideLabels);
 
         scope.updateValidity = function() {
             if (ngModelCtrl.$viewValue) {
                 var value = ngModelCtrl.$viewValue;
                 ngModelCtrl.$setValidity('filter', value.type ? true : false);
-                /*if (value.useFilter) {
-                    ngModelCtrl.$setValidity('entityList', true);
-                    if (angular.isDefined(value.entityNameFilter) && value.entityNameFilter.length > 0) {
-                        ngModelCtrl.$setValidity('entityNameFilter', true);
-                        valid = angular.isDefined(scope.model.matchingFilterEntity) && scope.model.matchingFilterEntity != null;
-                        ngModelCtrl.$setValidity('entityNameFilterEntityMatch', valid);
-                    } else {
-                        ngModelCtrl.$setValidity('entityNameFilter', false);
-                    }
-                } else {
-                    ngModelCtrl.$setValidity('entityNameFilter', true);
-                    ngModelCtrl.$setValidity('entityNameFilterDeviceMatch', true);
-                    valid = angular.isDefined(value.entityList) && value.entityList.length > 0;
-                    ngModelCtrl.$setValidity('entityList', valid);
-                }*/
-
             }
         }
 
         ngModelCtrl.$render = function () {
-            //destroyWatchers();
             if (ngModelCtrl.$viewValue) {
                 scope.model = angular.copy(ngModelCtrl.$viewValue);
             } else {
@@ -83,28 +53,6 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
                     resolveMultiple: false
                 }
             }
-           /* if (ngModelCtrl.$viewValue) {
-                var value = ngModelCtrl.$viewValue;
-                var model = scope.model;
-                model.useFilter = value.useFilter === true ? true: false;
-                model.entityList = [];
-                model.entityNameFilter = value.entityNameFilter || '';
-                processEntityNameFilter(model.entityNameFilter).then(
-                    function(entity) {
-                        scope.model.matchingFilterEntity = entity;
-                        if (value.entityList && value.entityList.length > 0) {
-                            entityService.getEntities(scope.entityType, value.entityList).then(function (entities) {
-                                model.entityList = entities;
-                                updateMatchingEntity();
-                                initWatchers();
-                            });
-                        } else {
-                            updateMatchingEntity();
-                            initWatchers();
-                        }
-                    }
-                )
-            }*/
         }
 
         scope.$watch('model.resolveMultiple', function () {
@@ -149,115 +97,6 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
             });
         }
 
-  /*      function updateMatchingEntity() {
-            if (scope.model.useFilter) {
-                scope.model.matchingEntity = scope.model.matchingFilterEntity;
-            } else {
-                if (scope.model.entityList && scope.model.entityList.length > 0) {
-                    scope.model.matchingEntity = scope.model.entityList[0];
-                } else {
-                    scope.model.matchingEntity = null;
-                }
-            }
-        }
-
-        function processEntityNameFilter(entityNameFilter) {
-            var deferred = $q.defer();
-            if (angular.isDefined(entityNameFilter) && entityNameFilter.length > 0) {
-                scope.fetchEntities(entityNameFilter, 1).then(function (entities) {
-                    if (entities && entities.length > 0) {
-                        deferred.resolve(entities[0]);
-                    } else {
-                        deferred.resolve(null);
-                    }
-                });
-            } else {
-                deferred.resolve(null);
-            }
-            return deferred.promise;
-        }
-
-        function destroyWatchers() {
-            if (scope.entityTypeDeregistration) {
-                scope.entityTypeDeregistration();
-                scope.entityTypeDeregistration = null;
-            }
-            if (scope.entityListDeregistration) {
-                scope.entityListDeregistration();
-                scope.entityListDeregistration = null;
-            }
-            if (scope.useFilterDeregistration) {
-                scope.useFilterDeregistration();
-                scope.useFilterDeregistration = null;
-            }
-            if (scope.entityNameFilterDeregistration) {
-                scope.entityNameFilterDeregistration();
-                scope.entityNameFilterDeregistration = null;
-            }
-            if (scope.matchingEntityDeregistration) {
-                scope.matchingEntityDeregistration();
-                scope.matchingEntityDeregistration = null;
-            }
-        }
-
-        function initWatchers() {
-
-            scope.entityTypeDeregistration = scope.$watch('entityType', function (newEntityType, prevEntityType) {
-                if (!angular.equals(newEntityType, prevEntityType)) {
-                    scope.model.entityList = [];
-                    scope.model.entityNameFilter = '';
-                }
-            });
-
-            scope.entityListDeregistration = scope.$watch('model.entityList', function () {
-                if (ngModelCtrl.$viewValue) {
-                    var value = ngModelCtrl.$viewValue;
-                    value.entityList = [];
-                    if (scope.model.entityList && scope.model.entityList.length > 0) {
-                        for (var i=0;i<scope.model.entityList.length;i++) {
-                            value.entityList.push(scope.model.entityList[i].id.id);
-                        }
-                    }
-                    updateMatchingEntity();
-                    ngModelCtrl.$setViewValue(value);
-                    scope.updateValidity();
-                }
-            }, true);
-            scope.useFilterDeregistration = scope.$watch('model.useFilter', function () {
-                if (ngModelCtrl.$viewValue) {
-                    var value = ngModelCtrl.$viewValue;
-                    value.useFilter = scope.model.useFilter;
-                    updateMatchingEntity();
-                    ngModelCtrl.$setViewValue(value);
-                    scope.updateValidity();
-                }
-            });
-            scope.entityNameFilterDeregistration = scope.$watch('model.entityNameFilter', function (newNameFilter, prevNameFilter) {
-                if (ngModelCtrl.$viewValue) {
-                    if (!angular.equals(newNameFilter, prevNameFilter)) {
-                        var value = ngModelCtrl.$viewValue;
-                        value.entityNameFilter = scope.model.entityNameFilter;
-                        processEntityNameFilter(value.entityNameFilter).then(
-                            function(entity) {
-                                scope.model.matchingFilterEntity = entity;
-                                updateMatchingEntity();
-                                ngModelCtrl.$setViewValue(value);
-                                scope.updateValidity();
-                            }
-                        );
-                    }
-                }
-            });
-
-            scope.matchingEntityDeregistration = scope.$watch('model.matchingEntity', function (newMatchingEntity, prevMatchingEntity) {
-                if (!angular.equals(newMatchingEntity, prevMatchingEntity)) {
-                    if (scope.onMatchingEntityChange) {
-                        scope.onMatchingEntityChange({entity: newMatchingEntity});
-                    }
-                }
-            });
-        }*/
-
         $compile(element.contents())(scope);
 
     }
diff --git a/ui/src/app/entity/entity-filter.scss b/ui/src/app/entity/entity-filter.scss
index 1525842..10ddcd3 100644
--- a/ui/src/app/entity/entity-filter.scss
+++ b/ui/src/app/entity/entity-filter.scss
@@ -14,18 +14,6 @@
  * limitations under the License.
  */
 .tb-entity-filter {
-  #entity_list_chips {
-    .md-chips {
-      padding-bottom: 1px;
-    }
-  }
-  .entity-name-filter-input {
-    margin-top: 10px;
-    margin-bottom: 0px;
-    .md-errors-spacer {
-      min-height: 0px;
-    }
-  }
   .tb-filter-switch {
     padding-left: 10px;
     .filter-switch {
@@ -39,7 +27,8 @@
     margin-top: -11px;
     height: 35px;
     .tb-error-message {
-      padding-left: 1px;
+      padding-left: 8px;
+      padding-top: 14px;
     }
   }
 }
\ No newline at end of file
diff --git a/ui/src/app/entity/entity-filter.tpl.html b/ui/src/app/entity/entity-filter.tpl.html
index 9a5337a..6ce95fa 100644
--- a/ui/src/app/entity/entity-filter.tpl.html
+++ b/ui/src/app/entity/entity-filter.tpl.html
@@ -15,80 +15,37 @@
     limitations under the License.
 
 -->
-<section layout='column' class="tb-entity-filter">
-    <section layout='row'>
-        <!--section layout="column" flex ng-show="!model.useFilter">
-            <md-chips flex
-                      id="entity_list_chips"
-                      ng-required="!useFilter"
-                      ng-model="model.entityList" md-autocomplete-snap
-                      md-require-match="true">
-                <md-autocomplete
-                        md-no-cache="true"
-                        id="entity"
-                        md-selected-item="selectedEntity"
-                        md-search-text="entitySearchText"
-                        md-items="item in fetchEntities(entitySearchText, 10)"
-                        md-item-text="item.name"
-                        md-min-length="0"
-                        placeholder="{{ 'entity.entity-list' | translate }}">
-                        <md-item-template>
-                            <span md-highlight-text="entitySearchText" md-highlight-flags="^i">{{item.name}}</span>
-                        </md-item-template>
-                        <md-not-found>
-                            <span translate translate-values='{ entity: entitySearchText }'>entity.no-entities-matching</span>
-                        </md-not-found>
-                </md-autocomplete>
-                <md-chip-template>
-                    <span>
-                      <strong>{{$chip.name}}</strong>
-                    </span>
-                </md-chip-template>
-            </md-chips>
-        </section>
-        <section layout="row" flex ng-show="model.useFilter">
-            <md-input-container flex class="entity-name-filter-input">
-                <label translate>entity.name-starts-with</label>
-                <input ng-model="model.entityNameFilter" aria-label="{{ 'entity.name-starts-with' | translate }}">
-            </md-input-container>
-        </section-->
-        <section layout="row" flex layout-align="start center">
-            <div flex ng-if="model.type">{{ types.aliasFilterType[model.type].name | translate }}</div>
-            <md-button ng-if="model.type" ng-disabled="loading" class="md-icon-button md-primary"
-                       style="min-width: 40px;"
-                       ng-click="editFilter($event)"
-                       aria-label="{{ 'alias.edit-entity-filter' | translate }}">
-                <md-tooltip md-direction="top">
-                    {{ 'alias.edit-entity-filter' | translate }}
-                </md-tooltip>
-                <md-icon aria-label="{{ 'alias.edit-entity-filter' | translate }}"
+<section layout='row' class="tb-entity-filter">
+    <section layout="row" flex="70">
+        <section flex layout="column" layout-align="center start">
+            <div ng-if="model.type">{{ types.aliasFilterType[model.type].name | translate }}</div>
+            <md-button ng-if="!model.type"
+                       ng-disabled="loading" class="md-primary"
+                       ng-click="createFilter($event)"
+                       aria-label="{{ 'alias.create-entity-filter' | translate }}">
+                <md-icon aria-label="{{ 'alias.create-entity-filter' | translate }}"
                          class="material-icons">
-                    edit
+                    add
                 </md-icon>
+                {{ 'alias.create-entity-filter' | translate }}
             </md-button>
-            <div ng-if="!model.type" layout="row" layout-align="center start">
-                <md-button ng-disabled="loading" class="md-primary md-raised"
-                           ng-click="createFilter($event)"
-                           aria-label="{{ 'alias.create-entity-filter' | translate }}">
-                    <md-icon aria-label="{{ 'alias.create-entity-filter' | translate }}"
-                             class="material-icons">
-                        add
-                    </md-icon>
-                    {{ 'alias.create-entity-filter' | translate }}
-                </md-button>
-            </div>
-        </section>
-        <section class="tb-filter-switch" layout="column" layout-align="center center">
-            <label class="tb-small filter-label" translate>alias.resolve-multiple</label>
-            <md-switch class="filter-switch" ng-model="model.resolveMultiple" aria-label="resolve-multiple-switcher">
-            </md-switch>
         </section>
+        <md-button ng-if="model.type" ng-disabled="loading" class="md-icon-button md-primary"
+                   style="min-width: 40px;"
+                   ng-click="editFilter($event)"
+                   aria-label="{{ 'alias.edit-entity-filter' | translate }}">
+            <md-tooltip md-direction="top">
+                {{ 'alias.edit-entity-filter' | translate }}
+            </md-tooltip>
+            <md-icon aria-label="{{ 'alias.edit-entity-filter' | translate }}"
+                     class="material-icons">
+                edit
+            </md-icon>
+        </md-button>
+    </section>
+    <section class="tb-filter-switch" layout="column" flex="30" layout-align="center center">
+        <label ng-if="!hideLabels" class="tb-small filter-label" translate>alias.resolve-multiple</label>
+        <md-switch class="filter-switch" ng-model="model.resolveMultiple" aria-label="resolve-multiple-switcher">
+        </md-switch>
     </section>
-    <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert">
-        <div translate ng-message="filter" class="tb-error-message">alias.entity-filter-required</div>
-        <!--div translate ng-message="entityList" class="tb-error-message">entity.entity-list-empty</div>
-        <div translate ng-message="entityNameFilter" class="tb-error-message">entity.entity-name-filter-required</div>
-        <div translate translate-values='{ entity: model.entityNameFilter }' ng-message="entityNameFilterEntityMatch"
-             class="tb-error-message">entity.entity-name-filter-no-entity-matched</div-->
-    </div>
-</section>
\ No newline at end of file
+</section>
diff --git a/ui/src/app/import-export/import-export.service.js b/ui/src/app/import-export/import-export.service.js
index 7f7f774..9b36c38 100644
--- a/ui/src/app/import-export/import-export.service.js
+++ b/ui/src/app/import-export/import-export.service.js
@@ -24,7 +24,7 @@ import entityAliasesTemplate from '../entity/entity-aliases.tpl.html';
 /* eslint-disable no-undef, angular/window-service, angular/document-service */
 
 /*@ngInject*/
-export default function ImportExport($log, $translate, $q, $mdDialog, $document, itembuffer, types, dashboardUtils,
+export default function ImportExport($log, $translate, $q, $mdDialog, $document, itembuffer, utils, types, dashboardUtils,
                                      entityService, dashboardService, pluginService, ruleService, widgetService, toast) {
 
 
@@ -415,6 +415,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                     deferred.reject();
                 } else {
                     var widget = widgetItem.widget;
+                    widget = dashboardUtils.validateAndUpdateWidget(widget);
                     var aliasesInfo = prepareAliasesInfo(widgetItem.aliasesInfo);
                     var originalColumns = widgetItem.originalColumns;
                     var originalSize = widgetItem.originalSize;
@@ -425,22 +426,22 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                         var entityAliases = {};
                         var datasourceAliasesMap = {};
                         var targetDeviceAliasesMap = {};
-                        var aliasId = 1;
+                        var aliasId;
                         var datasourceIndex;
                         if (datasourceAliases) {
                             for (datasourceIndex in datasourceAliases) {
+                                aliasId = utils.guid();
                                 datasourceAliasesMap[aliasId] = datasourceIndex;
                                 entityAliases[aliasId] = datasourceAliases[datasourceIndex];
                                 entityAliases[aliasId].id = aliasId;
-                                aliasId++;
                             }
                         }
                         if (targetDeviceAliases) {
                             for (datasourceIndex in targetDeviceAliases) {
+                                aliasId = utils.guid();
                                 targetDeviceAliasesMap[aliasId] = datasourceIndex;
                                 entityAliases[aliasId] = targetDeviceAliases[datasourceIndex];
                                 entityAliases[aliasId].id = aliasId;
-                                aliasId++;
                             }
                         }
 
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index 84c7097..9c6695b 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -124,7 +124,7 @@ export default angular.module('thingsboard.locale', [])
                     "create-entity-filter": "Create entity filter",
                     "edit-entity-filter": "Edit entity filter",
                     "entity-filter-required": "Entity filter is required.",
-                    "resolve-multiple": "Multiple",
+                    "resolve-multiple": "Resolve as multiple entities",
                     "filter-type": "Filter type",
                     "filter-type-required": "Filter type is required.",
                     "use-state-entity": "Use state entity",
diff --git a/ui/src/app/services/item-buffer.service.js b/ui/src/app/services/item-buffer.service.js
index 309e53e..d29ad18 100644
--- a/ui/src/app/services/item-buffer.service.js
+++ b/ui/src/app/services/item-buffer.service.js
@@ -298,11 +298,7 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) {
         }
         if (!newAliasId) {
             var newAliasName = createEntityAliasName(entityAliases, aliasInfo.alias);
-            newAliasId = 0;
-            for (aliasId in entityAliases) {
-                newAliasId = Math.max(newAliasId, aliasId);
-            }
-            newAliasId++;
+            newAliasId = utils.guid();
             entityAliases[newAliasId] = {id: newAliasId, alias: newAliasName, filter: aliasInfo.filter};
         }
         return newAliasId;