thingsboard-developers

Minor UI bug fixes and improvements.

10/23/2017 3:25:03 PM

Details

diff --git a/ui/src/app/api/alarm.service.js b/ui/src/app/api/alarm.service.js
index 6c61491..f59ecbc 100644
--- a/ui/src/app/api/alarm.service.js
+++ b/ui/src/app/api/alarm.service.js
@@ -295,9 +295,8 @@ function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) {
                     if (alarmSourceListener.lastUpdateTs) {
                         var interval = now - alarmSourceListener.lastUpdateTs;
                         alarmSourceListener.alarmsQuery.startTime += interval;
-                    } else {
-                        alarmSourceListener.lastUpdateTs = now;
                     }
+                    alarmSourceListener.lastUpdateTs = now;
                 }
                 alarmSourceListener.alarmsUpdated(alarms, false);
             }
diff --git a/ui/src/app/api/dashboard.service.js b/ui/src/app/api/dashboard.service.js
index 74b68d0..362a539 100644
--- a/ui/src/app/api/dashboard.service.js
+++ b/ui/src/app/api/dashboard.service.js
@@ -63,7 +63,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
         return deferred.promise;
     }
 
-    function getTenantDashboards(pageLink) {
+    function getTenantDashboards(pageLink, applyCustomersInfo) {
         var deferred = $q.defer();
         var url = '/api/tenant/dashboards?limit=' + pageLink.limit;
         if (angular.isDefined(pageLink.textSearch)) {
@@ -76,22 +76,26 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
             url += '&textOffset=' + pageLink.textOffset;
         }
         $http.get(url, null).then(function success(response) {
-            customerService.applyAssignedCustomersInfo(response.data.data).then(
-                function success(data) {
-                    response.data.data = data;
-                    deferred.resolve(response.data);
-                },
-                function fail() {
-                    deferred.reject();
-                }
-            );
+            if (applyCustomersInfo) {
+                customerService.applyAssignedCustomersInfo(response.data.data).then(
+                    function success(data) {
+                        response.data.data = data;
+                        deferred.resolve(response.data);
+                    },
+                    function fail() {
+                        deferred.reject();
+                    }
+                );
+            } else {
+                deferred.resolve(response.data);
+            }
         }, function fail() {
             deferred.reject();
         });
         return deferred.promise;
     }
 
-    function getCustomerDashboards(customerId, pageLink) {
+    function getCustomerDashboards(customerId, pageLink, applyCustomersInfo) {
         var deferred = $q.defer();
         var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit;
         if (angular.isDefined(pageLink.textSearch)) {
@@ -104,15 +108,19 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
             url += '&textOffset=' + pageLink.textOffset;
         }
         $http.get(url, null).then(function success(response) {
-            customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
-                function success(data) {
-                    response.data.data = data;
-                    deferred.resolve(response.data);
-                },
-                function fail() {
-                    deferred.reject();
-                }
-            );
+            if (applyCustomersInfo) {
+                customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
+                    function success(data) {
+                        response.data.data = data;
+                        deferred.resolve(response.data);
+                    },
+                    function fail() {
+                        deferred.reject();
+                    }
+                );
+            } else {
+                deferred.resolve(response.data);
+            }
         }, function fail() {
             deferred.reject();
         });
diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js
index c8a59cf..0022607 100644
--- a/ui/src/app/api/entity.service.js
+++ b/ui/src/app/api/entity.service.js
@@ -267,9 +267,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                 break;
             case types.entityType.dashboard:
                 if (user.authority === 'CUSTOMER_USER') {
-                    promise = dashboardService.getCustomerDashboards(customerId, pageLink);
+                    promise = dashboardService.getCustomerDashboards(customerId, pageLink, false);
                 } else {
-                    promise = dashboardService.getTenantDashboards(pageLink);
+                    promise = dashboardService.getTenantDashboards(pageLink, false);
                 }
                 break;
             case types.entityType.user:
diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js
index 7334165..52b884c 100644
--- a/ui/src/app/api/subscription.js
+++ b/ui/src/app/api/subscription.js
@@ -674,11 +674,14 @@ export default class Subscription {
 
     alarmsUpdated(alarms, apply) {
         this.notifyDataLoaded();
+        var updated = !angular.equals(this.alarms, alarms);
         this.alarms = alarms;
         if (this.subscriptionTimewindow && this.subscriptionTimewindow.realtimeWindowMs) {
             this.updateTimewindow();
         }
-        this.onDataUpdated(apply);
+        if (updated) {
+            this.onDataUpdated(apply);
+        }
     }
 
     updateLegend(dataIndex, data, apply) {
diff --git a/ui/src/app/api/user.service.js b/ui/src/app/api/user.service.js
index 57486b0..1668657 100644
--- a/ui/src/app/api/user.service.js
+++ b/ui/src/app/api/user.service.js
@@ -265,9 +265,9 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
             var pageLink = {limit: 100};
             var fetchDashboardsPromise;
             if (currentUser.authority === 'TENANT_ADMIN') {
-                fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink);
+                fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink, false);
             } else {
-                fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink);
+                fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink, false);
             }
             fetchDashboardsPromise.then(
                 function success(result) {
diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js
index b4912ea..6605692 100644
--- a/ui/src/app/components/dashboard.directive.js
+++ b/ui/src/app/components/dashboard.directive.js
@@ -15,7 +15,6 @@
  */
 import './dashboard.scss';
 
-import $ from 'jquery';
 import 'javascript-detect-element-resize/detect-element-resize';
 import angularGridster from 'angular-gridster';
 import thingsboardTypes from '../common/types.constant';
@@ -94,8 +93,8 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
     var highlightedWidget = null;
     var selectedWidget = null;
 
-    var gridsterParent = $('#gridster-parent', $element);
-    var gridsterElement = angular.element($('#gridster-child', gridsterParent));
+    var gridsterParent = angular.element('#gridster-parent', $element);
+    var gridsterElement = angular.element('#gridster-child', gridsterParent);
 
     var vm = this;
 
@@ -228,15 +227,15 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
         }
     };
 
