TB-70: Improve dashboards states management.
This commit is contained in:
parent
983ba89a04
commit
81ccdf7bfa
@ -26,12 +26,12 @@
|
||||
"name": "Entities table",
|
||||
"descriptor": {
|
||||
"type": "latest",
|
||||
"sizeX": 10.5,
|
||||
"sizeY": 6.5,
|
||||
"sizeX": 7.5,
|
||||
"sizeY": 4.5,
|
||||
"resources": [],
|
||||
"templateHtml": "<tb-entities-table-widget \n table-id=\"tableId\"\n ctx=\"ctx\">\n</tb-entities-table-widget>",
|
||||
"templateCss": "",
|
||||
"controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.tableId = \"table-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.$broadcast('entities-table-data-updated', self.ctx.$scope.tableId);\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1 \n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
|
||||
"controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.tableId = \"table-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.$broadcast('entities-table-data-updated', self.ctx.$scope.tableId);\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
|
||||
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"entitiesTitle\": {\n \"title\": \"Entities table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSearch\": {\n \"title\": \"Enable entities search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayEntityName\": {\n \"title\": \"Display entity name column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"entityNameColumnTitle\": {\n \"title\": \"Entity name column title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"displayEntityType\": {\n \"title\": \"Display entity type column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"entityName\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"entitiesTitle\",\n \"enableSearch\",\n \"displayEntityName\",\n \"entityNameColumnTitle\",\n \"displayEntityType\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\"\n ]\n}",
|
||||
"dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"columnWidth\": {\n \"title\": \"Column width (px or %)\",\n \"type\": \"string\",\n \"default\": \"0px\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, entity, filter)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"columnWidth\",\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}",
|
||||
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"18px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}]}"
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"resources": [],
|
||||
"templateHtml": "<div style=\"height: 100%;\" id=\"device-terminal\"></div>",
|
||||
"templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n",
|
||||
"controllerScript": "var requestTimeout = 500;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n var greetings = 'Welcome to ThingsBoard RPC commands terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = angular.copy(command).trim();\n if (localCommand == 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (cmdObj.args.length && cmdObj.args[0]) {\n params = JSON.parse(cmdObj.args[0]);\n }\n performRpc(this, cmdObj.name, params);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout).then(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n \nself.onDestroy = function() {\n}\n",
|
||||
"controllerScript": "var requestTimeout = 500;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n var greetings = 'Welcome to ThingsBoard RPC commands terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = angular.copy(command).trim();\n if (localCommand == 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (cmdObj.args.length && cmdObj.args[0]) {\n try {\n params = JSON.parse(cmdObj.args[0]);\n } catch (e) {\n params = cmdObj.args[0];\n }\n }\n performRpc(this, cmdObj.name, params);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout).then(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n \nself.onDestroy = function() {\n}\n",
|
||||
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\"\n ]\n}",
|
||||
"dataKeySettingsSchema": "{}\n",
|
||||
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"Device terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
|
||||
|
||||
@ -53,10 +53,11 @@ export default class AliasController {
|
||||
}
|
||||
|
||||
dashboardStateChanged() {
|
||||
var newEntityId = this.stateController.getStateParams().entityId;
|
||||
var changedAliasIds = [];
|
||||
for (var aliasId in this.resolvedAliasesToStateEntities) {
|
||||
var prevEntityId = this.resolvedAliasesToStateEntities[aliasId];
|
||||
var stateEntityInfo = this.resolvedAliasesToStateEntities[aliasId];
|
||||
var newEntityId = this.stateController.getEntityId(stateEntityInfo.entityParamName);
|
||||
var prevEntityId = stateEntityInfo.entityId;
|
||||
if (!angular.equals(newEntityId, prevEntityId)) {
|
||||
changedAliasIds.push(aliasId);
|
||||
this.setAliasUnresolved(aliasId);
|
||||
@ -93,19 +94,26 @@ export default class AliasController {
|
||||
this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then(
|
||||
function success(aliasInfo) {
|
||||
aliasCtrl.resolvedAliases[aliasId] = aliasInfo;
|
||||
delete aliasCtrl.resolvedAliasesPromise[aliasId];
|
||||
if (aliasInfo.stateEntity) {
|
||||
var stateEntityInfo = {
|
||||
entityParamName: aliasInfo.entityParamName,
|
||||
entityId: aliasCtrl.stateController.getEntityId(aliasInfo.entityParamName)
|
||||
};
|
||||
aliasCtrl.resolvedAliasesToStateEntities[aliasId] =
|
||||
aliasCtrl.stateController.getStateParams().entityId;
|
||||
stateEntityInfo;
|
||||
}
|
||||
aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId);
|
||||
deferred.resolve(aliasInfo);
|
||||
},
|
||||
function fail() {
|
||||
deferred.reject();
|
||||
delete aliasCtrl.resolvedAliasesPromise[aliasId];
|
||||
}
|
||||
);
|
||||
} else {
|
||||
deferred.reject();
|
||||
delete aliasCtrl.resolvedAliasesPromise[aliasId];
|
||||
}
|
||||
return this.resolvedAliasesPromise[aliasId];
|
||||
}
|
||||
|
||||
@ -376,6 +376,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
|
||||
var aliasInfo = {
|
||||
alias: entityAlias.alias,
|
||||
stateEntity: result.stateEntity,
|
||||
entityParamName: result.entityParamName,
|
||||
resolveMultiple: filter.resolveMultiple
|
||||
};
|
||||
aliasInfo.resolvedEntities = result.entities;
|
||||
@ -392,12 +393,30 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function getStateEntityId(filter, stateParams) {
|
||||
var entityId = null;
|
||||
if (stateParams) {
|
||||
if (filter.stateEntityParamName && filter.stateEntityParamName.length) {
|
||||
if (stateParams[filter.stateEntityParamName]) {
|
||||
entityId = stateParams[filter.stateEntityParamName].entityId;
|
||||
}
|
||||
} else {
|
||||
entityId = stateParams.entityId;
|
||||
}
|
||||
}
|
||||
return entityId;
|
||||
}
|
||||
|
||||
function resolveAliasFilter(filter, stateParams, maxItems) {
|
||||
var deferred = $q.defer();
|
||||
var result = {
|
||||
entities: [],
|
||||
stateEntity: false
|
||||
};
|
||||
if (filter.stateEntityParamName && filter.stateEntityParamName.length) {
|
||||
result.entityParamName = filter.stateEntityParamName;
|
||||
}
|
||||
var stateEntityId = getStateEntityId(filter, stateParams);
|
||||
switch (filter.type) {
|
||||
case types.aliasFilterType.entityList.value:
|
||||
getEntities(filter.entityType, filter.entityList).then(
|
||||
@ -431,8 +450,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
|
||||
break;
|
||||
case types.aliasFilterType.stateEntity.value:
|
||||
result.stateEntity = true;
|
||||
if (stateParams && stateParams.entityId) {
|
||||
getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then(
|
||||
if (stateEntityId) {
|
||||
getEntity(stateEntityId.entityType, stateEntityId.id).then(
|
||||
function success(entity) {
|
||||
result.entities = entitiesToEntitiesInfo([entity]);
|
||||
deferred.resolve(result);
|
||||
@ -479,9 +498,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
|
||||
result.stateEntity = filter.rootStateEntity;
|
||||
var rootEntityType;
|
||||
var rootEntityId;
|
||||
if (result.stateEntity && stateParams && stateParams.entityId) {
|
||||
rootEntityType = stateParams.entityId.entityType;
|
||||
rootEntityId = stateParams.entityId.id;
|
||||
if (result.stateEntity && stateEntityId) {
|
||||
rootEntityType = stateEntityId.entityType;
|
||||
rootEntityId = stateEntityId.id;
|
||||
} else if (!result.stateEntity) {
|
||||
rootEntityType = filter.rootEntity.entityType;
|
||||
rootEntityId = filter.rootEntity.id;
|
||||
@ -520,9 +539,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
|
||||
case types.aliasFilterType.assetSearchQuery.value:
|
||||
case types.aliasFilterType.deviceSearchQuery.value:
|
||||
result.stateEntity = filter.rootStateEntity;
|
||||
if (result.stateEntity && stateParams && stateParams.entityId) {
|
||||
rootEntityType = stateParams.entityId.entityType;
|
||||
rootEntityId = stateParams.entityId.id;
|
||||
if (result.stateEntity && stateEntityId) {
|
||||
rootEntityType = stateEntityId.entityType;
|
||||
rootEntityId = stateEntityId.id;
|
||||
} else if (!result.stateEntity) {
|
||||
rootEntityType = filter.rootEntity.entityType;
|
||||
rootEntityId = filter.rootEntity.id;
|
||||
|
||||
@ -730,12 +730,15 @@ export default class Subscription {
|
||||
index += datasource.dataKeys.length;
|
||||
|
||||
this.datasourceListeners.push(listener);
|
||||
this.ctx.datasourceService.subscribeToDatasource(listener);
|
||||
if (datasource.unresolvedStateEntity) {
|
||||
|
||||
if (datasource.dataKeys.length) {
|
||||
this.ctx.datasourceService.subscribeToDatasource(listener);
|
||||
}
|
||||
|
||||
if (datasource.unresolvedStateEntity || !datasource.dataKeys.length) {
|
||||
this.notifyDataLoaded();
|
||||
this.onDataUpdated();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -556,8 +556,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
|
||||
' self.typeParameters = function() {\n\n' +
|
||||
return {
|
||||
useCustomDatasources: false,
|
||||
maxDatasources: -1 //unlimited
|
||||
maxDataKeys: -1 //unlimited
|
||||
maxDatasources: -1, //unlimited
|
||||
maxDataKeys: -1, //unlimited
|
||||
dataKeysOptional: false
|
||||
};
|
||||
' }\n\n' +
|
||||
|
||||
@ -625,6 +626,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
|
||||
if (angular.isUndefined(result.typeParameters.maxDataKeys)) {
|
||||
result.typeParameters.maxDataKeys = -1;
|
||||
}
|
||||
if (angular.isUndefined(result.typeParameters.dataKeysOptional)) {
|
||||
result.typeParameters.dataKeysOptional = false;
|
||||
}
|
||||
if (angular.isFunction(widgetTypeInstance.actionSources)) {
|
||||
result.actionSources = widgetTypeInstance.actionSources();
|
||||
} else {
|
||||
|
||||
@ -68,10 +68,14 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
|
||||
ngModelCtrl.$setValidity('entityAlias',
|
||||
angular.isDefined(value.entityAliasId) &&
|
||||
value.entityAliasId != null);
|
||||
ngModelCtrl.$setValidity('entityKeys',
|
||||
angular.isDefined(value.dataKeys) &&
|
||||
value.dataKeys != null &&
|
||||
value.dataKeys.length > 0);
|
||||
if (scope.optDataKeys) {
|
||||
ngModelCtrl.$setValidity('entityKeys', true);
|
||||
} else {
|
||||
ngModelCtrl.$setValidity('entityKeys',
|
||||
angular.isDefined(value.dataKeys) &&
|
||||
value.dataKeys != null &&
|
||||
value.dataKeys.length > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -281,6 +285,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
|
||||
scope: {
|
||||
widgetType: '=',
|
||||
maxDataKeys: '=',
|
||||
optDataKeys: '=',
|
||||
aliasController: '=',
|
||||
datakeySettingsSchema: '=',
|
||||
generateDataKey: '&',
|
||||
|
||||
@ -63,10 +63,14 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document,
|
||||
var dataValid = angular.isDefined(value) && value != null;
|
||||
ngModelCtrl.$setValidity('deviceData', dataValid);
|
||||
if (dataValid) {
|
||||
ngModelCtrl.$setValidity('datasourceKeys',
|
||||
angular.isDefined(value.dataKeys) &&
|
||||
value.dataKeys != null &&
|
||||
value.dataKeys.length > 0);
|
||||
if (scope.optDataKeys) {
|
||||
ngModelCtrl.$setValidity('datasourceKeys', true);
|
||||
} else {
|
||||
ngModelCtrl.$setValidity('datasourceKeys',
|
||||
angular.isDefined(value.dataKeys) &&
|
||||
value.dataKeys != null &&
|
||||
value.dataKeys.length > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -222,6 +226,7 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document,
|
||||
scope: {
|
||||
widgetType: '=',
|
||||
maxDataKeys: '=',
|
||||
optDataKeys: '=',
|
||||
generateDataKey: '&',
|
||||
datakeySettingsSchema: '='
|
||||
},
|
||||
|
||||
@ -83,6 +83,7 @@ function Datasource($compile, $templateCache, utils, types) {
|
||||
scope: {
|
||||
aliasController: '=',
|
||||
maxDataKeys: '=',
|
||||
optDataKeys: '=',
|
||||
widgetType: '=',
|
||||
functionsOnly: '=',
|
||||
datakeySettingsSchema: '=',
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
ng-switch-default
|
||||
ng-model="model"
|
||||
max-data-keys="maxDataKeys"
|
||||
opt-data-keys="optDataKeys"
|
||||
datakey-settings-schema="datakeySettingsSchema"
|
||||
ng-required="model.type === types.datasourceType.function"
|
||||
widget-type="widgetType"
|
||||
@ -36,6 +37,7 @@
|
||||
<tb-datasource-entity flex
|
||||
ng-model="model"
|
||||
max-data-keys="maxDataKeys"
|
||||
opt-data-keys="optDataKeys"
|
||||
datakey-settings-schema="datakeySettingsSchema"
|
||||
ng-switch-when="entity"
|
||||
ng-required="model.type === types.datasourceType.entity"
|
||||
|
||||
@ -145,11 +145,13 @@ export default function WidgetActionDialogController($scope, $mdDialog, $filter,
|
||||
result.targetDashboardStateId = action.targetDashboardStateId;
|
||||
result.openRightLayout = action.openRightLayout;
|
||||
result.setEntityId = action.setEntityId;
|
||||
result.stateEntityParamName = action.stateEntityParamName;
|
||||
break;
|
||||
case vm.types.widgetActionTypes.openDashboard.value:
|
||||
result.targetDashboardId = action.targetDashboardId;
|
||||
result.targetDashboardStateId = action.targetDashboardStateId;
|
||||
result.setEntityId = action.setEntityId;
|
||||
result.stateEntityParamName = action.stateEntityParamName;
|
||||
break;
|
||||
case vm.types.widgetActionTypes.custom.value:
|
||||
result.customFunction = action.customFunction;
|
||||
|
||||
@ -104,12 +104,20 @@
|
||||
flex aria-label="{{ 'widget-action.open-right-layout' | translate }}"
|
||||
ng-model="vm.action.openRightLayout">{{ 'widget-action.open-right-layout' | translate }}
|
||||
</md-checkbox>
|
||||
<md-checkbox ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value ||
|
||||
vm.action.type == vm.types.widgetActionTypes.updateDashboardState.value ||
|
||||
vm.action.type == vm.types.widgetActionTypes.openDashboard.value"
|
||||
flex aria-label="{{ 'widget-action.set-entity-from-widget' | translate }}"
|
||||
ng-model="vm.action.setEntityId">{{ 'widget-action.set-entity-from-widget' | translate }}
|
||||
</md-checkbox>
|
||||
<div flex layout="column" ng-if="vm.action.type == vm.types.widgetActionTypes.openDashboardState.value ||
|
||||
vm.action.type == vm.types.widgetActionTypes.updateDashboardState.value ||
|
||||
vm.action.type == vm.types.widgetActionTypes.openDashboard.value">
|
||||
<md-checkbox flex aria-label="{{ 'widget-action.set-entity-from-widget' | translate }}"
|
||||
ng-model="vm.action.setEntityId">{{ 'widget-action.set-entity-from-widget' | translate }}
|
||||
</md-checkbox>
|
||||
<md-input-container ng-if="vm.action.setEntityId" class="md-block">
|
||||
<label translate>alias.state-entity-parameter-name</label>
|
||||
<input name="stateEntityParamName"
|
||||
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}"
|
||||
ng-model="vm.action.stateEntityParamName"
|
||||
aria-label="{{ 'alias.state-entity-parameter-name' | translate }}">
|
||||
</md-input-container>
|
||||
</div>
|
||||
<tb-js-func ng-if="vm.action.type == vm.types.widgetActionTypes.custom.value"
|
||||
ng-model="vm.action.customFunction"
|
||||
function-args="{{ ['$event', 'widgetContext', 'entityId'] }}"
|
||||
|
||||
@ -96,6 +96,7 @@
|
||||
<tb-datasource flex ng-model="datasource.value"
|
||||
widget-type="widgetType"
|
||||
max-data-keys="typeParameters.maxDataKeys"
|
||||
opt-data-keys="typeParameters.dataKeysOptional"
|
||||
alias-controller="aliasController"
|
||||
functions-only="functionsOnly"
|
||||
datakey-settings-schema="datakeySettingsSchema"
|
||||
|
||||
@ -421,23 +421,41 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
|
||||
return result;
|
||||
}
|
||||
|
||||
function updateEntityParams(params, targetEntityParamName, targetEntityId, entityName) {
|
||||
if (targetEntityId) {
|
||||
var targetEntityParams;
|
||||
if (targetEntityParamName && targetEntityParamName.length) {
|
||||
targetEntityParams = params[targetEntityParamName];
|
||||
if (!targetEntityParams) {
|
||||
targetEntityParams = {};
|
||||
params[targetEntityParamName] = targetEntityParams;
|
||||
}
|
||||
} else {
|
||||
targetEntityParams = params;
|
||||
}
|
||||
targetEntityParams.entityId = targetEntityId;
|
||||
if (entityName) {
|
||||
targetEntityParams.entityName = entityName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleWidgetAction($event, descriptor, entityId, entityName) {
|
||||
var type = descriptor.type;
|
||||
var targetEntityParamName = descriptor.stateEntityParamName;
|
||||
var targetEntityId;
|
||||
if (descriptor.setEntityId) {
|
||||
targetEntityId = entityId;
|
||||
}
|
||||
switch (type) {
|
||||
case types.widgetActionTypes.openDashboardState.value:
|
||||
case types.widgetActionTypes.updateDashboardState.value:
|
||||
var targetDashboardStateId = descriptor.targetDashboardStateId;
|
||||
var targetEntityId;
|
||||
if (descriptor.setEntityId) {
|
||||
targetEntityId = entityId;
|
||||
}
|
||||
var params = {};
|
||||
if (targetEntityId) {
|
||||
params.entityId = targetEntityId;
|
||||
if (entityName) {
|
||||
params.entityName = entityName;
|
||||
}
|
||||
var params = angular.copy(widgetContext.stateController.getStateParams());
|
||||
if (!params) {
|
||||
params = {};
|
||||
}
|
||||
updateEntityParams(params, targetEntityParamName, targetEntityId, entityName);
|
||||
if (type == types.widgetActionTypes.openDashboardState.value) {
|
||||
widgetContext.stateController.openState(targetDashboardStateId, params, descriptor.openRightLayout);
|
||||
} else {
|
||||
@ -447,18 +465,9 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
|
||||
case types.widgetActionTypes.openDashboard.value:
|
||||
var targetDashboardId = descriptor.targetDashboardId;
|
||||
targetDashboardStateId = descriptor.targetDashboardStateId;
|
||||
targetEntityId;
|
||||
if (descriptor.setEntityId) {
|
||||
targetEntityId = entityId;
|
||||
}
|
||||
var stateObject = {};
|
||||
stateObject.params = {};
|
||||
if (targetEntityId) {
|
||||
stateObject.params.entityId = targetEntityId;
|
||||
if (entityName) {
|
||||
stateObject.params.entityName = entityName;
|
||||
}
|
||||
}
|
||||
updateEntityParams(stateObject.params, targetEntityParamName, targetEntityId, entityName);
|
||||
if (targetDashboardStateId) {
|
||||
stateObject.id = targetDashboardStateId;
|
||||
}
|
||||
|
||||
@ -25,6 +25,8 @@ export default function DashboardSettingsController($scope, $mdDialog, statesCon
|
||||
vm.imageAdded = imageAdded;
|
||||
vm.clearImage = clearImage;
|
||||
|
||||
vm.stateControllerIdChanged = stateControllerIdChanged;
|
||||
|
||||
vm.settings = settings;
|
||||
vm.gridSettings = gridSettings;
|
||||
vm.stateControllers = statesControllerService.getStateControllers();
|
||||
@ -98,6 +100,12 @@ export default function DashboardSettingsController($scope, $mdDialog, statesCon
|
||||
vm.gridSettings.backgroundImageUrl = null;
|
||||
}
|
||||
|
||||
function stateControllerIdChanged() {
|
||||
if (vm.settings.stateControllerId != 'default') {
|
||||
vm.settings.toolbarAlwaysOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
function save() {
|
||||
$scope.theForm.$setPristine();
|
||||
if (vm.gridSettings) {
|
||||
|
||||
@ -34,7 +34,8 @@
|
||||
<div ng-show="vm.settings">
|
||||
<md-input-container class="md-block">
|
||||
<label translate>dashboard.state-controller</label>
|
||||
<md-select aria-label="{{ 'dashboard.state-controller' | translate }}" ng-model="vm.settings.stateControllerId">
|
||||
<md-select aria-label="{{ 'dashboard.state-controller' | translate }}"
|
||||
ng-model="vm.settings.stateControllerId" ng-change="vm.stateControllerIdChanged()">
|
||||
<md-option ng-repeat="(stateControllerId, stateController) in vm.stateControllers" ng-value="stateControllerId">
|
||||
{{stateControllerId}}
|
||||
</md-option>
|
||||
|
||||
@ -27,6 +27,7 @@ export default function DefaultStateController($scope, $location, $state, $state
|
||||
vm.getStateId = getStateId;
|
||||
vm.getStateParams = getStateParams;
|
||||
vm.getStateParamsByStateId = getStateParamsByStateId;
|
||||
vm.getEntityId = getEntityId;
|
||||
|
||||
vm.getStateName = getStateName;
|
||||
|
||||
@ -103,6 +104,10 @@ export default function DefaultStateController($scope, $location, $state, $state
|
||||
}
|
||||
}
|
||||
|
||||
function getEntityId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
function getStateObjById(id) {
|
||||
for (var i=0; i < vm.stateObject.length; i++) {
|
||||
if (vm.stateObject[i].id === id) {
|
||||
|
||||
@ -29,6 +29,7 @@ export default function EntityStateController($scope, $location, $state, $stateP
|
||||
vm.getStateId = getStateId;
|
||||
vm.getStateParams = getStateParams;
|
||||
vm.getStateParamsByStateId = getStateParamsByStateId;
|
||||
vm.getEntityId = getEntityId;
|
||||
|
||||
vm.getStateName = getStateName;
|
||||
|
||||
@ -111,6 +112,16 @@ export default function EntityStateController($scope, $location, $state, $stateP
|
||||
}
|
||||
}
|
||||
|
||||
function getEntityId(entityParamName) {
|
||||
var stateParams = getStateParams();
|
||||
if (!entityParamName || !entityParamName.length) {
|
||||
return stateParams.entityId;
|
||||
} else if (stateParams[entityParamName]) {
|
||||
return stateParams[entityParamName].entityId;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getStateObjById(id) {
|
||||
for (var i=0; i < vm.stateObject.length; i++) {
|
||||
if (vm.stateObject[i].id === id) {
|
||||
@ -135,15 +146,22 @@ export default function EntityStateController($scope, $location, $state, $stateP
|
||||
function resolveEntity(params) {
|
||||
var deferred = $q.defer();
|
||||
if (params && params.entityId && params.entityId.id && params.entityId.entityType) {
|
||||
entityService.getEntity(params.entityId.entityType, params.entityId.id, {ignoreLoading: true, ignoreErrors: true}).then(
|
||||
function success(entity) {
|
||||
var entityName = entity.name;
|
||||
deferred.resolve(entityName);
|
||||
},
|
||||
function fail() {
|
||||
deferred.reject();
|
||||
}
|
||||
);
|
||||
if (params.entityName && params.entityName.length) {
|
||||
deferred.resolve(params.entityName);
|
||||
} else {
|
||||
entityService.getEntity(params.entityId.entityType, params.entityId.id, {
|
||||
ignoreLoading: true,
|
||||
ignoreErrors: true
|
||||
}).then(
|
||||
function success(entity) {
|
||||
var entityName = entity.name;
|
||||
deferred.resolve(entityName);
|
||||
},
|
||||
function fail() {
|
||||
deferred.reject();
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
deferred.reject();
|
||||
}
|
||||
|
||||
@ -70,6 +70,15 @@ export default function StatesComponent($compile, $templateCache, $controller, s
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
stateController.getEntityId = function(entityParamName) {
|
||||
if (scope.statesController) {
|
||||
return scope.statesController.getEntityId(entityParamName);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
scope.$on('$destroy', function callOnDestroyHook() {
|
||||
|
||||
@ -54,6 +54,7 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
|
||||
filter.entityNameFilter = '';
|
||||
break;
|
||||
case types.aliasFilterType.stateEntity.value:
|
||||
filter.stateEntityParamName = null;
|
||||
break;
|
||||
case types.aliasFilterType.assetType.value:
|
||||
filter.assetType = null;
|
||||
@ -67,6 +68,7 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
|
||||
case types.aliasFilterType.assetSearchQuery.value:
|
||||
case types.aliasFilterType.deviceSearchQuery.value:
|
||||
filter.rootStateEntity = false;
|
||||
filter.stateEntityParamName = null;
|
||||
filter.rootEntity = null;
|
||||
filter.direction = types.entitySearchDirection.from;
|
||||
filter.maxLevel = 1;
|
||||
|
||||
@ -59,6 +59,13 @@
|
||||
</md-input-container>
|
||||
</section>
|
||||
<section layout="column" ng-if="filter.type == types.aliasFilterType.stateEntity.value" id="stateEntityFilter">
|
||||
<md-input-container class="md-block">
|
||||
<label translate>alias.state-entity-parameter-name</label>
|
||||
<input name="stateEntityParamName"
|
||||
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}"
|
||||
ng-model="filter.stateEntityParamName"
|
||||
aria-label="{{ 'alias.state-entity-parameter-name' | translate }}">
|
||||
</md-input-container>
|
||||
</section>
|
||||
<section layout="column" ng-if="filter.type == types.aliasFilterType.assetType.value" id="assetTypeFilter">
|
||||
<tb-entity-subtype-autocomplete
|
||||
@ -97,12 +104,21 @@
|
||||
ng-disabled="filter.rootStateEntity"
|
||||
ng-model="filter.rootEntity">
|
||||
</tb-entity-select>
|
||||
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
|
||||
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
|
||||
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
|
||||
aria-label="{{ 'alias.root-state-entity' | translate }}">
|
||||
</md-switch>
|
||||
</section>
|
||||
<div layout="column">
|
||||
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
|
||||
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
|
||||
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
|
||||
aria-label="{{ 'alias.root-state-entity' | translate }}">
|
||||
</md-switch>
|
||||
</section>
|
||||
<md-input-container ng-if="filter.rootStateEntity" class="md-block">
|
||||
<label translate>alias.state-entity-parameter-name</label>
|
||||
<input name="stateEntityParamName"
|
||||
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}"
|
||||
ng-model="filter.stateEntityParamName"
|
||||
aria-label="{{ 'alias.state-entity-parameter-name' | translate }}">
|
||||
</md-input-container>
|
||||
</div>
|
||||
</div>
|
||||
<div flex layout="row">
|
||||
<md-input-container class="md-block" style="min-width: 100px;">
|
||||
@ -139,12 +155,21 @@
|
||||
ng-disabled="filter.rootStateEntity"
|
||||
ng-model="filter.rootEntity">
|
||||
</tb-entity-select>
|
||||
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
|
||||
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
|
||||
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
|
||||
aria-label="{{ 'alias.root-state-entity' | translate }}">
|
||||
</md-switch>
|
||||
</section>
|
||||
<div layout="column">
|
||||
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
|
||||
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
|
||||
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
|
||||
aria-label="{{ 'alias.root-state-entity' | translate }}">
|
||||
</md-switch>
|
||||
</section>
|
||||
<md-input-container ng-if="filter.rootStateEntity" class="md-block">
|
||||
<label translate>alias.state-entity-parameter-name</label>
|
||||
<input name="stateEntityParamName"
|
||||
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}"
|
||||
ng-model="filter.stateEntityParamName"
|
||||
aria-label="{{ 'alias.state-entity-parameter-name' | translate }}">
|
||||
</md-input-container>
|
||||
</div>
|
||||
</div>
|
||||
<div flex layout="row">
|
||||
<md-input-container class="md-block" style="min-width: 100px;">
|
||||
@ -189,12 +214,21 @@
|
||||
ng-disabled="filter.rootStateEntity"
|
||||
ng-model="filter.rootEntity">
|
||||
</tb-entity-select>
|
||||
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
|
||||
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
|
||||
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
|
||||
aria-label="{{ 'alias.root-state-entity' | translate }}">
|
||||
</md-switch>
|
||||
</section>
|
||||
<div layout="column">
|
||||
<section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
|
||||
<label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
|
||||
<md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
|
||||
aria-label="{{ 'alias.root-state-entity' | translate }}">
|
||||
</md-switch>
|
||||
</section>
|
||||
<md-input-container ng-if="filter.rootStateEntity" class="md-block">
|
||||
<label translate>alias.state-entity-parameter-name</label>
|
||||
<input name="stateEntityParamName"
|
||||
placeholder="{{ 'alias.default-entity-parameter-name' | translate }}"
|
||||
ng-model="filter.stateEntityParamName"
|
||||
aria-label="{{ 'alias.state-entity-parameter-name' | translate }}">
|
||||
</md-input-container>
|
||||
</div>
|
||||
</div>
|
||||
<div flex layout="row">
|
||||
<md-input-container class="md-block" style="min-width: 100px;">
|
||||
|
||||
@ -187,6 +187,8 @@ export default angular.module('thingsboard.locale', [])
|
||||
"no-entity-filter-specified": "No entity filter specified",
|
||||
"root-state-entity": "Use dashboard state entity as root",
|
||||
"root-entity": "Root entity",
|
||||
"state-entity-parameter-name": "State entity parameter name",
|
||||
"default-entity-parameter-name": "By default",
|
||||
"max-relation-level": "Max relation level",
|
||||
"unlimited-level": "Unlimited level",
|
||||
"state-entity": "Dashboard state entity",
|
||||
|
||||
@ -479,6 +479,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
|
||||
|
||||
for (var i=0;i<vm.datasources.length;i++) {
|
||||
datasource = vm.datasources[i];
|
||||
if (datasource.type == types.datasourceType.entity && !datasource.entityId) {
|
||||
continue;
|
||||
}
|
||||
var entity = {
|
||||
id: {}
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user