Browse Source

TB-61: Improve Alias list editor.

pull/164/head
Igor Kulikov 9 years ago
parent
commit
43b23cef4f
  1. 4
      ui/src/app/api/alias-controller.js
  2. 103
      ui/src/app/api/subscription.js
  3. 85
      ui/src/app/common/dashboard-utils.service.js
  4. 11
      ui/src/app/common/utils.service.js
  5. 31
      ui/src/app/components/dashboard.directive.js
  6. 157
      ui/src/app/components/widget.controller.js
  7. 85
      ui/src/app/components/widget.directive.js
  8. 15
      ui/src/app/entity/aliases-entity-select-panel.controller.js
  9. 4
      ui/src/app/entity/aliases-entity-select-panel.tpl.html
  10. 8
      ui/src/app/entity/aliases-entity-select.directive.js
  11. 11
      ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
  12. 43
      ui/src/app/entity/attribute/attribute-table.directive.js
  13. 2
      ui/src/app/entity/attribute/attribute-table.tpl.html
  14. 17
      ui/src/app/entity/entity-aliases.controller.js
  15. 15
      ui/src/app/entity/entity-aliases.scss
  16. 56
      ui/src/app/entity/entity-aliases.tpl.html
  17. 163
      ui/src/app/entity/entity-filter.directive.js
  18. 15
      ui/src/app/entity/entity-filter.scss
  19. 101
      ui/src/app/entity/entity-filter.tpl.html
  20. 9
      ui/src/app/import-export/import-export.service.js
  21. 2
      ui/src/app/locale/locale.constant.js
  22. 6
      ui/src/app/services/item-buffer.service.js

4
ui/src/app/api/alias-controller.js