-    addResizeListener(gridsterParent[0], onGirdsterParentResize); // eslint-disable-line no-undef
+    addResizeListener(gridsterParent[0], onGridsterParentResize); // eslint-disable-line no-undef
 
     $scope.$on("$destroy", function () {
-        removeResizeListener(gridsterParent[0], onGirdsterParentResize); // eslint-disable-line no-undef
+        removeResizeListener(gridsterParent[0], onGridsterParentResize); // eslint-disable-line no-undef
     });
 
     watchWidgets();
 
-    function onGirdsterParentResize() {
+    function onGridsterParentResize() {
         if (gridsterParent.height() && autofillHeight()) {
             updateMobileOpts();
         }
@@ -244,6 +243,10 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
 
     function watchWidgets() {
         $scope.widgetsCollectionWatch = $scope.$watchCollection('vm.widgets', function () {
+            if (vm.skipInitialWidgetsWatch) {
+                $timeout(function() { vm.skipInitialWidgetsWatch = false; });
+                return;
+            }
             var ids = [];
             for (var i=0;i<vm.widgets.length;i++) {
                 var widget = vm.widgets[i];
@@ -524,6 +527,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
             }
             return res;
         });
+        vm.skipInitialWidgetsWatch = true;
         watchWidgets();
     }
 
@@ -714,9 +718,9 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $
 
     function scrollToWidget(widget, delay) {
         if (vm.gridster) {
-            var item = $('.gridster-item', vm.gridster.$element)[vm.widgets.indexOf(widget)];
+            var item = angular.element('.gridster-item', vm.gridster.$element)[vm.widgets.indexOf(widget)];
             if (item) {
-                var height = $(item).outerHeight(true);
+                var height = angular.element(item).outerHeight(true);
                 var rectHeight = gridsterParent.height();
                 var offset = (rectHeight - height) / 2;
                 var scrollTop = item.offsetTop;
diff --git a/ui/src/app/components/dashboard.tpl.html b/ui/src/app/components/dashboard.tpl.html
index 0fe24de..e374fab 100644
--- a/ui/src/app/components/dashboard.tpl.html
+++ b/ui/src/app/components/dashboard.tpl.html
@@ -55,7 +55,7 @@
 											   ng-show="!vm.isEdit"
 											   ng-click="action.onAction($event)"
 											   class="md-icon-button">
-										<md-tooltip md-direction="top">
+										<md-tooltip md-direction="{{vm.isWidgetExpanded ? 'bottom' : 'top'}}">
 											{{action.displayName}}
 										</md-tooltip>
 										<ng-md-icon size="20" icon="{{action.icon}}"></ng-md-icon>
@@ -65,7 +65,7 @@
 											   ng-show="!vm.isEdit && action.show"
 											   ng-click="action.onAction($event)"
 											   class="md-icon-button">
-											   <md-tooltip md-direction="top">
+											   <md-tooltip md-direction="{{vm.isWidgetExpanded ? 'bottom' : 'top'}}">
 												   {{ action.name | translate }}
 											   </md-tooltip>
 											   <ng-md-icon size="20" icon="{{action.icon}}"></ng-md-icon>
@@ -114,7 +114,8 @@
 										 isEdit: vm.isEdit,
 										 isMobile: vm.isMobileSize,
 										 dashboardTimewindow: vm.dashboardTimewindow,
-										 dashboardTimewindowApi: vm.dashboardTimewindowApi }">
+										 dashboardTimewindowApi: vm.dashboardTimewindowApi,
+										 dashboard: vm }">
 									</div>
 								</div>
 							</div>
diff --git a/ui/src/app/components/dashboard-autocomplete.directive.js b/ui/src/app/components/dashboard-autocomplete.directive.js
index 59a56e1..ce7e477 100644
--- a/ui/src/app/components/dashboard-autocomplete.directive.js
+++ b/ui/src/app/components/dashboard-autocomplete.directive.js
@@ -48,7 +48,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u
             var promise;
             if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
                 if (scope.customerId) {
-                    promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink);
+                    promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false);
                 } else {
                     promise = $q.when({data: []});
                 }
@@ -60,7 +60,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u
                         promise = $q.when({data: []});
                     }
                 } else {
-                    promise = dashboardService.getTenantDashboards(pageLink);
+                    promise = dashboardService.getTenantDashboards(pageLink, false);
                 }
             }
 
diff --git a/ui/src/app/components/dashboard-select.directive.js b/ui/src/app/components/dashboard-select.directive.js
index e0de25e..d7b32d8 100644
--- a/ui/src/app/components/dashboard-select.directive.js
+++ b/ui/src/app/components/dashboard-select.directive.js
@@ -48,12 +48,12 @@ function DashboardSelect($compile, $templateCache, $q, $mdMedia, $mdPanel, $docu
         var promise;
         if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
             if (scope.customerId && scope.customerId != types.id.nullUid) {
-                promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink);
+                promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false);
             } else {
                 promise = $q.when({data: []});
             }
         } else {
-            promise = dashboardService.getTenantDashboards(pageLink);
+            promise = dashboardService.getTenantDashboards(pageLink, false);
         }
 
         promise.then(function success(result) {
diff --git a/ui/src/app/components/datasource-entity.tpl.html b/ui/src/app/components/datasource-entity.tpl.html
index e788f30..f2e080e 100644
--- a/ui/src/app/components/datasource-entity.tpl.html
+++ b/ui/src/app/components/datasource-entity.tpl.html
@@ -186,7 +186,7 @@
 				<div translate ng-message="entityKeys" ng-if="widgetType === types.widgetType.latest.value" class="tb-error-message">datakey.timeseries-or-attributes-required</div>
 			    <div translate ng-message="entityKeys" ng-if="widgetType === types.widgetType.alarm.value" class="tb-error-message">datakey.alarm-fields-required</div>
 			</div>
-		   <div class="md-caption" style="color: rgba(0,0,0,0.57);" ng-if="maxDataKeys != -1"
+		   <div class="md-caption" style="color: rgba(0,0,0,0.57);" ng-if="maxDataKeys > -1"
 				translate="datakey.maximum-timeseries-or-attributes"
 				translate-values="{count: maxDataKeys}"
 				translate-interpolation="messageformat"
diff --git a/ui/src/app/components/datasource-func.tpl.html b/ui/src/app/components/datasource-func.tpl.html
index 8a57b07..6fb00c0 100644
--- a/ui/src/app/components/datasource-func.tpl.html
+++ b/ui/src/app/components/datasource-func.tpl.html
@@ -132,7 +132,7 @@
 			<div translate ng-message="datasourceKeys" ng-if="widgetType !== types.widgetType.alarm.value" class="tb-error-message">datakey.function-types-required</div>
 			<div translate ng-message="datasourceKeys" ng-if="widgetType === types.widgetType.alarm.value" class="tb-error-message">datakey.alarm-fields-required</div>
 		</div>
-		<div class="md-caption" style="color: rgba(0,0,0,0.57);" ng-if="maxDataKeys != -1"
+		<div class="md-caption" style="color: rgba(0,0,0,0.57);" ng-if="maxDataKeys > -1"
 			 translate="datakey.maximum-function-types"
 			 translate-values="{count: maxDataKeys}"
 			 translate-interpolation="messageformat"
diff --git a/ui/src/app/components/widget/widget.controller.js b/ui/src/app/components/widget/widget.controller.js
index e5c45d2..7d91aef 100644
--- a/ui/src/app/components/widget/widget.controller.js
+++ b/ui/src/app/components/widget/widget.controller.js
@@ -22,7 +22,7 @@ import Subscription from '../../api/subscription';
 /*@ngInject*/
 export default function WidgetController($scope, $state, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService,
                                          datasourceService, alarmService, entityService, dashboardService, deviceService, visibleRect, isEdit, isMobile, dashboardTimewindow,
-                                         dashboardTimewindowApi, widget, aliasController, stateController, widgetInfo, widgetType) {
+                                         dashboardTimewindowApi, dashboard, widget, aliasController, stateController, widgetInfo, widgetType) {
 
     var vm = this;
 
@@ -67,6 +67,7 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
         hideTitlePanel: false,
         isEdit: isEdit,
         isMobile: isMobile,
+        dashboard: dashboard,
         widgetConfig: widget.config,
         settings: widget.config.settings,
         units: widget.config.units || '',
diff --git a/ui/src/app/dashboard/add-dashboards-to-customer.controller.js b/ui/src/app/dashboard/add-dashboards-to-customer.controller.js
index b0bbef1..6fe6e79 100644
--- a/ui/src/app/dashboard/add-dashboards-to-customer.controller.js
+++ b/ui/src/app/dashboard/add-dashboards-to-customer.controller.js
@@ -52,7 +52,7 @@ export default function AddDashboardsToCustomerController(dashboardService, $mdD
         fetchMoreItems_: function () {
             if (vm.dashboards.hasNext && !vm.dashboards.pending) {
                 vm.dashboards.pending = true;
-                dashboardService.getTenantDashboards(vm.dashboards.nextPageLink).then(
+                dashboardService.getTenantDashboards(vm.dashboards.nextPageLink, false).then(
                     function success(dashboards) {
                         vm.dashboards.data = vm.dashboards.data.concat(dashboards.data);
                         vm.dashboards.nextPageLink = dashboards.nextPageLink;
diff --git a/ui/src/app/dashboard/dashboard.tpl.html b/ui/src/app/dashboard/dashboard.tpl.html
index 8af9081..87f2840 100644
--- a/ui/src/app/dashboard/dashboard.tpl.html
+++ b/ui/src/app/dashboard/dashboard.tpl.html
@@ -15,7 +15,7 @@
     limitations under the License.
 
 -->
-<md-content flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-button-id="dashboard-expand-button"
+<md-content style="padding-top: 150px;" flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-button-id="dashboard-expand-button"
             hide-expand-button="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-tooltip-direction="bottom" ng-if="vm.dashboard">
     <section class="tb-dashboard-toolbar" ng-show="vm.showDashboardToolbar()"
              ng-class="{ 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }">
diff --git a/ui/src/app/dashboard/dashboards.controller.js b/ui/src/app/dashboard/dashboards.controller.js
index 134baff..d2887f8 100644
--- a/ui/src/app/dashboard/dashboards.controller.js
+++ b/ui/src/app/dashboard/dashboards.controller.js
@@ -167,7 +167,7 @@ export function DashboardsController(userService, dashboardService, customerServ
 
         if (vm.dashboardsScope === 'tenant') {
             fetchDashboardsFunction = function (pageLink) {
-                return dashboardService.getTenantDashboards(pageLink);
+                return dashboardService.getTenantDashboards(pageLink, true);
             };
             deleteDashboardFunction = function (dashboardId) {
                 return dashboardService.deleteDashboard(dashboardId);
@@ -290,7 +290,7 @@ export function DashboardsController(userService, dashboardService, customerServ
             });
         } else if (vm.dashboardsScope === 'customer' || vm.dashboardsScope === 'customer_user') {
             fetchDashboardsFunction = function (pageLink) {
-                return dashboardService.getCustomerDashboards(customerId, pageLink);
+                return dashboardService.getCustomerDashboards(customerId, pageLink, true);
             };
             deleteDashboardFunction = function (dashboardId) {
                 return dashboardService.unassignDashboardFromCustomer(dashboardId);
@@ -468,7 +468,7 @@ export function DashboardsController(userService, dashboardService, customerServ
             $event.stopPropagation();
         }
         var pageSize = 10;
-        dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}).then(
+        dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}, false).then(
             function success(_dashboards) {
                 var dashboards = {
                     pageSize: pageSize,
diff --git a/ui/src/app/layout/breadcrumb-label.filter.js b/ui/src/app/layout/breadcrumb-label.filter.js
index 76a2632..0294810 100644
--- a/ui/src/app/layout/breadcrumb-label.filter.js
+++ b/ui/src/app/layout/breadcrumb-label.filter.js
@@ -22,18 +22,22 @@ export default function BreadcrumbLabel($translate) {
         var labelObj;
         labelObj = angular.fromJson(bLabel);
         if (labelObj) {
+            var translate = !(labelObj.translate && labelObj.translate === 'false');
+            var key = translate ? $translate.use() : 'orig';
             if (!labels[labelObj.label]) {
-                labels[labelObj.label] = labelObj.label;
-                var translate = !(labelObj.translate && labelObj.translate === 'false');
+                labels[labelObj.label] = {};
+            }
+            if (!labels[labelObj.label][key]) {
+                labels[labelObj.label][key] = labelObj.label;
                 if (translate) {
                     $translate([labelObj.label]).then(
                         function (translations) {
-                            labels[labelObj.label] = translations[labelObj.label];
+                            labels[labelObj.label][key] = translations[labelObj.label];
                         }
                     )
                 }
             }
-            return labels[labelObj.label];
+            return labels[labelObj.label][key];
         } else {
             return '';
         }
diff --git a/ui/src/app/widget/lib/alarms-table-widget.tpl.html b/ui/src/app/widget/lib/alarms-table-widget.tpl.html
index b7416e9..860128d 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.tpl.html
+++ b/ui/src/app/widget/lib/alarms-table-widget.tpl.html
@@ -22,7 +22,7 @@
             <div class="md-toolbar-tools">
                 <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}">
                     <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon>
-                    <md-tooltip md-direction="top">
+                    <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
                         {{'alarm.search' | translate}}
                     </md-tooltip>
                 </md-button>
@@ -32,7 +32,7 @@
                 </md-input-container>
                 <md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()">
                     <md-icon aria-label="Close" class="material-icons">close</md-icon>
-                    <md-tooltip md-direction="top">
+                    <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
                         {{ 'action.close' | translate }}
                     </md-tooltip>
                 </md-button>
@@ -46,13 +46,13 @@
                 <span flex></span>
                 <md-button ng-if="vm.allowAcknowledgment" class="md-icon-button" ng-click="vm.ackAlarms($event)">
                     <md-icon>done</md-icon>
-                    <md-tooltip md-direction="top">
+                    <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
                         {{ 'alarm.acknowledge' | translate }}
                     </md-tooltip>
                 </md-button>
                 <md-button ng-if="vm.allowClear" class="md-icon-button" ng-click="vm.clearAlarms($event)">
                     <md-icon>clear</md-icon>
-                    <md-tooltip md-direction="top">
+                    <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
                         {{ 'alarm.clear' | translate }}
                     </md-tooltip>
                 </md-button>
diff --git a/ui/src/app/widget/lib/analogue-compass.js b/ui/src/app/widget/lib/analogue-compass.js
index a59b0de..46059c7 100644
--- a/ui/src/app/widget/lib/analogue-compass.js
+++ b/ui/src/app/widget/lib/analogue-compass.js
@@ -17,6 +17,8 @@ import $ from 'jquery';
 import canvasGauges from 'canvas-gauges';
 /*import tinycolor from 'tinycolor2';*/
 
+/* eslint-disable angular/angularelement */
+
 export default class TbAnalogueCompass {
     constructor(ctx, canvasId) {
         this.ctx = ctx;
@@ -436,3 +438,5 @@ export default class TbAnalogueCompass {
         };
     }
 }
+
+/* eslint-enable angular/angularelement */
diff --git a/ui/src/app/widget/lib/entities-table-widget.tpl.html b/ui/src/app/widget/lib/entities-table-widget.tpl.html
index 162e5f7..2587da1 100644
--- a/ui/src/app/widget/lib/entities-table-widget.tpl.html
+++ b/ui/src/app/widget/lib/entities-table-widget.tpl.html
@@ -21,7 +21,7 @@
             <div class="md-toolbar-tools">
                 <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}">
                     <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon>
-                    <md-tooltip md-direction="top">
+                    <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
                         {{'entity.search' | translate}}
                     </md-tooltip>
                 </md-button>
@@ -31,7 +31,7 @@
                 </md-input-container>
                 <md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()">
                     <md-icon aria-label="Close" class="material-icons">close</md-icon>
-                    <md-tooltip md-direction="top">
+                    <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
                         {{ 'action.close' | translate }}
                     </md-tooltip>
                 </md-button>
diff --git a/ui/src/app/widget/lib/map-widget2.js b/ui/src/app/widget/lib/map-widget2.js
index 5d27114..44abb2b 100644
--- a/ui/src/app/widget/lib/map-widget2.js
+++ b/ui/src/app/widget/lib/map-widget2.js
@@ -275,9 +275,11 @@ export default class TbMapWidgetV2 {
                         for (var i = 0; i < latData.length; i++) {
                             lat = latData[i][1];
                             lng = lngData[i][1];
-                            latLng = tbMap.map.createLatLng(lat, lng);
-                            if (i == 0 || !latLngs[latLngs.length - 1].equals(latLng)) {
-                                latLngs.push(latLng);
+                            if (angular.isDefined(lat) && lat != null && angular.isDefined(lng) && lng != null) {
+                                latLng = tbMap.map.createLatLng(lat, lng);
+                                if (i == 0 || !latLngs[latLngs.length - 1].equals(latLng)) {
+                                    latLngs.push(latLng);
+                                }
                             }
                         }
                         if (latLngs.length > 0) {
@@ -308,24 +310,28 @@ export default class TbMapWidgetV2 {
                         // Create or update marker
                         lat = latData[latData.length - 1][1];
                         lng = lngData[lngData.length - 1][1];
-                        latLng = tbMap.map.createLatLng(lat, lng);
-                        if (!location.marker) {
-                            location.marker = tbMap.map.createMarker(latLng, location.settings,
-                                function (event) {
-                                    tbMap.callbacks.onLocationClick(location);
-                                    locationRowClick(event, location);
-                                }, [location.dsIndex]);
-                            tbMap.markers.push(location.marker);
-                            locationChanged = true;
-                        } else {
-                            var prevPosition = tbMap.map.getMarkerPosition(location.marker);
-                            if (!prevPosition.equals(latLng)) {
-                                tbMap.map.setMarkerPosition(location.marker, latLng);
+                        if (angular.isDefined(lat) && lat != null && angular.isDefined(lng) && lng != null) {
+                            latLng = tbMap.map.createLatLng(lat, lng);
+                            if (!location.marker) {
+                                location.marker = tbMap.map.createMarker(latLng, location.settings,
+                                    function (event) {
+                                        tbMap.callbacks.onLocationClick(location);
+                                        locationRowClick(event, location);
+                                    }, [location.dsIndex]);
+                                tbMap.markers.push(location.marker);
                                 locationChanged = true;
+                            } else {
+                                var prevPosition = tbMap.map.getMarkerPosition(location.marker);
+                                if (!prevPosition.equals(latLng)) {
+                                    tbMap.map.setMarkerPosition(location.marker, latLng);
+                                    locationChanged = true;
+                                }
                             }
                         }
                     }
-                    updateLocationStyle(location, dataMap);
+                    if (location.marker) {
+                        updateLocationStyle(location, dataMap);
+                    }
                 }
             }
             return locationChanged;