@ -100,6 +100,7 @@ export default class AliasController {
aliasCtrl.resolvedAliasesToStateEntities[aliasId] = aliasCtrl.resolvedAliasesToStateEntities[aliasId] =
aliasCtrl.stateController.getStateParams().entityId; aliasCtrl.stateController.getStateParams().entityId;
} }
aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId);
deferred.resolve(aliasInfo); deferred.resolve(aliasInfo);
}, },
function fail() { function fail() {
@ -239,6 +240,9 @@ export default class AliasController {
datasource.name = name; datasource.name = name;
datasource.aliasName = name; datasource.aliasName = name;
datasource.entityName = name; datasource.entityName = name;
} else if (datasource.unresolvedStateEntity) {
datasource.name = "Unresolved";
datasource.entityName = "Unresolved";
} }
datasource.dataKeys.forEach(function(dataKey) { datasource.dataKeys.forEach(function(dataKey) {
if (datasource.generated) { if (datasource.generated) {

103
ui/src/app/api/subscription.js

@ -73,6 +73,15 @@ export default class Subscription {
this.datasources = this.ctx.utils.validateDatasources(options.datasources); this.datasources = this.ctx.utils.validateDatasources(options.datasources);
this.datasourceListeners = []; this.datasourceListeners = [];
/*
* data = array of datasourceData
* datasourceData = {
* tbDatasource,
* dataKey, { name, config }
* data = array of [time, value]
* }
*/
this.data = []; this.data = [];
this.hiddenData = []; this.hiddenData = [];
this.originalTimewindow = null; this.originalTimewindow = null;
@ -543,39 +552,6 @@ export default class Subscription {
var datasource = this.datasources[i]; var datasource = this.datasources[i];
if (angular.isFunction(datasource)) if (angular.isFunction(datasource))
continue; 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; var subscription = this;
@ -606,6 +582,10 @@ export default class Subscription {
this.datasourceListeners.push(listener); this.datasourceListeners.push(listener);
this.ctx.datasourceService.subscribeToDatasource(listener); this.ctx.datasourceService.subscribeToDatasource(listener);
if (datasource.unresolvedStateEntity) {
this.notifyDataLoaded();
this.onDataUpdated();
}
} }
} }
@ -625,19 +605,6 @@ export default class Subscription {
} else { } else {
return false; 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) { checkSubscriptions(aliasIds) {
@ -650,29 +617,8 @@ export default class Subscription {
break; 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; return subscriptionsChanged;
/*if (subscriptionsChanged) {
this.unsubscribe();
this.subscribe();
}*/
} }
destroy() { 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) { function calculateMin(data) {
if (data.length > 0) { if (data.length > 0) {
var result = Number(data[0][1]); var result = Number(data[0][1]);

85
ui/src/app/common/dashboard-utils.service.js

@ -23,8 +23,10 @@ function DashboardUtils(types, utils, timeService) {
var service = { var service = {
validateAndUpdateDashboard: validateAndUpdateDashboard, validateAndUpdateDashboard: validateAndUpdateDashboard,
validateAndUpdateWidget: validateAndUpdateWidget,
getRootStateId: getRootStateId, getRootStateId: getRootStateId,
createSingleWidgetDashboard: createSingleWidgetDashboard, createSingleWidgetDashboard: createSingleWidgetDashboard,
createSingleEntityFilter: createSingleEntityFilter,
getStateLayoutsData: getStateLayoutsData, getStateLayoutsData: getStateLayoutsData,
createDefaultState: createDefaultState, createDefaultState: createDefaultState,
createDefaultLayoutData: createDefaultLayoutData, createDefaultLayoutData: createDefaultLayoutData,
@ -39,7 +41,7 @@ function DashboardUtils(types, utils, timeService) {
return service; return service;
function validateAndUpdateEntityAliases(configuration) { function validateAndUpdateEntityAliases(configuration, datasourcesByAliasId, targetDevicesByAliasId) {
var aliasId, entityAlias; var aliasId, entityAlias;
if (angular.isUndefined(configuration.entityAliases)) { if (angular.isUndefined(configuration.entityAliases)) {
configuration.entityAliases = {}; configuration.entityAliases = {};
@ -47,8 +49,8 @@ function DashboardUtils(types, utils, timeService) {
var deviceAliases = configuration.deviceAliases; var deviceAliases = configuration.deviceAliases;
for (aliasId in deviceAliases) { for (aliasId in deviceAliases) {
var deviceAlias = deviceAliases[aliasId]; var deviceAlias = deviceAliases[aliasId];
entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias); entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId);
configuration.entityAliases[aliasId] = entityAlias; configuration.entityAliases[entityAlias.id] = entityAlias;
} }
delete configuration.deviceAliases; delete configuration.deviceAliases;
} }
@ -56,16 +58,43 @@ function DashboardUtils(types, utils, timeService) {
var entityAliases = configuration.entityAliases; var entityAliases = configuration.entityAliases;
for (aliasId in entityAliases) { for (aliasId in entityAliases) {
entityAlias = entityAliases[aliasId]; entityAlias = entityAliases[aliasId];
entityAliases[aliasId] = validateAndUpdateEntityAlias(entityAlias); entityAlias = validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId);
if (!entityAliases[aliasId].id) { if (aliasId != entityAlias.id) {
entityAliases[aliasId].id = aliasId; delete entityAliases[aliasId];
} }
entityAliases[entityAlias.id] = entityAlias;
} }
} }
return configuration; 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 alias = deviceAlias.alias;
var entityAlias = { var entityAlias = {
id: aliasId, id: aliasId,
@ -93,7 +122,8 @@ function DashboardUtils(types, utils, timeService) {
return entityAlias; return entityAlias;
} }
function validateAndUpdateEntityAlias(entityAlias) { function validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId) {
entityAlias.id = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId);
if (!entityAlias.filter) { if (!entityAlias.filter) {
entityAlias.filter = { entityAlias.filter = {
type: entityAlias.entityFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value, 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) { 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, datasourcesByAliasId, targetDevicesByAliasId);
dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration);
if (angular.isUndefined(dashboard.configuration.timewindow)) { if (angular.isUndefined(dashboard.configuration.timewindow)) {
dashboard.configuration.timewindow = timeService.defaultTimewindow(); dashboard.configuration.timewindow = timeService.defaultTimewindow();
@ -288,6 +339,16 @@ function DashboardUtils(types, utils, timeService) {
return dashboard; return dashboard;
} }
function createSingleEntityFilter(entityType, entityId) {
return {
type: types.aliasFilterType.entityList.value,
stateEntity: false,
entityList: [entityId],
entityType: entityType,
resolveMultiple: false
};
}
function getStateLayoutsData(dashboard, targetState) { function getStateLayoutsData(dashboard, targetState) {
var dashboardConfiguration = dashboard.configuration; var dashboardConfiguration = dashboard.configuration;
var states = dashboardConfiguration.states; var states = dashboardConfiguration.states;

11
ui/src/app/common/utils.service.js

@ -106,6 +106,7 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
isDescriptorSchemaNotEmpty: isDescriptorSchemaNotEmpty, isDescriptorSchemaNotEmpty: isDescriptorSchemaNotEmpty,
filterSearchTextEntities: filterSearchTextEntities, filterSearchTextEntities: filterSearchTextEntities,
guid: guid, guid: guid,
cleanCopy: cleanCopy,
isLocalUrl: isLocalUrl, isLocalUrl: isLocalUrl,
validateDatasources: validateDatasources, validateDatasources: validateDatasources,
createKey: createKey, createKey: createKey,
@ -291,6 +292,16 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
s4() + '-' + s4() + s4() + s4(); 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) { function genNextColor(datasources) {
var index = 0; var index = 0;
if (datasources) { if (datasources) {

31
ui/src/app/components/dashboard.directive.js

@ -85,7 +85,7 @@ function Dashboard() {
} }
/*@ngInject*/ /*@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 highlightedMode = false;
var highlightedWidget = null; var highlightedWidget = null;
@ -792,7 +792,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t
} }
function dashboardLoaded() { function dashboardLoaded() {
$timeout(function () { $mdUtil.nextTick(function () {
if (vm.dashboardTimewindowWatch) { if (vm.dashboardTimewindowWatch) {
vm.dashboardTimewindowWatch(); vm.dashboardTimewindowWatch();
vm.dashboardTimewindowWatch = null; vm.dashboardTimewindowWatch = null;
@ -802,14 +802,27 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t
}, true); }, true);
adoptMaxRows(); adoptMaxRows();
vm.dashboardLoading = false; vm.dashboardLoading = false;
$timeout(function () { if ($scope.gridsterScopeWatcher) {
var gridsterScope = gridsterElement.scope(); $scope.gridsterScopeWatcher();
vm.gridster = gridsterScope.gridster; }
if (vm.onInit) { $scope.gridsterScopeWatcher = $scope.$watch(
vm.onInit({dashboard: vm}); 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() { function loading() {

157
ui/src/app/components/widget.controller.js

@ -20,9 +20,9 @@ import Subscription from '../api/subscription';
/* eslint-disable angular/angularelement */ /* eslint-disable angular/angularelement */
/*@ngInject*/ /*@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, datasourceService, entityService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow,
dashboardTimewindowApi, widget, aliasController, stateController, widgetType) { dashboardTimewindowApi, widget, aliasController, stateController, widgetInfo, widgetType) {
var vm = this; var vm = this;
@ -42,20 +42,11 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
var cafs = {}; var cafs = {};
/*
* data = array of datasourceData
* datasourceData = {
* tbDatasource,
* dataKey, { name, config }
* data = array of [time, value]
* }
*/
var widgetContext = { var widgetContext = {
inited: false, inited: false,
$scope: $scope, $scope: $scope,
$container: $('#container', $element), $container: null,
$containerParent: $($element), $containerParent: null,
width: 0, width: 0,
height: 0, height: 0,
isEdit: isEdit, isEdit: isEdit,
@ -82,30 +73,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
createSubscription: function(options, subscribe) { createSubscription: function(options, subscribe) {
return createSubscription(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) { createSubscriptionFromInfo: function (type, subscriptionsInfo, options, useDefaultComponents, subscribe) {
return createSubscriptionFromInfo(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) { function createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe) {
var deferred = $q.defer(); var deferred = $q.defer();
options.type = type; options.type = type;
@ -400,6 +352,101 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
return deferred.promise; 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() { function initialize() {
@ -407,8 +454,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
onEditModeChanged(isEdit); onEditModeChanged(isEdit);
}); });
addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
$scope.$watch(function () { $scope.$watch(function () {
return widget.row + ',' + widget.col + ',' + widget.config.mobileOrder; return widget.row + ',' + widget.col + ',' + widget.config.mobileOrder;
}, function () { }, function () {
@ -439,10 +484,10 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
}); });
$scope.$on("$destroy", function () { $scope.$on("$destroy", function () {
removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
onDestroy(); onDestroy();
}); });
configureWidgetElement();
var deferred = $q.defer(); var deferred = $q.defer();
if (!vm.useCustomDatasources) { if (!vm.useCustomDatasources) {
createDefaultSubscription().then( createDefaultSubscription().then(
@ -465,6 +510,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
function reInit() { function reInit() {
onDestroy(); onDestroy();
configureWidgetElement();
if (!vm.useCustomDatasources) { if (!vm.useCustomDatasources) {
createDefaultSubscription().then( createDefaultSubscription().then(
function success() { function success() {
@ -636,6 +682,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
handleWidgetException(e); handleWidgetException(e);
} }
} }
destroyWidgetElement();
} }
//TODO: widgets visibility //TODO: widgets visibility

85
ui/src/app/components/widget.directive.js

@ -28,7 +28,7 @@ export default angular.module('thingsboard.directives.widget', [thingsboardLegen
.name; .name;
/*@ngInject*/ /*@ngInject*/
function Widget($controller, $compile, types, widgetService) { function Widget($controller, widgetService) {
return { return {
scope: true, scope: true,
link: function (scope, elem, attrs) { link: function (scope, elem, attrs) {
@ -81,90 +81,9 @@ function Widget($controller, $compile, types, widgetService) {
elem.addClass(widgetNamespace); 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); 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); widgetController = $controller('WidgetController', locals);

15
ui/src/app/entity/aliases-entity-select-panel.controller.js

@ -15,7 +15,7 @@
*/ */
/*@ngInject*/ /*@ngInject*/
export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, types, aliasController, onEntityAliasesUpdate) { export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, $filter, types, aliasController, onEntityAliasesUpdate) {
var vm = this; var vm = this;
vm._mdPanelRef = mdPanelRef; vm._mdPanelRef = mdPanelRef;
@ -31,13 +31,18 @@ export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, t
var aliasInfo = vm.aliasController.getInstantAliasInfo(aliasId); var aliasInfo = vm.aliasController.getInstantAliasInfo(aliasId);
if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity) { if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity) {
vm.entityAliasesInfo[aliasId] = angular.copy(aliasInfo); vm.entityAliasesInfo[aliasId] = angular.copy(aliasInfo);
vm.entityAliasesInfo[aliasId].selectedId = aliasInfo.currentEntity.id;
} }
} }
function currentAliasEntityChanged(aliasId, currentEntity) { function currentAliasEntityChanged(aliasId, selectedId) {
vm.aliasController.updateCurrentAliasEntity(aliasId, currentEntity); var resolvedEntities = vm.entityAliasesInfo[aliasId].resolvedEntities;
if (onEntityAliasesUpdate) { var selected = $filter('filter')(resolvedEntities, {id: selectedId});
onEntityAliasesUpdate(); if (selected && selected.length) {
vm.aliasController.updateCurrentAliasEntity(aliasId, selected[0]);
if (onEntityAliasesUpdate) {
onEntityAliasesUpdate();
}
} }
} }

4
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"> <div flex layout="row" ng-repeat="(aliasId, entityAliasInfo) in vm.entityAliasesInfo">
<md-input-container flex> <md-input-container flex>
<label>{{entityAliasInfo.alias}}</label> <label>{{entityAliasInfo.alias}}</label>
<md-select ng-model="entityAliasInfo.currentEntity" ng-change="vm.currentAliasEntityChanged(aliasId, entityAliasInfo.currentEntity)"> <md-select ng-model="entityAliasInfo.selectedId" ng-change="vm.currentAliasEntityChanged(aliasId, entityAliasInfo.selectedId)">
<md-option ng-repeat="resolvedEntity in entityAliasInfo.resolvedEntities" ng-value="resolvedEntity"> <md-option ng-repeat="resolvedEntity in entityAliasInfo.resolvedEntities" ng-value="resolvedEntity.id">
{{resolvedEntity.name}} {{resolvedEntity.name}}
</md-option> </md-option>
</md-select> </md-select>

8
ui/src/app/entity/aliases-entity-select.directive.js

@ -94,6 +94,14 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $
$mdPanel.open(config); $mdPanel.open(config);
} }
scope.$on('entityAliasesChanged', function() {
scope.updateView();
});
scope.$on('entityAliasResolved', function() {
scope.updateView();
});
scope.updateView = function () { scope.updateView = function () {
updateDisplayValue(); updateDisplayValue();
} }

11
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 */ /* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/ /*@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) { types, itembuffer, dashboardService, entityId, entityType, entityName, widget) {
var vm = this; var vm = this;
@ -126,15 +126,8 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog,
targetDeviceAliases: {} targetDeviceAliases: {}
}; };
aliasesInfo.datasourceAliases[0] = { aliasesInfo.datasourceAliases[0] = {
id: 1,
alias: entityName, alias: entityName,
filter: { filter: dashboardUtils.createSingleEntityFilter(entityType, entityId)
type: types.aliasFilterType.entityList.value,
stateEntity: false,
entityList: [entityId],
entityType: entityType,
resolveMultiple: false
}
}; };
itembuffer.addWidgetToDashboard(theDashboard, targetState, targetLayout, vm.widget, aliasesInfo, null, 48, null, -1, -1).then( itembuffer.addWidgetToDashboard(theDashboard, targetState, targetLayout, vm.widget, aliasesInfo, null, 48, null, -1, -1).then(
function(theDashboard) { function(theDashboard) {

43
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 */ /* eslint-enable import/no-unresolved, import/default */
import EditAttributeValueController from './edit-attribute-value.controller'; import EditAttributeValueController from './edit-attribute-value.controller';
import AliasController from '../../api/alias-controller';
/*@ngInject*/ /*@ngInject*/
export default function AttributeTableDirective($compile, $templateCache, $rootScope, $q, $mdEditDialog, $mdDialog, 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) { var linker = function (scope, element, attrs) {
@ -246,15 +248,19 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
} }
scope.nextWidget = function() { scope.nextWidget = function() {
if (scope.widgetsCarousel.index < scope.widgetsList.length-1) { $mdUtil.nextTick(function () {
scope.widgetsCarousel.index++; if (scope.widgetsCarousel.index < scope.widgetsList.length - 1) {
} scope.widgetsCarousel.index++;
}
});
} }
scope.prevWidget = function() { scope.prevWidget = function() {
if (scope.widgetsCarousel.index > 0) { $mdUtil.nextTick(function () {
scope.widgetsCarousel.index--; if (scope.widgetsCarousel.index > 0) {
} scope.widgetsCarousel.index--;
}
});
} }
scope.enterWidgetMode = function() { scope.enterWidgetMode = function() {
@ -281,23 +287,28 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
scope.firstBundle = true; scope.firstBundle = true;
scope.selectedWidgetsBundleAlias = types.systemBundleAlias.cards; scope.selectedWidgetsBundleAlias = types.systemBundleAlias.cards;
scope.aliasesInfo = { var entityAlias = {
entityAliases: { id: utils.guid(),
'1': {alias: scope.entityName, entityType: scope.entityType, entityId: scope.entityId} alias: scope.entityName,
}, filter: dashboardUtils.createSingleEntityFilter(scope.entityType, scope.entityId)
entityAliasesInfo: { };
'1': [ var entitiAliases = {};
{name: scope.entityName, entityType: scope.entityType, id: scope.entityId} 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 ? var dataKeyType = scope.attributeScope === types.latestTelemetry ?
types.dataKeyType.timeseries : types.dataKeyType.attribute; types.dataKeyType.timeseries : types.dataKeyType.attribute;
var datasource = { var datasource = {
type: types.datasourceType.entity, type: types.datasourceType.entity,
entityAliasId: '1', entityAliasId: entityAlias.id,
dataKeys: [] dataKeys: []
} }
var i = 0; var i = 0;

2
ui/src/app/entity/attribute/attribute-table.tpl.html

@ -156,7 +156,7 @@
rn-swipe-disabled="true"> rn-swipe-disabled="true">
<li ng-repeat="widgets in widgetsList"> <li ng-repeat="widgets in widgetsList">
<tb-dashboard <tb-dashboard
aliases-info="aliasesInfo" alias-controller="aliasController"
widgets="widgets" widgets="widgets"
get-st-diff="getServerTimeDiff()" get-st-diff="getServerTimeDiff()"
columns="20" columns="20"

17
ui/src/app/entity/entity-aliases.controller.js

@ -113,12 +113,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
} }
function addAlias() { function addAlias() {
var aliasId = 0; var entityAlias = {id: utils.guid(), alias: '', filter: {}, changed: false};
for (var a in vm.entityAliases) {
aliasId = Math.max(vm.entityAliases[a].id, aliasId);
}
aliasId++;
var entityAlias = {id: aliasId, alias: '', filter: {}, changed: false};
vm.entityAliases.push(entityAlias); vm.entityAliases.push(entityAlias);
} }
@ -162,23 +157,21 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
var uniqueAliasList = {}; var uniqueAliasList = {};
var valid = true; var valid = true;
var aliasId, maxAliasId; var aliasId;
var alias; var alias;
var i; var i;
if (vm.isSingleEntityAlias) { if (vm.isSingleEntityAlias) {
maxAliasId = 0; if (!vm.singleEntityAlias.id) {
vm.singleEntityAlias.id = utils.guid();
}
for (i = 0; i < vm.entityAliases.length; i ++) { for (i = 0; i < vm.entityAliases.length; i ++) {
aliasId = vm.entityAliases[i].id;
alias = vm.entityAliases[i].alias; alias = vm.entityAliases[i].alias;
if (alias === vm.singleEntityAlias.alias) { if (alias === vm.singleEntityAlias.alias) {
valid = false; valid = false;
break; break;
} }
maxAliasId = Math.max(aliasId, maxAliasId);
} }
maxAliasId++;
vm.singleEntityAlias.id = maxAliasId;
} else { } else {
for (i = 0; i < vm.entityAliases.length; i++) { for (i = 0; i < vm.entityAliases.length; i++) {
aliasId = vm.entityAliases[i].id; aliasId = vm.entityAliases[i].id;

15
ui/src/app/entity/entity-aliases.scss

@ -18,11 +18,20 @@
.md-dialog-content { .md-dialog-content {
padding-bottom: 0px; 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 { .tb-alias {
padding: 10px 0 0 10px; padding: 0 0 0 10px;
margin: 5px; margin: 5px;
md-select.tb-entity-type-select { md-input-container {
padding-bottom: 24px; margin: 0px;
} }
} }
} }

56
ui/src/app/entity/entity-aliases.tpl.html

@ -28,23 +28,28 @@
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <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> <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> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="loading">
<div ng-show="vm.isSingleEntityAlias" layout="row"> <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> </tb-entity-filter>
</div> </div>
<div ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center"> <div ng-show="!vm.isSingleEntityAlias" style="height: 100%; overflow: auto; padding-bottom: 20px;">
<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-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index"> <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> <span flex="5">{{$index + 1}}.</span>
<div class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center"> <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 translate ng-message="required">entity.alias-required</div>
</div> </div>
</md-input-container> </md-input-container>
<section flex="80" layout="column"> <tb-entity-filter flex="80" style="min-width: 240px; padding-left: 10px;"
<tb-entity-filter style="padding-left: 10px;" hide-labels
allowed-entity-types="vm.allowedEntityTypes" allowed-entity-types="vm.allowedEntityTypes"
ng-model="entityAlias.filter" ng-model="entityAlias.filter"
on-matching-entity-change="vm.onFilterEntityChanged(entity, stateEntity, entityAlias)"> on-matching-entity-change="vm.onFilterEntityChanged(entity, stateEntity, entityAlias)">
</tb-entity-filter> </tb-entity-filter>
</section>
<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;" <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 }}"> ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
@ -73,18 +77,18 @@
</div> </div>
</div> </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> </fieldset>
</div> </div>
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <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> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}

163
ui/src/app/entity/entity-filter.directive.js

@ -35,46 +35,16 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
scope.ngModelCtrl = ngModelCtrl; scope.ngModelCtrl = ngModelCtrl;
scope.types = types; scope.types = types;
scope.hideLabels = angular.isDefined(attrs.hideLabels);
/* 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.updateValidity = function() { scope.updateValidity = function() {
if (ngModelCtrl.$viewValue) { if (ngModelCtrl.$viewValue) {
var value = ngModelCtrl.$viewValue; var value = ngModelCtrl.$viewValue;
ngModelCtrl.$setValidity('filter', value.type ? true : false); 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 () { ngModelCtrl.$render = function () {
//destroyWatchers();
if (ngModelCtrl.$viewValue) { if (ngModelCtrl.$viewValue) {
scope.model = angular.copy(ngModelCtrl.$viewValue); scope.model = angular.copy(ngModelCtrl.$viewValue);
} else { } else {
@ -83,28 +53,6 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
resolveMultiple: false 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 () { 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); $compile(element.contents())(scope);
} }

15
ui/src/app/entity/entity-filter.scss

@ -14,18 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
.tb-entity-filter { .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 { .tb-filter-switch {
padding-left: 10px; padding-left: 10px;
.filter-switch { .filter-switch {
@ -39,7 +27,8 @@
margin-top: -11px; margin-top: -11px;
height: 35px; height: 35px;
.tb-error-message { .tb-error-message {
padding-left: 1px; padding-left: 8px;
padding-top: 14px;
} }
} }
} }

101
ui/src/app/entity/entity-filter.tpl.html

@ -15,80 +15,37 @@
limitations under the License. limitations under the License.
--> -->
<section layout='column' class="tb-entity-filter"> <section layout='row' class="tb-entity-filter">
<section layout='row'> <section layout="row" flex="70">
<!--section layout="column" flex ng-show="!model.useFilter"> <section flex layout="column" layout-align="center start">
<md-chips flex <div ng-if="model.type">{{ types.aliasFilterType[model.type].name | translate }}</div>
id="entity_list_chips" <md-button ng-if="!model.type"
ng-required="!useFilter" ng-disabled="loading" class="md-primary"
ng-model="model.entityList" md-autocomplete-snap ng-click="createFilter($event)"
md-require-match="true"> aria-label="{{ 'alias.create-entity-filter' | translate }}">
<md-autocomplete <md-icon aria-label="{{ 'alias.create-entity-filter' | translate }}"
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 }}"
class="material-icons"> class="material-icons">
edit add
</md-icon> </md-icon>
{{ 'alias.create-entity-filter' | translate }}
</md-button> </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> </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> </section>
<div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert"> </section>
<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>

9
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 */ /* eslint-disable no-undef, angular/window-service, angular/document-service */
/*@ngInject*/ /*@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) { entityService, dashboardService, pluginService, ruleService, widgetService, toast) {
@ -415,6 +415,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
deferred.reject(); deferred.reject();
} else { } else {
var widget = widgetItem.widget; var widget = widgetItem.widget;
widget = dashboardUtils.validateAndUpdateWidget(widget);
var aliasesInfo = prepareAliasesInfo(widgetItem.aliasesInfo); var aliasesInfo = prepareAliasesInfo(widgetItem.aliasesInfo);
var originalColumns = widgetItem.originalColumns; var originalColumns = widgetItem.originalColumns;
var originalSize = widgetItem.originalSize; var originalSize = widgetItem.originalSize;
@ -425,22 +426,22 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
var entityAliases = {}; var entityAliases = {};
var datasourceAliasesMap = {}; var datasourceAliasesMap = {};
var targetDeviceAliasesMap = {}; var targetDeviceAliasesMap = {};
var aliasId = 1; var aliasId;
var datasourceIndex; var datasourceIndex;
if (datasourceAliases) { if (datasourceAliases) {
for (datasourceIndex in datasourceAliases) { for (datasourceIndex in datasourceAliases) {
aliasId = utils.guid();
datasourceAliasesMap[aliasId] = datasourceIndex; datasourceAliasesMap[aliasId] = datasourceIndex;
entityAliases[aliasId] = datasourceAliases[datasourceIndex]; entityAliases[aliasId] = datasourceAliases[datasourceIndex];
entityAliases[aliasId].id = aliasId; entityAliases[aliasId].id = aliasId;
aliasId++;
} }
} }
if (targetDeviceAliases) { if (targetDeviceAliases) {
for (datasourceIndex in targetDeviceAliases) { for (datasourceIndex in targetDeviceAliases) {
aliasId = utils.guid();
targetDeviceAliasesMap[aliasId] = datasourceIndex; targetDeviceAliasesMap[aliasId] = datasourceIndex;
entityAliases[aliasId] = targetDeviceAliases[datasourceIndex]; entityAliases[aliasId] = targetDeviceAliases[datasourceIndex];
entityAliases[aliasId].id = aliasId; entityAliases[aliasId].id = aliasId;
aliasId++;
} }
} }

2
ui/src/app/locale/locale.constant.js

@ -124,7 +124,7 @@ export default angular.module('thingsboard.locale', [])
"create-entity-filter": "Create entity filter", "create-entity-filter": "Create entity filter",
"edit-entity-filter": "Edit entity filter", "edit-entity-filter": "Edit entity filter",
"entity-filter-required": "Entity filter is required.", "entity-filter-required": "Entity filter is required.",
"resolve-multiple": "Multiple", "resolve-multiple": "Resolve as multiple entities",
"filter-type": "Filter type", "filter-type": "Filter type",
"filter-type-required": "Filter type is required.", "filter-type-required": "Filter type is required.",
"use-state-entity": "Use state entity", "use-state-entity": "Use state entity",

6
ui/src/app/services/item-buffer.service.js

@ -298,11 +298,7 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) {
} }
if (!newAliasId) { if (!newAliasId) {
var newAliasName = createEntityAliasName(entityAliases, aliasInfo.alias); var newAliasName = createEntityAliasName(entityAliases, aliasInfo.alias);
newAliasId = 0; newAliasId = utils.guid();
for (aliasId in entityAliases) {
newAliasId = Math.max(newAliasId, aliasId);
}
newAliasId++;
entityAliases[newAliasId] = {id: newAliasId, alias: newAliasName, filter: aliasInfo.filter}; entityAliases[newAliasId] = {id: newAliasId, alias: newAliasName, filter: aliasInfo.filter};
} }
return newAliasId; return newAliasId;

Loading…
Cancel
Save