Commit 0c0b3dade41aaa80996cece0dc3ee7865dc44143

Authored by Igor Kulikov
1 parent ae4148d7

TB-61: Improve Alias Filter. Miltiple datasources resolution.

Showing 38 changed files with 1415 additions and 369 deletions
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +const varsRegex = /\$\{([^\}]*)\}/g;
  18 +
  19 +export default class AliasController {
  20 +
  21 + constructor($scope, $q, $filter, utils, types, entityService, stateController, entityAliases) {
  22 + this.$scope = $scope;
  23 + this.$q = $q;
  24 + this.$filter = $filter;
  25 + this.utils = utils;
  26 + this.types = types;
  27 + this.entityService = entityService;
  28 + this.stateController = stateController;
  29 + this.entityAliases = angular.copy(entityAliases);
  30 + this.resolvedAliases = {};
  31 + this.resolvedAliasesPromise = {};
  32 + this.resolvedAliasesToStateEntities = {};
  33 + }
  34 +
  35 + updateEntityAliases(newEntityAliases) {
  36 + var changedAliasIds = [];
  37 + for (var aliasId in newEntityAliases) {
  38 + var newEntityAlias = newEntityAliases[aliasId];
  39 + var prevEntityAlias = this.entityAliases[aliasId];
  40 + if (!angular.equals(newEntityAlias, prevEntityAlias)) {
  41 + changedAliasIds.push(aliasId);
  42 + this.setAliasUnresolved(aliasId);
  43 + }
  44 + }
  45 + for (aliasId in this.entityAliases) {
  46 + if (!newEntityAliases[aliasId]) {
  47 + changedAliasIds.push(aliasId);
  48 + this.setAliasUnresolved(aliasId);
  49 + }
  50 + }
  51 + this.entityAliases = angular.copy(newEntityAliases);
  52 + if (changedAliasIds.length) {
  53 + this.$scope.$broadcast('entityAliasesChanged', changedAliasIds);
  54 + }
  55 + }
  56 +
  57 + dashboardStateChanged() {
  58 + var newEntityId = this.stateController.getStateParams().entityId;
  59 + var changedAliasIds = [];
  60 + for (var aliasId in this.resolvedAliasesToStateEntities) {
  61 + var prevEntityId = this.resolvedAliasesToStateEntities[aliasId];
  62 + if (!angular.equals(newEntityId, prevEntityId)) {
  63 + changedAliasIds.push(aliasId);
  64 + this.setAliasUnresolved(aliasId);
  65 + }
  66 + }
  67 + if (changedAliasIds.length) {
  68 + this.$scope.$broadcast('entityAliasesChanged', changedAliasIds);
  69 + }
  70 + }
  71 +
  72 + setAliasUnresolved(aliasId) {
  73 + delete this.resolvedAliases[aliasId];
  74 + delete this.resolvedAliasesPromise[aliasId];
  75 + delete this.resolvedAliasesToStateEntities[aliasId];
  76 + }
  77 +
  78 + getEntityAliases() {
  79 + return this.entityAliases;
  80 + }
  81 +
  82 + getAliasInfo(aliasId) {
  83 + var deferred = this.$q.defer();
  84 + var aliasInfo = this.resolvedAliases[aliasId];
  85 + if (aliasInfo) {
  86 + deferred.resolve(aliasInfo);
  87 + return deferred.promise;
  88 + } else if (this.resolvedAliasesPromise[aliasId]) {
  89 + return this.resolvedAliasesPromise[aliasId];
  90 + } else {
  91 + this.resolvedAliasesPromise[aliasId] = deferred.promise;
  92 + var aliasCtrl = this;
  93 + var entityAlias = this.entityAliases[aliasId];
  94 + if (entityAlias) {
  95 + this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then(
  96 + function success(aliasInfo) {
  97 + aliasCtrl.resolvedAliases[aliasId] = aliasInfo;
  98 + if (entityAlias.filter.stateEntity) {
  99 + aliasCtrl.resolvedAliasesToStateEntities[aliasId] =
  100 + aliasCtrl.stateController.getStateParams().entityId;
  101 + }
  102 + deferred.resolve(aliasInfo);
  103 + },
  104 + function fail() {
  105 + deferred.reject();
  106 + }
  107 + );
  108 + } else {
  109 + deferred.reject();
  110 + }
  111 + return this.resolvedAliasesPromise[aliasId];
  112 + }
  113 + }
  114 +
  115 + resolveDatasource(datasource) {
  116 + var deferred = this.$q.defer();
  117 + if (datasource.type === this.types.datasourceType.entity) {
  118 + if (datasource.entityAliasId) {
  119 + this.getAliasInfo(datasource.entityAliasId).then(
  120 + function success(aliasInfo) {
  121 + datasource.aliasName = aliasInfo.alias;
  122 + if (aliasInfo.resolveMultiple) {
  123 + var resolvedEntities = aliasInfo.resolvedEntities;
  124 + if (resolvedEntities && resolvedEntities.length) {
  125 + var datasources = [];
  126 + for (var i=0;i<resolvedEntities.length;i++) {
  127 + var resolvedEntity = resolvedEntities[i];
  128 + var newDatasource = angular.copy(datasource);
  129 + newDatasource.entityId = resolvedEntity.id;
  130 + newDatasource.entityType = resolvedEntity.entityType;
  131 + newDatasource.entityName = resolvedEntity.name;
  132 + newDatasource.name = resolvedEntity.name;
  133 + newDatasource.generated = i > 0 ? true : false;
  134 + datasources.push(newDatasource);
  135 + }
  136 + deferred.resolve(datasources);
  137 + } else {
  138 + deferred.reject();
  139 + }
  140 + } else {
  141 + var entity = aliasInfo.currentEntity;
  142 + datasource.entityId = entity.id;
  143 + datasource.entityType = entity.entityType;
  144 + datasource.entityName = entity.name;
  145 + datasource.name = entity.name;
  146 + deferred.resolve([datasource]);
  147 + }
  148 + },
  149 + function fail() {
  150 + deferred.reject();
  151 + }
  152 + );
  153 + } else { // entityId
  154 + datasource.aliasName = datasource.entityName;
  155 + datasource.name = datasource.entityName;
  156 + deferred.resolve([datasource]);
  157 + }
  158 + } else { // function
  159 + deferred.resolve([datasource]);
  160 + }
  161 + return deferred.promise;
  162 + }
  163 +
  164 + resolveDatasources(datasources) {
  165 +
  166 + function updateDataKeyLabel(dataKey, datasource) {
  167 + if (!dataKey.pattern) {
  168 + dataKey.pattern = angular.copy(dataKey.label);
  169 + }
  170 + var pattern = dataKey.pattern;
  171 + var label = dataKey.pattern;
  172 + var match = varsRegex.exec(pattern);
  173 + while (match !== null) {
  174 + var variable = match[0];
  175 + var variableName = match[1];
  176 + if (variableName === 'dsName') {
  177 + label = label.split(variable).join(datasource.name);
  178 + } else if (variableName === 'entityName') {
  179 + label = label.split(variable).join(datasource.entityName);
  180 + } else if (variableName === 'deviceName') {
  181 + label = label.split(variable).join(datasource.entityName);
  182 + } else if (variableName === 'aliasName') {
  183 + label = label.split(variable).join(datasource.aliasName);
  184 + }
  185 + match = varsRegex.exec(pattern);
  186 + }
  187 + dataKey.label = label;
  188 + }
  189 +
  190 + function updateDatasourceKeyLabels(datasource) {
  191 + for (var dk = 0; dk < datasource.dataKeys.length; dk++) {
  192 + updateDataKeyLabel(datasource.dataKeys[dk], datasource);
  193 + }
  194 + }
  195 +
  196 + var deferred = this.$q.defer();
  197 + var newDatasources = angular.copy(datasources);
  198 + var datasorceResolveTasks = [];
  199 + var aliasCtrl = this;
  200 + newDatasources.forEach(function (datasource) {
  201 + var resolveDatasourceTask = aliasCtrl.resolveDatasource(datasource);
  202 + datasorceResolveTasks.push(resolveDatasourceTask);
  203 + });
  204 + this.$q.all(datasorceResolveTasks).then(
  205 + function success(datasourcesArrays) {
  206 + var datasources = [].concat.apply([], datasourcesArrays);
  207 + datasources = aliasCtrl.$filter('orderBy')(datasources, '+generated');
  208 + var index = 0;
  209 + var functionIndex = 0;
  210 + datasources.forEach(function(datasource) {
  211 + if (datasource.type === aliasCtrl.types.datasourceType.function) {
  212 + var name;
  213 + if (datasource.name && datasource.name.length) {
  214 + name = datasource.name;
  215 + } else {
  216 + functionIndex++;
  217 + name = aliasCtrl.types.datasourceType.function;
  218 + if (functionIndex > 1) {
  219 + name += ' ' + functionIndex;
  220 + }
  221 + }
  222 + datasource.name = name;
  223 + datasource.aliasName = name;
  224 + datasource.entityName = name;
  225 + }
  226 + datasource.dataKeys.forEach(function(dataKey) {
  227 + if (datasource.generated) {
  228 + dataKey._hash = Math.random();
  229 + dataKey.color = aliasCtrl.utils.getMaterialColor(index);
  230 + }
  231 + index++;
  232 + });
  233 + updateDatasourceKeyLabels(datasource);
  234 + });
  235 + deferred.resolve(datasources);
  236 + },
  237 + function fail() {
  238 + deferred.reject();
  239 + }
  240 + );
  241 + return deferred.promise;
  242 + }
  243 +
  244 + getInstantAliasInfo(aliasId) {
  245 + return this.resolvedAliases[aliasId];
  246 + }
  247 +
  248 + updateCurrentAliasEntity(aliasId, currentEntity) {
  249 + var aliasInfo = this.resolvedAliases[aliasId];
  250 + if (aliasInfo) {
  251 + var prevCurrentEntity = aliasInfo.currentEntity;
  252 + if (!angular.equals(currentEntity, prevCurrentEntity)) {
  253 + aliasInfo.currentEntity = currentEntity;
  254 + this.$scope.$broadcast('entityAliasesChanged', [aliasId]);
  255 + }
  256 + }
  257 + }
  258 +
  259 +}
\ No newline at end of file
... ...
... ... @@ -27,10 +27,13 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
27 27 getEntity: getEntity,
28 28 getEntities: getEntities,
29 29 getEntitiesByNameFilter: getEntitiesByNameFilter,
30   - processEntityAliases: processEntityAliases,
  30 + resolveAlias: resolveAlias,
  31 + resolveAliasFilter: resolveAliasFilter,
  32 + filterAliasByEntityTypes: filterAliasByEntityTypes,
  33 + //processEntityAliases: processEntityAliases,
31 34 getEntityKeys: getEntityKeys,
32 35 checkEntityAlias: checkEntityAlias,
33   - createDatasoucesFromSubscriptionsInfo: createDatasoucesFromSubscriptionsInfo,
  36 + createDatasourcesFromSubscriptionsInfo: createDatasourcesFromSubscriptionsInfo,
34 37 getRelatedEntities: getRelatedEntities,
35 38 saveRelatedEntity: saveRelatedEntity,
36 39 getRelatedEntity: getRelatedEntity,
... ... @@ -244,81 +247,151 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
244 247 return deferred.promise;
245 248 }
246 249
247   - function entityToEntityInfo(entityType, entity) {
248   - return { name: entity.name, entityType: entityType, id: entity.id.id };
  250 + function entityToEntityInfo(entity) {
  251 + return { name: entity.name, entityType: entity.id.entityType, id: entity.id.id };
249 252 }
250 253
251   - function entitiesToEntitiesInfo(entityType, entities) {
  254 + function entitiesToEntitiesInfo(entities) {
252 255 var entitiesInfo = [];
253 256 for (var d = 0; d < entities.length; d++) {
254   - entitiesInfo.push(entityToEntityInfo(entityType, entities[d]));
  257 + entitiesInfo.push(entityToEntityInfo(entities[d]));
255 258 }
256 259 return entitiesInfo;
257 260 }
258 261
259   - function processEntityAlias(index, aliasIds, entityAliases, resolution, deferred) {
260   - if (index < aliasIds.length) {
261   - var aliasId = aliasIds[index];
262   - var entityAlias = entityAliases[aliasId];
263   - var alias = entityAlias.alias;
264   - var entityFilter = entityAlias.entityFilter;
265   - if (entityFilter.useFilter) {
266   - var entityNameFilter = entityFilter.entityNameFilter;
267   - getEntitiesByNameFilter(entityAlias.entityType, entityNameFilter, 100).then(
268   - function(entities) {
269   - if (entities && entities != null) {
270   - var resolvedAlias = {alias: alias, entityType: entityAlias.entityType, entityId: entities[0].id.id};
271   - resolution.aliasesInfo.entityAliases[aliasId] = resolvedAlias;
272   - resolution.aliasesInfo.entityAliasesInfo[aliasId] = entitiesToEntitiesInfo(entityAlias.entityType, entities);
273   - index++;
274   - processEntityAlias(index, aliasIds, entityAliases, resolution, deferred);
275   - } else {
276   - if (!resolution.error) {
277   - resolution.error = 'dashboard.invalid-aliases-config';
  262 + function resolveAliasFilter(filter, stateParams) {
  263 + var deferred = $q.defer();
  264 + var result = {
  265 + entities: [],
  266 + stateEntity: false
  267 + };
  268 + switch (filter.type) {
  269 + case types.aliasFilterType.entityList.value:
  270 + if (filter.stateEntity) {
  271 + result.stateEntity = true;
  272 + if (stateParams && stateParams.entityId) {
  273 + getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then(
  274 + function success(entity) {
  275 + result.entities = [entity];
  276 + deferred.resolve(result);
  277 + },
  278 + function fail() {
  279 + deferred.reject();
278 280 }
279   - index++;
280   - processEntityAlias(index, aliasIds, entityAliases, resolution, deferred);
  281 + );
  282 + } else {
  283 + deferred.resolve(result);
  284 + }
  285 + } else {
  286 + getEntities(filter.entityType, filter.entityList).then(
  287 + function success(entities) {
  288 + if (entities && entities.length) {
  289 + result.entities = entities;
  290 + deferred.resolve(result);
  291 + } else {
  292 + deferred.reject();
  293 + }
  294 + },
  295 + function fail() {
  296 + deferred.reject();
281 297 }
282   - });
283   - } else {
284   - var entityList = entityFilter.entityList;
285   - getEntities(entityAlias.entityType, entityList).then(
  298 + );
  299 + }
  300 + break;
  301 + case types.aliasFilterType.entityName.value:
  302 + getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, 100).then(
286 303 function success(entities) {
287   - if (entities && entities.length > 0) {
288   - var resolvedAlias = {alias: alias, entityType: entityAlias.entityType, entityId: entities[0].id.id};
289   - resolution.aliasesInfo.entityAliases[aliasId] = resolvedAlias;
290   - resolution.aliasesInfo.entityAliasesInfo[aliasId] = entitiesToEntitiesInfo(entityAlias.entityType, entities);
291   - index++;
292   - processEntityAlias(index, aliasIds, entityAliases, resolution, deferred);
  304 + if (entities && entities.length) {
  305 + result.entities = entities;
  306 + deferred.resolve(result);
293 307 } else {
294   - if (!resolution.error) {
295   - resolution.error = 'dashboard.invalid-aliases-config';
296   - }
297   - index++;
298   - processEntityAlias(index, aliasIds, entityAliases, resolution, deferred);
  308 + deferred.reject();
299 309 }
300 310 },
301 311 function fail() {
  312 + deferred.reject();
  313 + }
  314 + );
  315 + break;
  316 + //TODO:
  317 + }
  318 + return deferred.promise;
  319 + }
  320 +
  321 + function resolveAlias(entityAlias, stateParams) {
  322 + var deferred = $q.defer();
  323 + var filter = entityAlias.filter;
  324 + resolveAliasFilter(filter, stateParams).then(
  325 + function (result) {
  326 + var entities = result.entities;
  327 + var aliasInfo = {
  328 + alias: entityAlias.alias,
  329 + resolveMultiple: filter.resolveMultiple
  330 + };
  331 + var resolvedEntities = entitiesToEntitiesInfo(entities);
  332 + aliasInfo.resolvedEntities = resolvedEntities;
  333 + aliasInfo.currentEntity = null;
  334 + if (aliasInfo.resolvedEntities.length) {
  335 + aliasInfo.currentEntity = aliasInfo.resolvedEntities[0];
  336 + }
  337 + deferred.resolve(aliasInfo);
  338 + },
  339 + function fail() {
  340 + deferred.reject();
  341 + }
  342 + );
  343 + return deferred.promise;
  344 + }
  345 +
  346 + function filterAliasByEntityTypes(entityAlias, entityTypes) {
  347 + var filter = entityAlias.filter;
  348 + switch (filter.type) {
  349 + case types.aliasFilterType.entityList.value:
  350 + if (filter.stateEntity) {
  351 + return true;
  352 + } else {
  353 + return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
  354 + }
  355 + case types.aliasFilterType.entityName.value:
  356 + return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
  357 + }
  358 + //TODO:
  359 + return false;
  360 + }
  361 +
  362 + /*function processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred) {
  363 + if (index < aliasIds.length) {
  364 + var aliasId = aliasIds[index];
  365 + var entityAlias = entityAliases[aliasId];
  366 + var alias = entityAlias.alias;
  367 + var filter = entityAlias.filter;
  368 + resolveAliasFilter(filter, stateParams).then(
  369 + function (entities) {
  370 + if (entities && entities.length) {
  371 + var entity = entities[0];
  372 + var resolvedAlias = {alias: alias, entityType: entity.id.entityType, entityId: entity.id.id};
  373 + resolution.aliasesInfo.entityAliases[aliasId] = resolvedAlias;
  374 + resolution.aliasesInfo.entityAliasesInfo[aliasId] = entitiesToEntitiesInfo(entities);
  375 + index++;
  376 + processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred);
  377 + } else {
302 378 if (!resolution.error) {
303 379 resolution.error = 'dashboard.invalid-aliases-config';
304 380 }
305 381 index++;
306   - processEntityAlias(index, aliasIds, entityAliases, resolution, deferred);
  382 + processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred);
307 383 }
308   - );
309   - }
  384 + }
  385 + );
310 386 } else {
311 387 deferred.resolve(resolution);
312 388 }
313   - }
  389 + }*/
314 390
315   - function processEntityAliases(entityAliases) {
  391 + /*function processEntityAliases(entityAliases, stateParams) {
316 392 var deferred = $q.defer();
317 393 var resolution = {
318   - aliasesInfo: {
319   - entityAliases: {},
320   - entityAliasesInfo: {}
321   - }
  394 + aliasesInfo: {}
322 395 };
323 396 var aliasIds = [];
324 397 if (entityAliases) {
... ... @@ -326,9 +399,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
326 399 aliasIds.push(aliasId);
327 400 }
328 401 }
329   - processEntityAlias(0, aliasIds, entityAliases, resolution, deferred);
  402 + processEntityAlias(0, aliasIds, entityAliases, stateParams, resolution, deferred);
330 403 return deferred.promise;
331   - }
  404 + }*/
332 405
333 406 function getEntityKeys(entityType, entityId, query, type) {
334 407 var deferred = $q.defer();
... ... @@ -354,8 +427,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
354 427 }
355 428 }
356 429 deferred.resolve(result);
357   - }, function fail(response) {
358   - deferred.reject(response.data);
  430 + }, function fail() {
  431 + deferred.reject();
359 432 });
360 433 return deferred.promise;
361 434 }
... ... @@ -387,7 +460,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
387 460 return deferred.promise;
388 461 }
389 462
390   - function createDatasoucesFromSubscriptionsInfo(subscriptionsInfo) {
  463 + function createDatasourcesFromSubscriptionsInfo(subscriptionsInfo) {
391 464 var deferred = $q.defer();
392 465 var datasources = [];
393 466 processSubscriptionsInfo(0, subscriptionsInfo, datasources, deferred);
... ...
... ... @@ -39,6 +39,9 @@ export default class Subscription {
39 39 this.cafs = {};
40 40 this.registrations = [];
41 41
  42 + var subscription = this;
  43 + var deferred = this.ctx.$q.defer();
  44 +
42 45 if (this.type === this.ctx.types.widgetType.rpc.value) {
43 46 this.callbacks.rpcStateChanged = this.callbacks.rpcStateChanged || function(){};
44 47 this.callbacks.onRpcSuccess = this.callbacks.onRpcSuccess || function(){};
... ... @@ -56,7 +59,11 @@ export default class Subscription {
56 59 this.rpcEnabled = false;
57 60 this.executingRpcRequest = false;
58 61 this.executingPromises = [];
59   - this.initRpc();
  62 + this.initRpc().then(
  63 + function() {
  64 + deferred.resolve(subscription);
  65 + }
  66 + );
60 67 } else {
61 68 this.callbacks.onDataUpdated = this.callbacks.onDataUpdated || function(){};
62 69 this.callbacks.onDataUpdateError = this.callbacks.onDataUpdateError || function(){};
... ... @@ -103,11 +110,36 @@ export default class Subscription {
103 110 this.legendConfig.showMax === true ||
104 111 this.legendConfig.showAvg === true ||
105 112 this.legendConfig.showTotal === true);
106   - this.initDataSubscription();
  113 + this.initDataSubscription().then(
  114 + function success() {
  115 + deferred.resolve(subscription);
  116 + },
  117 + function fail() {
  118 + deferred.reject();
  119 + }
  120 + );
107 121 }
  122 +
  123 + return deferred.promise;
108 124 }
109 125
110 126 initDataSubscription() {
  127 + var deferred = this.ctx.$q.defer();
  128 + var subscription = this;
  129 + this.ctx.aliasController.resolveDatasources(this.datasources).then(
  130 + function success(datasources) {
  131 + subscription.datasources = datasources;
  132 + subscription.configureData();
  133 + deferred.resolve();
  134 + },
  135 + function fail() {
  136 + deferred.reject();
  137 + }
  138 + );
  139 + return deferred.promise;
  140 + }
  141 +
  142 + configureData() {
111 143 var dataIndex = 0;
112 144 for (var i = 0; i < this.datasources.length; i++) {
113 145 var datasource = this.datasources[i];
... ... @@ -199,21 +231,46 @@ export default class Subscription {
199 231 }
200 232
201 233 initRpc() {
  234 + var deferred = this.ctx.$q.defer();
202 235 if (this.targetDeviceAliasIds && this.targetDeviceAliasIds.length > 0) {
203 236 this.targetDeviceAliasId = this.targetDeviceAliasIds[0];
204   - if (this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId]) {
205   - this.targetDeviceId = this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId].entityId;
  237 + var subscription = this;
  238 + this.ctx.aliasController.getAliasInfo(this.targetDeviceAliasId).then(
  239 + function success(aliasInfo) {
  240 + if (aliasInfo.currentEntity && aliasInfo.currentEntity.entityType == subscription.ctx.types.entityType.device) {
  241 + subscription.targetDeviceId = aliasInfo.currentEntity.id;
  242 + if (subscription.targetDeviceId) {
  243 + subscription.rpcEnabled = true;
  244 + } else {
  245 + subscription.rpcEnabled = subscription.ctx.$scope.widgetEditMode ? true : false;
  246 + }
  247 + subscription.callbacks.rpcStateChanged(this);
  248 + deferred.resolve();
  249 + } else {
  250 + subscription.rpcEnabled = false;
  251 + subscription.callbacks.rpcStateChanged(this);
  252 + deferred.resolve();
  253 + }
  254 + },
  255 + function fail () {
  256 + subscription.rpcEnabled = false;
  257 + subscription.callbacks.rpcStateChanged(this);
  258 + deferred.resolve();
  259 + }
  260 + );
  261 + } else {
  262 + if (this.targetDeviceIds && this.targetDeviceIds.length > 0) {
  263 + this.targetDeviceId = this.targetDeviceIds[0];
206 264 }
207   - } else if (this.targetDeviceIds && this.targetDeviceIds.length > 0) {
208   - this.targetDeviceId = this.targetDeviceIds[0];
209   - }
210   -
211   - if (this.targetDeviceId) {
212   - this.rpcEnabled = true;
213   - } else {
214   - this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false;
  265 + if (this.targetDeviceId) {
  266 + this.rpcEnabled = true;
  267 + } else {
  268 + this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false;
  269 + }
  270 + this.callbacks.rpcStateChanged(this);
  271 + deferred.resolve();
215 272 }
216   - this.callbacks.rpcStateChanged(this);
  273 + return deferred.promise;
217 274 }
218 275
219 276 clearRpcError() {
... ... @@ -319,11 +376,11 @@ export default class Subscription {
319 376 this.onDataUpdated();
320 377 }
321 378
322   - onAliasesChanged() {
  379 + onAliasesChanged(aliasIds) {
323 380 if (this.type === this.ctx.types.widgetType.rpc.value) {
324   - this.checkRpcTarget();
  381 + return this.checkRpcTarget(aliasIds);
325 382 } else {
326   - this.checkSubscriptions();
  383 + return this.checkSubscriptions(aliasIds);
327 384 }
328 385 }
329 386
... ... @@ -481,7 +538,7 @@ export default class Subscription {
481 538 var datasource = this.datasources[i];
482 539 if (angular.isFunction(datasource))
483 540 continue;
484   - var entityId = null;
  541 + /* var entityId = null;
485 542 var entityType = null;
486 543 if (datasource.type === this.ctx.types.datasourceType.entity) {
487 544 var aliasName = null;
... ... @@ -513,7 +570,7 @@ export default class Subscription {
513 570 }
514 571 for (var dk = 0; dk < datasource.dataKeys.length; dk++) {
515 572 updateDataKeyLabel(datasource.dataKeys[dk], datasource.name, entityName, aliasName);
516   - }
  573 + }*/
517 574
518 575 var subscription = this;
519 576
... ... @@ -521,8 +578,8 @@ export default class Subscription {
521 578 subscriptionType: this.type,
522 579 subscriptionTimewindow: this.subscriptionTimewindow,
523 580 datasource: datasource,
524   - entityType: entityType,
525   - entityId: entityId,
  581 + entityType: datasource.entityType,
  582 + entityId: datasource.entityId,
526 583 dataUpdated: function (data, datasourceIndex, dataKeyIndex, apply) {
527 584 subscription.dataUpdated(data, datasourceIndex, dataKeyIndex, apply);
528 585 },
... ... @@ -557,8 +614,13 @@ export default class Subscription {
557 614 }
558 615 }
559 616
560   - checkRpcTarget() {
561   - var deviceId = null;
  617 + checkRpcTarget(aliasIds) {
  618 + if (aliasIds.indexOf(this.targetDeviceAliasId) > -1) {
  619 + return true;
  620 + } else {
  621 + return false;
  622 + }
  623 + /*var deviceId = null;
562 624 if (this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId]) {
563 625 deviceId = this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId].entityId;
564 626 }
... ... @@ -570,14 +632,20 @@ export default class Subscription {
570 632 this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false;
571 633 }
572 634 this.callbacks.rpcStateChanged(this);
573   - }
  635 + }*/
574 636 }
575 637
576   - checkSubscriptions() {
  638 + checkSubscriptions(aliasIds) {
577 639 var subscriptionsChanged = false;
578 640 for (var i = 0; i < this.datasourceListeners.length; i++) {
579 641 var listener = this.datasourceListeners[i];
580   - var entityId = null;
  642 + if (listener.datasource.entityAliasId) {
  643 + if (aliasIds.indexOf(listener.datasource.entityAliasId) > -1) {
  644 + subscriptionsChanged = true;
  645 + break;
  646 + }
  647 + }
  648 + /*var entityId = null;
581 649 var entityType = null;
582 650 var aliasName = null;
583 651 if (listener.datasource.type === this.ctx.types.datasourceType.entity) {
... ... @@ -593,12 +661,13 @@ export default class Subscription {
593 661 subscriptionsChanged = true;
594 662 break;
595 663 }
596   - }
  664 + }*/
597 665 }
598   - if (subscriptionsChanged) {
  666 + return subscriptionsChanged;
  667 + /*if (subscriptionsChanged) {
599 668 this.unsubscribe();
600 669 this.subscribe();
601   - }
  670 + }*/
602 671 }
603 672
604 673 destroy() {
... ... @@ -617,7 +686,7 @@ export default class Subscription {
617 686
618 687 }
619 688
620   -const varsRegex = /\$\{([^\}]*)\}/g;
  689 +/*const varsRegex = /\$\{([^\}]*)\}/g;
621 690
622 691 function updateDataKeyLabel(dataKey, dsName, entityName, aliasName) {
623 692 var pattern = dataKey.pattern;
... ... @@ -638,7 +707,7 @@ function updateDataKeyLabel(dataKey, dsName, entityName, aliasName) {
638 707 match = varsRegex.exec(pattern);
639 708 }
640 709 dataKey.label = label;
641   -}
  710 +}*/
642 711
643 712 function calculateMin(data) {
644 713 if (data.length > 0) {
... ...
... ... @@ -40,39 +40,75 @@ function DashboardUtils(types, utils, timeService) {
40 40 return service;
41 41
42 42 function validateAndUpdateEntityAliases(configuration) {
  43 + var aliasId, entityAlias;
43 44 if (angular.isUndefined(configuration.entityAliases)) {
44 45 configuration.entityAliases = {};
45 46 if (configuration.deviceAliases) {
46 47 var deviceAliases = configuration.deviceAliases;
47   - for (var aliasId in deviceAliases) {
  48 + for (aliasId in deviceAliases) {
48 49 var deviceAlias = deviceAliases[aliasId];
49   - var alias = deviceAlias.alias;
50   - var entityFilter = {
51   - useFilter: false,
52   - entityNameFilter: '',
53   - entityList: []
54   - }
55   - if (deviceAlias.deviceFilter) {
56   - entityFilter.useFilter = deviceAlias.deviceFilter.useFilter;
57   - entityFilter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter;
58   - entityFilter.entityList = deviceAlias.deviceFilter.deviceList;
59   - } else if (deviceAlias.deviceId) {
60   - entityFilter.entityList = [deviceAlias.deviceId];
61   - }
62   - var entityAlias = {
63   - id: aliasId,
64   - alias: alias,
65   - entityType: types.entityType.device,
66   - entityFilter: entityFilter
67   - };
  50 + entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias);
68 51 configuration.entityAliases[aliasId] = entityAlias;
69 52 }
70 53 delete configuration.deviceAliases;
71 54 }
  55 + } else {
  56 + var entityAliases = configuration.entityAliases;
  57 + for (aliasId in entityAliases) {
  58 + entityAlias = entityAliases[aliasId];
  59 + entityAliases[aliasId] = validateAndUpdateEntityAlias(entityAlias);
  60 + }
72 61 }
73 62 return configuration;
74 63 }
75 64
  65 + function validateAndUpdateDeviceAlias(aliasId, deviceAlias) {
  66 + var alias = deviceAlias.alias;
  67 + var entityAlias = {
  68 + id: aliasId,
  69 + alias: alias,
  70 + filter: {
  71 + type: null,
  72 + entityType: types.entityType.device,
  73 + resolveMultiple: false
  74 + },
  75 + }
  76 + if (deviceAlias.deviceFilter) {
  77 + entityAlias.filter.type =
  78 + deviceAlias.deviceFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value;
  79 + if (entityAlias.filter.type == types.aliasFilterType.entityList.value) {
  80 + entityAlias.filter.entityList = deviceAlias.deviceFilter.deviceList;
  81 + entityAlias.filter.stateEntity = false;
  82 + } else {
  83 + entityAlias.filter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter;
  84 + }
  85 + } else {
  86 + entityAlias.filter.type = types.aliasFilterType.entityList.value;
  87 + entityAlias.filter.entityList = [deviceAlias.deviceId];
  88 + entityAlias.filter.stateEntity = false;
  89 + }
  90 + return entityAlias;
  91 + }
  92 +
  93 + function validateAndUpdateEntityAlias(entityAlias) {
  94 + if (!entityAlias.filter) {
  95 + entityAlias.filter = {
  96 + type: entityAlias.entityFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value,
  97 + entityType: entityAlias.entityType,
  98 + resolveMultiple: false
  99 + }
  100 + if (entityAlias.filter.type == types.aliasFilterType.entityList.value) {
  101 + entityAlias.filter.entityList = entityAlias.entityFilter.entityList;
  102 + entityAlias.filter.stateEntity = false;
  103 + } else {
  104 + entityAlias.filter.entityNameFilter = entityAlias.entityFilter.entityNameFilter;
  105 + }
  106 + delete entityAlias.entityType;
  107 + delete entityAlias.entityFilter;
  108 + }
  109 + return entityAlias;
  110 + }
  111 +
76 112 function validateAndUpdateWidget(widget) {
77 113 if (!widget.config) {
78 114 widget.config = {};
... ...
... ... @@ -65,6 +65,36 @@ export default angular.module('thingsboard.types', [])
65 65 clearedUnack: "CLEARED_UNACK",
66 66 clearedAck: "CLEARED_ACK"
67 67 },
  68 + aliasFilterType: {
  69 + entityList: {
  70 + value: 'entityList',
  71 + name: 'alias.filter-type-entity-list'
  72 + },
  73 + entityName: {
  74 + value: 'entityName',
  75 + name: 'alias.filter-type-entity-name'
  76 + },
  77 + assetType: {
  78 + value: 'assetType',
  79 + name: 'alias.filter-type-asset-type'
  80 + },
  81 + deviceType: {
  82 + value: 'deviceType',
  83 + name: 'alias.filter-type-device-type'
  84 + },
  85 + relationsQuery: {
  86 + value: 'relationsQuery',
  87 + name: 'alias.filter-type-relations-query'
  88 + },
  89 + assetSearchQuery: {
  90 + value: 'assetSearchQuery',
  91 + name: 'alias.filter-type-asset-search-query'
  92 + },
  93 + deviceSearchQuery: {
  94 + value: 'deviceSearchQuery',
  95 + name: 'alias.filter-type-device-search-query'
  96 + }
  97 + },
68 98 position: {
69 99 top: {
70 100 value: "top",
... ...
... ... @@ -52,7 +52,7 @@ function Dashboard() {
52 52 bindToController: {
53 53 widgets: '=',
54 54 widgetLayouts: '=?',
55   - aliasesInfo: '=',
  55 + aliasController: '=',
56 56 stateController: '=',
57 57 dashboardTimewindow: '=?',
58 58 columns: '=',
... ... @@ -329,10 +329,6 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t
329 329 $scope.$broadcast('toggleDashboardEditMode', vm.isEdit);
330 330 });
331 331
332   - $scope.$watch('vm.aliasesInfo.entityAliases', function () {
333   - $scope.$broadcast('entityAliasListChanged', vm.aliasesInfo);
334   - }, true);
335   -
336 332 $scope.$on('gridster-resized', function (event, sizes, theGridster) {
337 333 if (checkIsLocalGridsterElement(theGridster)) {
338 334 vm.gridster = theGridster;
... ...
... ... @@ -89,7 +89,7 @@
89 89 <div flex tb-widget
90 90 locals="{ visibleRect: vm.visibleRect,
91 91 widget: widget,
92   - aliasesInfo: vm.aliasesInfo,
  92 + aliasController: vm.aliasController,
93 93 stateController: vm.stateController,
94 94 isEdit: vm.isEdit,
95 95 stDiff: vm.stDiff,
... ...
... ... @@ -20,14 +20,14 @@ export default angular.module('thingsboard.dialogs.datakeyConfigDialog', [things
20 20 .name;
21 21
22 22 /*@ngInject*/
23   -function DatakeyConfigDialogController($scope, $mdDialog, entityService, dataKey, dataKeySettingsSchema, entityAlias, entityAliases) {
  23 +function DatakeyConfigDialogController($scope, $mdDialog, $q, entityService, dataKey, dataKeySettingsSchema, entityAlias, aliasController) {
24 24
25 25 var vm = this;
26 26
27 27 vm.dataKey = dataKey;
28 28 vm.dataKeySettingsSchema = dataKeySettingsSchema;
29 29 vm.entityAlias = entityAlias;
30   - vm.entityAliases = entityAliases;
  30 + vm.aliasController = aliasController;
31 31
32 32 vm.hide = function () {
33 33 $mdDialog.hide();
... ... @@ -38,12 +38,28 @@ function DatakeyConfigDialogController($scope, $mdDialog, entityService, dataKey
38 38 };
39 39
40 40 vm.fetchEntityKeys = function (entityAliasId, query, type) {
41   - var alias = vm.entityAliases[entityAliasId];
42   - if (alias) {
43   - return entityService.getEntityKeys(alias.entityType, alias.entityId, query, type);
44   - } else {
45   - return [];
46   - }
  41 + var deferred = $q.defer();
  42 + vm.aliasController.getAliasInfo(entityAliasId).then(
  43 + function success(aliasInfo) {
  44 + var entity = aliasInfo.currentEntity;
  45 + if (entity) {
  46 + entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
  47 + function success(keys) {
  48 + deferred.resolve(keys);
  49 + },
  50 + function fail() {
  51 + deferred.resolve([]);
  52 + }
  53 + );
  54 + } else {
  55 + deferred.resolve([]);
  56 + }
  57 + },
  58 + function fail() {
  59 + deferred.resolve([]);
  60 + }
  61 + );
  62 + return deferred.promise;
47 63 };
48 64
49 65 vm.save = function () {
... ...
... ... @@ -103,10 +103,9 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
103 103 ngModelCtrl.$render = function () {
104 104 if (ngModelCtrl.$viewValue) {
105 105 var entityAliasId = ngModelCtrl.$viewValue.entityAliasId;
106   - if (scope.entityAliases[entityAliasId]) {
107   - scope.entityAlias = {id: entityAliasId, alias: scope.entityAliases[entityAliasId].alias,
108   - entityType: scope.entityAliases[entityAliasId].entityType,
109   - entityId: scope.entityAliases[entityAliasId].entityId};
  106 + var entityAliases = scope.aliasController.getEntityAliases();
  107 + if (entityAliases[entityAliasId]) {
  108 + scope.entityAlias = entityAliases[entityAliasId];
110 109 } else {
111 110 scope.entityAlias = null;
112 111 }
... ... @@ -182,7 +181,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
182 181 dataKey: angular.copy(dataKey),
183 182 dataKeySettingsSchema: scope.datakeySettingsSchema,
184 183 entityAlias: scope.entityAlias,
185   - entityAliases: scope.entityAliases
  184 + aliasController: scope.aliasController
186 185 },
187 186 parent: angular.element($document[0].body),
188 187 fullscreen: true,
... ... @@ -236,7 +235,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
236 235 require: "^ngModel",
237 236 scope: {
238 237 widgetType: '=',
239   - entityAliases: '=',
  238 + aliasController: '=',
240 239 datakeySettingsSchema: '=',
241 240 generateDataKey: '&',
242 241 fetchEntityKeys: '&',
... ...
... ... @@ -18,7 +18,7 @@
18 18 <section flex layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
19 19 <tb-entity-alias-select
20 20 tb-required="true"
21   - entity-aliases="entityAliases"
  21 + alias-controller="aliasController"
22 22 ng-model="entityAlias"
23 23 on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})">
24 24 </tb-entity-alias-select>
... ...
... ... @@ -76,7 +76,7 @@ function Datasource($compile, $templateCache, types) {
76 76 restrict: "E",
77 77 require: "^ngModel",
78 78 scope: {
79   - entityAliases: '=',
  79 + aliasController: '=',
80 80 widgetType: '=',
81 81 functionsOnly: '=',
82 82 datakeySettingsSchema: '=',
... ...
... ... @@ -37,7 +37,7 @@
37 37 ng-switch-when="entity"
38 38 ng-required="model.type === types.datasourceType.entity"
39 39 widget-type="widgetType"
40   - entity-aliases="entityAliases"
  40 + alias-controller="aliasController"
41 41 generate-data-key="generateDataKey({chip: chip, type: type})"
42 42 fetch-entity-keys="fetchEntityKeys({entityAliasId: entityAliasId, query: query, type: type})"
43 43 on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})">
... ...
... ... @@ -31,7 +31,7 @@ export default angular.module('thingsboard.directives.entityAliasSelect', [])
31 31 .name;
32 32
33 33 /*@ngInject*/
34   -function EntityAliasSelect($compile, $templateCache, $mdConstant) {
  34 +function EntityAliasSelect($compile, $templateCache, $mdConstant, entityService) {
35 35
36 36 var linker = function (scope, element, attrs, ngModelCtrl) {
37 37 var template = $templateCache.get(entityAliasSelectTemplate);
... ... @@ -49,19 +49,18 @@ function EntityAliasSelect($compile, $templateCache, $mdConstant) {
49 49 ngModelCtrl.$setValidity('entityAlias', valid);
50 50 };
51 51
52   - scope.$watch('entityAliases', function () {
  52 + scope.$watch('aliasController', function () {
53 53 scope.entityAliasList = [];
54   - for (var aliasId in scope.entityAliases) {
  54 + var entityAliases = scope.aliasController.getEntityAliases();
  55 + for (var aliasId in entityAliases) {
55 56 if (scope.allowedEntityTypes) {
56   - if (scope.allowedEntityTypes.indexOf(scope.entityAliases[aliasId].entityType) === -1) {
  57 + if (!entityService.filterAliasByEntityTypes(entityAliases[aliasId], scope.allowedEntityTypes)) {
57 58 continue;
58 59 }
59 60 }
60   - var entityAlias = {id: aliasId, alias: scope.entityAliases[aliasId].alias,
61   - entityType: scope.entityAliases[aliasId].entityType, entityId: scope.entityAliases[aliasId].entityId};
62   - scope.entityAliasList.push(entityAlias);
  61 + scope.entityAliasList.push(entityAliases[aliasId]);
63 62 }
64   - }, true);
  63 + });
65 64
66 65 scope.$watch('entityAlias', function () {
67 66 scope.updateView();
... ... @@ -141,7 +140,7 @@ function EntityAliasSelect($compile, $templateCache, $mdConstant) {
141 140 link: linker,
142 141 scope: {
143 142 tbRequired: '=?',
144   - entityAliases: '=',
  143 + aliasController: '=',
145 144 allowedEntityTypes: '=?',
146 145 onCreateEntityAlias: '&'
147 146 }
... ...
... ... @@ -128,13 +128,9 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti
128 128 } else if (scope.widgetType === types.widgetType.rpc.value && scope.isDataEnabled) {
129 129 if (config.targetDeviceAliasIds && config.targetDeviceAliasIds.length > 0) {
130 130 var aliasId = config.targetDeviceAliasIds[0];
131   - if (scope.entityAliases[aliasId]) {
132   - scope.targetDeviceAlias.value = {
133   - id: aliasId,
134   - alias: scope.entityAliases[aliasId].alias,
135   - entityType: scope.entityAliases[aliasId].entityType,
136   - entityId: scope.entityAliases[aliasId].entityId
137   - };
  131 + var entityAliases = scope.aliasController.getEntityAliases();
  132 + if (entityAliases[aliasId]) {
  133 + scope.targetDeviceAlias.value = entityAliases[aliasId];
138 134 } else {
139 135 scope.targetDeviceAlias.value = null;
140 136 }
... ... @@ -395,7 +391,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti
395 391 widgetType: '=',
396 392 widgetSettingsSchema: '=',
397 393 datakeySettingsSchema: '=',
398   - entityAliases: '=',
  394 + aliasController: '=',
399 395 functionsOnly: '=',
400 396 fetchEntityKeys: '&',
401 397 onCreateEntityAlias: '&',
... ...
... ... @@ -60,7 +60,7 @@
60 60 style="padding: 0 0 0 10px; margin: 5px;">
61 61 <tb-datasource flex ng-model="datasource.value"
62 62 widget-type="widgetType"
63   - entity-aliases="entityAliases"
  63 + alias-controller="aliasController"
64 64 functions-only="functionsOnly"
65 65 datakey-settings-schema="datakeySettingsSchema"
66 66 generate-data-key="generateDataKey(chip,type)"
... ... @@ -104,7 +104,7 @@
104 104 <v-pane-content style="padding: 0 5px;">
105 105 <tb-entity-alias-select flex
106 106 tb-required="widgetType === types.widgetType.rpc.value && !widgetEditMode"
107   - entity-aliases="entityAliases"
  107 + alias-controller="aliasController"
108 108 allowed-entity-types="[types.entityType.device]"
109 109 ng-model="targetDeviceAlias.value"
110 110 on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias, allowedEntityTypes: allowedEntityTypes})">
... ...
... ... @@ -22,7 +22,7 @@ import Subscription from '../api/subscription';
22 22 /*@ngInject*/
23 23 export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, tbRaf, types, utils, timeService,
24 24 datasourceService, entityService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow,
25   - dashboardTimewindowApi, widget, aliasesInfo, stateController, widgetType) {
  25 + dashboardTimewindowApi, widget, aliasController, stateController, widgetType) {
26 26
27 27 var vm = this;
28 28
... ... @@ -37,6 +37,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
37 37 $scope.executingRpcRequest = false;
38 38
39 39 var gridsterItemInited = false;
  40 + var subscriptionInited = false;
40 41
41 42 var cafs = {};
42 43
... ... @@ -149,7 +150,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
149 150 dashboardTimewindowApi: dashboardTimewindowApi,
150 151 types: types,
151 152 stDiff: stDiff,
152   - aliasesInfo: aliasesInfo
  153 + aliasController: aliasController
153 154 };
154 155
155 156 var widgetTypeInstance;
... ... @@ -203,8 +204,13 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
203 204
204 205 vm.gridsterItemInitialized = gridsterItemInitialized;
205 206
206   - initialize();
207   -
  207 + initialize().then(
  208 + function(){
  209 + if (checkSize()) {
  210 + onInit();
  211 + }
  212 + }
  213 + );
208 214
209 215 /*
210 216 options = {
... ... @@ -233,28 +239,42 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
233 239 }
234 240 }
235 241
236   - entityService.createDatasoucesFromSubscriptionsInfo(subscriptionsInfo).then(
  242 + entityService.createDatasourcesFromSubscriptionsInfo(subscriptionsInfo).then(
237 243 function (datasources) {
238 244 options.datasources = datasources;
239   - var subscription = createSubscription(options, subscribe);
240   - if (useDefaultComponents) {
241   - defaultSubscriptionOptions(subscription, options);
242   - }
243   - deferred.resolve(subscription);
  245 + createSubscription(options, subscribe).then(
  246 + function success(subscription) {
  247 + if (useDefaultComponents) {
  248 + defaultSubscriptionOptions(subscription, options);
  249 + }
  250 + deferred.resolve(subscription);
  251 + },
  252 + function fail() {
  253 + deferred.reject();
  254 + }
  255 + );
244 256 }
245 257 );
246 258 return deferred.promise;
247 259 }
248 260
249 261 function createSubscription(options, subscribe) {
  262 + var deferred = $q.defer();
250 263 options.dashboardTimewindow = dashboardTimewindow;
251   - var subscription =
252   - new Subscription(subscriptionContext, options);
253   - widgetContext.subscriptions[subscription.id] = subscription;
254   - if (subscribe) {
255   - subscription.subscribe();
256   - }
257   - return subscription;
  264 + new Subscription(subscriptionContext, options).then(
  265 + function success(subscription) {
  266 + widgetContext.subscriptions[subscription.id] = subscription;
  267 + if (subscribe) {
  268 + subscription.subscribe();
  269 + }
  270 + deferred.resolve(subscription);
  271 + },
  272 + function fail() {
  273 + deferred.reject();
  274 + }
  275 + );
  276 +
  277 + return deferred.promise;
258 278 }
259 279
260 280 function defaultComponentsOptions(options) {
... ... @@ -310,8 +330,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
310 330 }
311 331
312 332 function createDefaultSubscription() {
313   - var subscription;
314 333 var options;
  334 + var deferred = $q.defer();
315 335 if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) {
316 336 options = {
317 337 type: widget.type,
... ... @@ -319,16 +339,23 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
319 339 };
320 340 defaultComponentsOptions(options);
321 341
322   - subscription = createSubscription(options);
323   -
324   - defaultSubscriptionOptions(subscription, options);
  342 + createSubscription(options).then(
  343 + function success(subscription) {
  344 + defaultSubscriptionOptions(subscription, options);
325 345
326   - // backward compatibility
  346 + // backward compatibility
327 347
328   - widgetContext.datasources = subscription.datasources;
329   - widgetContext.data = subscription.data;
330   - widgetContext.hiddenData = subscription.hiddenData;
331   - widgetContext.timeWindow = subscription.timeWindow;
  348 + widgetContext.datasources = subscription.datasources;
  349 + widgetContext.data = subscription.data;
  350 + widgetContext.hiddenData = subscription.hiddenData;
  351 + widgetContext.timeWindow = subscription.timeWindow;
  352 + widgetContext.defaultSubscription = subscription;
  353 + deferred.resolve();
  354 + },
  355 + function fail() {
  356 + deferred.reject();
  357 + }
  358 + );
332 359
333 360 } else if (widget.type === types.widgetType.rpc.value) {
334 361 $scope.loadingData = false;
... ... @@ -356,24 +383,27 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
356 383 $scope.rpcRejection = null;
357 384 }
358 385 }
359   - subscription = createSubscription(options);
  386 + createSubscription(options).then(
  387 + function success(subscription) {
  388 + widgetContext.defaultSubscription = subscription;
  389 + deferred.resolve();
  390 + },
  391 + function fail() {
  392 + deferred.reject();
  393 + }
  394 + );
360 395 } else if (widget.type === types.widgetType.static.value) {
361 396 $scope.loadingData = false;
  397 + deferred.resolve();
  398 + } else {
  399 + deferred.resolve();
362 400 }
363   - if (subscription) {
364   - widgetContext.defaultSubscription = subscription;
365   - }
  401 + return deferred.promise;
366 402 }
367 403
368 404
369 405 function initialize() {
370 406
371   - if (!vm.useCustomDatasources) {
372   - createDefaultSubscription();
373   - } else {
374   - $scope.loadingData = false;
375   - }
376   -
377 407 $scope.$on('toggleDashboardEditMode', function (event, isEdit) {
378 408 onEditModeChanged(isEdit);
379 409 });
... ... @@ -398,11 +428,14 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
398 428 onMobileModeChanged(newIsMobile);
399 429 });
400 430
401   - $scope.$on('entityAliasListChanged', function (event, aliasesInfo) {
402   - subscriptionContext.aliasesInfo = aliasesInfo;
  431 + $scope.$on('entityAliasesChanged', function (event, aliasIds) {
  432 + var subscriptionChanged = false;
403 433 for (var id in widgetContext.subscriptions) {
404 434 var subscription = widgetContext.subscriptions[id];
405   - subscription.onAliasesChanged();
  435 + subscriptionChanged = subscriptionChanged || subscription.onAliasesChanged(aliasIds);
  436 + }
  437 + if (subscriptionChanged && !vm.useCustomDatasources) {
  438 + reInit();
406 439 }
407 440 });
408 441
... ... @@ -410,6 +443,44 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
410 443 removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
411 444 onDestroy();
412 445 });
  446 +
  447 + var deferred = $q.defer();
  448 + if (!vm.useCustomDatasources) {
  449 + createDefaultSubscription().then(
  450 + function success() {
  451 + subscriptionInited = true;
  452 + deferred.resolve();
  453 + },
  454 + function fail() {
  455 + subscriptionInited = true;
  456 + deferred.reject();
  457 + }
  458 + );
  459 + } else {
  460 + $scope.loadingData = false;
  461 + subscriptionInited = true;
  462 + deferred.resolve();
  463 + }
  464 + return deferred.promise;
  465 + }
  466 +
  467 + function reInit() {
  468 + onDestroy();
  469 + if (!vm.useCustomDatasources) {
  470 + createDefaultSubscription().then(
  471 + function success() {
  472 + subscriptionInited = true;
  473 + onInit();
  474 + },
  475 + function fail() {
  476 + subscriptionInited = true;
  477 + onInit();
  478 + }
  479 + );
  480 + } else {
  481 + subscriptionInited = true;
  482 + onInit();
  483 + }
413 484 }
414 485
415 486 function handleWidgetException(e) {
... ... @@ -418,7 +489,9 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
418 489 }
419 490
420 491 function onInit() {
421   - if (!widgetContext.inited) {
  492 + if (!widgetContext.inited &&
  493 + subscriptionInited &&
  494 + gridsterItemInited) {
422 495 widgetContext.inited = true;
423 496 try {
424 497 widgetTypeInstance.onInit();
... ... @@ -462,7 +535,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
462 535 handleWidgetException(e);
463 536 }
464 537 });
465   - } else if (gridsterItemInited) {
  538 + } else {
466 539 onInit();
467 540 }
468 541 }
... ... @@ -544,6 +617,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
544 617 var subscription = widgetContext.subscriptions[id];
545 618 subscription.destroy();
546 619 }
  620 + subscriptionInited = false;
547 621 widgetContext.subscriptions = [];
548 622 if (widgetContext.inited) {
549 623 widgetContext.inited = false;
... ...
... ... @@ -20,12 +20,13 @@ import entityAliasesTemplate from '../entity/entity-aliases.tpl.html';
20 20 /* eslint-enable import/no-unresolved, import/default */
21 21
22 22 /*@ngInject*/
23   -export default function AddWidgetController($scope, widgetService, entityService, $mdDialog, $q, $document, types, dashboard, aliasesInfo, widget, widgetInfo) {
  23 +export default function AddWidgetController($scope, widgetService, entityService, $mdDialog, $q, $document, types, dashboard,
  24 + aliasController, widget, widgetInfo) {
24 25
25 26 var vm = this;
26 27
27 28 vm.dashboard = dashboard;
28   - vm.aliasesInfo = aliasesInfo;
  29 + vm.aliasController = aliasController;
29 30 vm.widget = widget;
30 31 vm.widgetInfo = widgetInfo;
31 32
... ... @@ -85,7 +86,7 @@ export default function AddWidgetController($scope, widgetService, entityService
85 86 }
86 87
87 88 function cancel () {
88   - $mdDialog.cancel({aliasesInfo: vm.aliasesInfo});
  89 + $mdDialog.cancel();
89 90 }
90 91
91 92 function add () {
... ... @@ -94,23 +95,39 @@ export default function AddWidgetController($scope, widgetService, entityService
94 95 vm.widget.config = vm.widgetConfig.config;
95 96 vm.widget.config.mobileOrder = vm.widgetConfig.layout.mobileOrder;
96 97 vm.widget.config.mobileHeight = vm.widgetConfig.layout.mobileHeight;
97   - $mdDialog.hide({widget: vm.widget, aliasesInfo: vm.aliasesInfo});
  98 + $mdDialog.hide({widget: vm.widget});
98 99 }
99 100 }
100 101
101 102 function fetchEntityKeys (entityAliasId, query, type) {
102   - var entityAlias = vm.aliasesInfo.entityAliases[entityAliasId];
103   - if (entityAlias && entityAlias.entityId) {
104   - return entityService.getEntityKeys(entityAlias.entityType, entityAlias.entityId, query, type);
105   - } else {
106   - return $q.when([]);
107   - }
  103 + var deferred = $q.defer();
  104 + vm.aliasController.getAliasInfo(entityAliasId).then(
  105 + function success(aliasInfo) {
  106 + var entity = aliasInfo.currentEntity;
  107 + if (entity) {
  108 + entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
  109 + function success(keys) {
  110 + deferred.resolve(keys);
  111 + },
  112 + function fail() {
  113 + deferred.resolve([]);
  114 + }
  115 + );
  116 + } else {
  117 + deferred.resolve([]);
  118 + }
  119 + },
  120 + function fail() {
  121 + deferred.resolve([]);
  122 + }
  123 + );
  124 + return deferred.promise;
108 125 }
109 126
110 127 function createEntityAlias (event, alias, allowedEntityTypes) {
111 128
112 129 var deferred = $q.defer();
113   - var singleEntityAlias = {id: null, alias: alias, entityType: types.entityType.device, entityFilter: null};
  130 + var singleEntityAlias = {id: null, alias: alias, filter: {}};
114 131
115 132 $mdDialog.show({
116 133 controller: 'EntityAliasesController',
... ... @@ -130,16 +147,9 @@ export default function AddWidgetController($scope, widgetService, entityService
130 147 skipHide: true,
131 148 targetEvent: event
132 149 }).then(function (singleEntityAlias) {
133   - vm.dashboard.configuration.entityAliases[singleEntityAlias.id] =
134   - { alias: singleEntityAlias.alias, entityType: singleEntityAlias.entityType, entityFilter: singleEntityAlias.entityFilter };
135   - entityService.processEntityAliases(vm.dashboard.configuration.entityAliases).then(
136   - function(resolution) {
137   - if (!resolution.error) {
138   - vm.aliasesInfo = resolution.aliasesInfo;
139   - }
140   - deferred.resolve(singleEntityAlias);
141   - }
142   - );
  150 + vm.dashboard.configuration.entityAliases[singleEntityAlias.id] = singleEntityAlias;
  151 + vm.aliasController.updateEntityAliases(vm.dashboard.configuration.entityAliases);
  152 + deferred.resolve(singleEntityAlias);
143 153 }, function () {
144 154 deferred.reject();
145 155 });
... ...
... ... @@ -37,7 +37,7 @@
37 37 ng-model="vm.widgetConfig"
38 38 widget-settings-schema="vm.settingsSchema"
39 39 datakey-settings-schema="vm.dataKeySettingsSchema"
40   - entity-aliases="vm.aliasesInfo.entityAliases"
  40 + alias-controller="vm.aliasController"
41 41 functions-only="vm.functionsOnly"
42 42 fetch-entity-keys="vm.fetchEntityKeys(entityAliasId, query, type)"
43 43 on-create-entity-alias="vm.createEntityAlias(event, alias, allowedEntityTypes)"
... ...
... ... @@ -24,8 +24,10 @@ import selectTargetLayoutTemplate from './layouts/select-target-layout.tpl.html'
24 24
25 25 /* eslint-enable import/no-unresolved, import/default */
26 26
  27 +import AliasController from '../api/alias-controller';
  28 +
27 29 /*@ngInject*/
28   -export default function DashboardController(types, dashboardUtils, widgetService, userService,
  30 +export default function DashboardController(types, utils, dashboardUtils, widgetService, userService,
29 31 dashboardService, timeService, entityService, itembuffer, importExport, hotkeys, $window, $rootScope,
30 32 $scope, $element, $state, $stateParams, $mdDialog, $mdMedia, $timeout, $document, $q, $translate, $filter) {
31 33
... ... @@ -349,7 +351,13 @@ export default function DashboardController(types, dashboardUtils, widgetService
349 351 dashboardService.getDashboard($stateParams.dashboardId)
350 352 .then(function success(dashboard) {
351 353 vm.dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard);
352   - entityService.processEntityAliases(vm.dashboard.configuration.entityAliases)
  354 + vm.dashboardConfiguration = vm.dashboard.configuration;
  355 + vm.dashboardCtx.dashboard = vm.dashboard;
  356 + vm.dashboardCtx.dashboardTimewindow = vm.dashboardConfiguration.timewindow;
  357 + vm.dashboardCtx.aliasController = new AliasController($scope, $q, $filter, utils,
  358 + types, entityService, vm.dashboardCtx.stateController, vm.dashboardConfiguration.entityAliases);
  359 +
  360 + /* entityService.processEntityAliases(vm.dashboard.configuration.entityAliases)
353 361 .then(
354 362 function(resolution) {
355 363 if (resolution.error && !isTenantAdmin()) {
... ... @@ -362,7 +370,7 @@ export default function DashboardController(types, dashboardUtils, widgetService
362 370 vm.dashboardCtx.dashboardTimewindow = vm.dashboardConfiguration.timewindow;
363 371 }
364 372 }
365   - );
  373 + );*/
366 374 }, function fail() {
367 375 vm.configurationError = true;
368 376 });
... ... @@ -373,6 +381,7 @@ export default function DashboardController(types, dashboardUtils, widgetService
373 381 var layoutsData = dashboardUtils.getStateLayoutsData(vm.dashboard, state);
374 382 if (layoutsData) {
375 383 vm.dashboardCtx.state = state;
  384 + vm.dashboardCtx.aliasController.dashboardStateChanged();
376 385 var layoutVisibilityChanged = false;
377 386 for (var l in vm.layouts) {
378 387 var layout = vm.layouts[l];
... ... @@ -916,7 +925,7 @@ export default function DashboardController(types, dashboardUtils, widgetService
916 925 templateUrl: addWidgetTemplate,
917 926 locals: {
918 927 dashboard: vm.dashboard,
919   - aliasesInfo: vm.dashboardCtx.aliasesInfo,
  928 + aliasController: vm.dashboardCtx.aliasController,
920 929 widget: newWidget,
921 930 widgetInfo: widgetTypeInfo
922 931 },
... ... @@ -930,10 +939,8 @@ export default function DashboardController(types, dashboardUtils, widgetService
930 939 }
931 940 }).then(function (result) {
932 941 var widget = result.widget;
933   - vm.dashboardCtx.aliasesInfo = result.aliasesInfo;
934 942 addWidget(widget);
935   - }, function (rejection) {
936   - vm.dashboardCtx.aliasesInfo = rejection.aliasesInfo;
  943 + }, function () {
937 944 });
938 945 }
939 946 }
... ... @@ -1025,7 +1032,7 @@ export default function DashboardController(types, dashboardUtils, widgetService
1025 1032 notifyDashboardUpdated();
1026 1033 }
1027 1034
1028   - function showAliasesResolutionError(error) {
  1035 +/* function showAliasesResolutionError(error) {
1029 1036 var alert = $mdDialog.alert()
1030 1037 .parent(angular.element($document[0].body))
1031 1038 .clickOutsideToClose(true)
... ... @@ -1037,20 +1044,10 @@ export default function DashboardController(types, dashboardUtils, widgetService
1037 1044 alert._options.fullscreen = true;
1038 1045
1039 1046 $mdDialog.show(alert);
1040   - }
  1047 + }*/
1041 1048
1042 1049 function entityAliasesUpdated() {
1043   - var deferred = $q.defer();
1044   - entityService.processEntityAliases(vm.dashboard.configuration.entityAliases)
1045   - .then(
1046   - function(resolution) {
1047   - if (resolution.aliasesInfo) {
1048   - vm.dashboardCtx.aliasesInfo = resolution.aliasesInfo;
1049   - }
1050   - deferred.resolve();
1051   - }
1052   - );
1053   - return deferred.promise;
  1050 + vm.dashboardCtx.aliasController.updateEntityAliases(vm.dashboard.configuration.entityAliases);
1054 1051 }
1055 1052
1056 1053 function notifyDashboardUpdated() {
... ...
... ... @@ -57,8 +57,7 @@
57 57 </tb-timewindow>
58 58 <tb-aliases-entity-select ng-show="!vm.isEdit && vm.displayEntitiesSelect()"
59 59 tooltip-direction="bottom"
60   - ng-model="vm.dashboardCtx.aliasesInfo.entityAliases"
61   - entity-aliases-info="vm.dashboardCtx.aliasesInfo.entityAliasesInfo">
  60 + alias-controller="vm.dashboardCtx.aliasController">
62 61 </tb-aliases-entity-select>
63 62 <md-button ng-show="vm.isEdit" aria-label="{{ 'entity.aliases' | translate }}" class="md-icon-button"
64 63 ng-click="vm.openEntityAliases($event)">
... ... @@ -179,7 +178,7 @@
179 178 <form name="vm.widgetForm" ng-if="vm.isEditingWidget">
180 179 <tb-edit-widget
181 180 dashboard="vm.dashboard"
182   - aliases-info="vm.dashboardCtx.aliasesInfo"
  181 + alias-controller="vm.dashboardCtx.aliasController"
183 182 widget="vm.editingWidget"
184 183 widget-layout="vm.editingWidgetLayout"
185 184 the-form="vm.widgetForm">
... ...
... ... @@ -68,18 +68,34 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
68 68 });
69 69
70 70 scope.fetchEntityKeys = function (entityAliasId, query, type) {
71   - var entityAlias = scope.aliasesInfo.entityAliases[entityAliasId];
72   - if (entityAlias && entityAlias.entityId) {
73   - return entityService.getEntityKeys(entityAlias.entityType, entityAlias.entityId, query, type);
74   - } else {
75   - return $q.when([]);
76   - }
  71 + var deferred = $q.defer();
  72 + scope.aliasController.getAliasInfo(entityAliasId).then(
  73 + function success(aliasInfo) {
  74 + var entity = aliasInfo.currentEntity;
  75 + if (entity) {
  76 + entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
  77 + function success(keys) {
  78 + deferred.resolve(keys);
  79 + },
  80 + function fail() {
  81 + deferred.resolve([]);
  82 + }
  83 + );
  84 + } else {
  85 + deferred.resolve([]);
  86 + }
  87 + },
  88 + function fail() {
  89 + deferred.resolve([]);
  90 + }
  91 + );
  92 + return deferred.promise;
77 93 };
78 94
79 95 scope.createEntityAlias = function (event, alias, allowedEntityTypes) {
80 96
81 97 var deferred = $q.defer();
82   - var singleEntityAlias = {id: null, alias: alias, entityType: types.entityType.device, entityFilter: null};
  98 + var singleEntityAlias = {id: null, alias: alias, filter: {}};
83 99
84 100 $mdDialog.show({
85 101 controller: 'EntityAliasesController',
... ... @@ -99,16 +115,9 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
99 115 skipHide: true,
100 116 targetEvent: event
101 117 }).then(function (singleEntityAlias) {
102   - scope.dashboard.configuration.entityAliases[singleEntityAlias.id] =
103   - { alias: singleEntityAlias.alias, entityType: singleEntityAlias.entityType, entityFilter: singleEntityAlias.entityFilter };
104   - entityService.processEntityAliases(scope.dashboard.configuration.entityAliases).then(
105   - function(resolution) {
106   - if (!resolution.error) {
107   - scope.aliasesInfo = resolution.aliasesInfo;
108   - }
109   - deferred.resolve(singleEntityAlias);
110   - }
111   - );
  118 + scope.dashboard.configuration.entityAliases[singleEntityAlias.id] = singleEntityAlias;
  119 + scope.aliasController.updateEntityAliases(scope.dashboard.configuration.entityAliases);
  120 + deferred.resolve(singleEntityAlias);
112 121 }, function () {
113 122 deferred.reject();
114 123 });
... ... @@ -124,7 +133,7 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
124 133 link: linker,
125 134 scope: {
126 135 dashboard: '=',
127   - aliasesInfo: '=',
  136 + aliasController: '=',
128 137 widget: '=',
129 138 widgetLayout: '=',
130 139 theForm: '='
... ...
... ... @@ -21,7 +21,7 @@
21 21 is-data-enabled="isDataEnabled"
22 22 widget-settings-schema="settingsSchema"
23 23 datakey-settings-schema="dataKeySettingsSchema"
24   - entity-aliases="aliasesInfo.entityAliases"
  24 + alias-controller="aliasController"
25 25 functions-only="functionsOnly"
26 26 fetch-entity-keys="fetchEntityKeys(entityAliasId, query, type)"
27 27 on-create-entity-alias="createEntityAlias(event, alias, allowedEntityTypes)"
... ...
... ... @@ -45,7 +45,7 @@
45 45 widget-layouts="vm.layoutCtx.widgetLayouts"
46 46 columns="vm.layoutCtx.gridSettings.columns"
47 47 margins="vm.layoutCtx.gridSettings.margins"
48   - aliases-info="vm.dashboardCtx.aliasesInfo"
  48 + alias-controller="vm.dashboardCtx.aliasController"
49 49 state-controller="vm.dashboardCtx.stateController"
50 50 dashboard-timewindow="vm.dashboardCtx.dashboardTimewindow"
51 51 is-edit="vm.isEdit"
... ...
... ... @@ -15,17 +15,30 @@
15 15 */
16 16
17 17 /*@ngInject*/
18   -export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, types, entityAliases, entityAliasesInfo, onEntityAliasesUpdate) {
  18 +export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, types, aliasController, onEntityAliasesUpdate) {
19 19
20 20 var vm = this;
21 21 vm._mdPanelRef = mdPanelRef;
22   - vm.entityAliases = entityAliases;
23   - vm.entityAliasesInfo = entityAliasesInfo;
  22 + vm.aliasController = aliasController;
24 23 vm.onEntityAliasesUpdate = onEntityAliasesUpdate;
  24 + vm.entityAliases = {};
  25 + vm.entityAliasesInfo = {};
25 26
26   - $scope.$watch('vm.entityAliases', function () {
  27 + vm.currentAliasEntityChanged = currentAliasEntityChanged;
  28 +
  29 + var allEntityAliases = vm.aliasController.getEntityAliases();
  30 + for (var aliasId in allEntityAliases) {
  31 + var aliasInfo = vm.aliasController.getInstantAliasInfo(aliasId);
  32 + if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity) {
  33 + vm.entityAliasesInfo[aliasId] = angular.copy(aliasInfo);
  34 + }
  35 + }
  36 +
  37 + function currentAliasEntityChanged(aliasId, currentEntity) {
  38 + vm.aliasController.updateCurrentAliasEntity(aliasId, currentEntity);
27 39 if (onEntityAliasesUpdate) {
28   - onEntityAliasesUpdate(vm.entityAliases);
  40 + onEntityAliasesUpdate();
29 41 }
30   - }, true);
  42 + }
  43 +
31 44 }
... ...
... ... @@ -18,12 +18,12 @@
18 18 <md-content flex layout="column">
19 19 <section flex layout="column">
20 20 <md-content flex class="md-padding" layout="column">
21   - <div flex layout="row" ng-repeat="(aliasId, entityAlias) in vm.entityAliases">
  21 + <div flex layout="row" ng-repeat="(aliasId, entityAliasInfo) in vm.entityAliasesInfo">
22 22 <md-input-container flex>
23   - <label>{{entityAlias.alias}}</label>
24   - <md-select ng-model="vm.entityAliases[aliasId].entityId">
25   - <md-option ng-repeat="entityInfo in vm.entityAliasesInfo[aliasId]" ng-value="entityInfo.id">
26   - {{entityInfo.name}}
  23 + <label>{{entityAliasInfo.alias}}</label>
  24 + <md-select ng-model="entityAliasInfo.currentEntity" ng-change="vm.currentAliasEntityChanged(aliasId, entityAliasInfo.currentEntity)">
  25 + <md-option ng-repeat="resolvedEntity in entityAliasInfo.resolvedEntities" ng-value="resolvedEntity">
  26 + {{resolvedEntity.name}}
27 27 </md-option>
28 28 </md-select>
29 29 </md-input-container>
... ...
... ... @@ -29,7 +29,7 @@ import aliasesEntitySelectPanelTemplate from './aliases-entity-select-panel.tpl.
29 29 /*@ngInject*/
30 30 export default function AliasesEntitySelectDirective($compile, $templateCache, $mdMedia, types, $mdPanel, $document, $translate) {
31 31
32   - var linker = function (scope, element, attrs, ngModelCtrl) {
  32 + var linker = function (scope, element, attrs) {
33 33
34 34 /* tbAliasesEntitySelect (ng-model)
35 35 * {
... ... @@ -81,10 +81,8 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $
81 81 position: position,
82 82 fullscreen: false,
83 83 locals: {
84   - 'entityAliases': angular.copy(scope.model),
85   - 'entityAliasesInfo': scope.entityAliasesInfo,
86   - 'onEntityAliasesUpdate': function (entityAliases) {
87   - scope.model = entityAliases;
  84 + 'aliasController': scope.aliasController,
  85 + 'onEntityAliasesUpdate': function () {
88 86 scope.updateView();
89 87 }
90 88 },
... ... @@ -97,40 +95,31 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $
97 95 }
98 96
99 97 scope.updateView = function () {
100   - var value = angular.copy(scope.model);
101   - ngModelCtrl.$setViewValue(value);
102 98 updateDisplayValue();
103 99 }
104 100
105   - ngModelCtrl.$render = function () {
106   - if (ngModelCtrl.$viewValue) {
107   - var value = ngModelCtrl.$viewValue;
108   - scope.model = angular.copy(value);
109   - updateDisplayValue();
110   - }
111   - }
112   -
113 101 function updateDisplayValue() {
114 102 var displayValue;
115 103 var singleValue = true;
116 104 var currentAliasId;
117   - for (var aliasId in scope.model) {
118   - if (!currentAliasId) {
119   - currentAliasId = aliasId;
120   - } else {
121   - singleValue = false;
122   - break;
  105 + var entityAliases = scope.aliasController.getEntityAliases();
  106 + for (var aliasId in entityAliases) {
  107 + var entityAlias = entityAliases[aliasId];
  108 + if (!entityAlias.filter.resolveMultiple) {
  109 + var resolvedAlias = scope.aliasController.getInstantAliasInfo(aliasId);
  110 + if (resolvedAlias && resolvedAlias.currentEntity) {
  111 + if (!currentAliasId) {
  112 + currentAliasId = aliasId;
  113 + } else {
  114 + singleValue = false;
  115 + break;
  116 + }
  117 + }
123 118 }
124 119 }
125 120 if (singleValue && currentAliasId) {
126   - var entityId = scope.model[currentAliasId].entityId;
127   - var entitiesInfo = scope.entityAliasesInfo[currentAliasId];
128   - for (var i=0;i<entitiesInfo.length;i++) {
129   - if (entitiesInfo[i].id === entityId) {
130   - displayValue = entitiesInfo[i].name;
131   - break;
132   - }
133   - }
  121 + var aliasInfo = scope.aliasController.getInstantAliasInfo(currentAliasId);
  122 + displayValue = aliasInfo.currentEntity.name;
134 123 } else {
135 124 displayValue = $translate.instant('entity.entities');
136 125 }
... ... @@ -142,9 +131,8 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $
142 131
143 132 return {
144 133 restrict: "E",
145   - require: "^ngModel",
146 134 scope: {
147   - entityAliasesInfo:'='
  135 + aliasController:'='
148 136 },
149 137 link: linker
150 138 };
... ...
... ... @@ -85,32 +85,29 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
85 85
86 86 for (aliasId in config.entityAliases) {
87 87 var entityAlias = config.entityAliases[aliasId];
88   - var result = {id: aliasId, alias: entityAlias.alias, entityType: entityAlias.entityType, entityFilter: entityAlias.entityFilter, changed: true};
  88 + var result = {id: aliasId, alias: entityAlias.alias, filter: entityAlias.filter, changed: true};
89 89 checkEntityAlias(result);
90 90 vm.entityAliases.push(result);
91 91 }
92 92 }
93 93
94 94 function checkEntityAlias(entityAlias) {
95   - if (!entityAlias.entityType) {
96   - entityAlias.entityType = types.entityType.device;
97   - }
98   - if (!entityAlias.entityFilter || entityAlias.entityFilter == null) {
99   - entityAlias.entityFilter = {
100   - useFilter: false,
101   - entityNameFilter: '',
102   - entityList: [],
103   - };
  95 + if (!entityAlias.filter || entityAlias.filter == null) {
  96 + entityAlias.filter = {};
104 97 }
105 98 }
106 99
107   - function onFilterEntityChanged(entity, entityAlias) {
  100 + function onFilterEntityChanged(entity, stateEntity, entityAlias) {
108 101 if (entityAlias) {
109 102 if (!entityAlias.alias || entityAlias.alias.length == 0) {
110 103 entityAlias.changed = false;
111 104 }
112   - if (!entityAlias.changed && entity && entityAlias.entityType) {
113   - entityAlias.alias = entity.name;
  105 + if (!entityAlias.changed && entityAlias.filter && entityAlias.filter.type) {
  106 + if (stateEntity) {
  107 + entityAlias.alias = $translate.instant('alias.state-entity');
  108 + } else {
  109 + entityAlias.alias = entity.name;
  110 + }
114 111 }
115 112 }
116 113 }
... ... @@ -121,8 +118,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
121 118 aliasId = Math.max(vm.entityAliases[a].id, aliasId);
122 119 }
123 120 aliasId++;
124   - var entityAlias = {id: aliasId, alias: '', entityType: types.entityType.device,
125   - entityFilter: {useFilter: false, entityNameFilter: '', entityList: []}, changed: false};
  121 + var entityAlias = {id: aliasId, alias: '', filter: {}, changed: false};
126 122 vm.entityAliases.push(entityAlias);
127 123 }
128 124
... ... @@ -160,15 +156,6 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
160 156 $mdDialog.cancel();
161 157 }
162 158
163   - function cleanupEntityFilter(entityFilter) {
164   - if (entityFilter.useFilter) {
165   - entityFilter.entityList = [];
166   - } else {
167   - entityFilter.entityNameFilter = '';
168   - }
169   - return entityFilter;
170   - }
171   -
172 159 function save() {
173 160
174 161 var entityAliases = {};
... ... @@ -181,7 +168,6 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
181 168
182 169 if (vm.isSingleEntityAlias) {
183 170 maxAliasId = 0;
184   - vm.singleEntityAlias.entityFilter = cleanupEntityFilter(vm.singleEntityAlias.entityFilter);
185 171 for (i = 0; i < vm.entityAliases.length; i ++) {
186 172 aliasId = vm.entityAliases[i].id;
187 173 alias = vm.entityAliases[i].alias;
... ... @@ -199,7 +185,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
199 185 alias = vm.entityAliases[i].alias;
200 186 if (!uniqueAliasList[alias]) {
201 187 uniqueAliasList[alias] = alias;
202   - entityAliases[aliasId] = {alias: alias, entityType: vm.entityAliases[i].entityType, entityFilter: cleanupEntityFilter(vm.entityAliases[i].entityFilter)};
  188 + entityAliases[aliasId] = {id: aliasId, alias: alias, filter: vm.entityAliases[i].filter};
203 189 } else {
204 190 valid = false;
205 191 break;
... ...
... ... @@ -32,20 +32,15 @@
32 32 <div class="md-dialog-content">
33 33 <fieldset ng-disabled="loading">
34 34 <div ng-show="vm.isSingleEntityAlias" layout="row">
35   - <tb-entity-type-select style="min-width: 100px;"
36   - ng-model="vm.singleEntityAlias.entityType"
37   - allowed-entity-types="vm.allowedEntityTypes">
38   - </tb-entity-type-select>
39   - <tb-entity-filter flex entity-type="vm.singleEntityAlias.entityType" ng-model="vm.singleEntityAlias.entityFilter">
  35 + <tb-entity-filter flex allowed-entity-types="vm.allowedEntityTypes" ng-model="vm.singleEntityAlias.filter">
40 36 </tb-entity-filter>
41 37 </div>
42 38 <div ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center">
43 39 <span flex="5"></span>
44 40 <div flex layout="row" layout-align="start center"
45 41 style="padding: 0 0 0 10px; margin: 5px;">
46   - <span translate flex="20" style="min-width: 100px;">entity.alias</span>
47   - <span translate flex="20" style="min-width: 100px;">entity.type</span>
48   - <span translate flex="60" style="min-width: 190px; padding-left: 10px;">entity.entities</span>
  42 + <span translate flex="20" style="min-width: 150px;">entity.alias</span>
  43 + <span translate flex="80" style="min-width: 240px; padding-left: 10px;">alias.entity-filter</span>
49 44 <span style="min-width: 40px;"></span>
50 45 </div>
51 46 </div>
... ... @@ -53,23 +48,17 @@
53 48 <div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">
54 49 <span flex="5">{{$index + 1}}.</span>
55 50 <div class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center">
56   - <md-input-container flex="20" style="min-width: 100px;" md-no-float class="md-block">
  51 + <md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block">
57 52 <input required ng-change="entityAlias.changed=true" name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias">
58 53 <div ng-messages="aliasForm.alias.$error">
59 54 <div translate ng-message="required">entity.alias-required</div>
60 55 </div>
61 56 </md-input-container>
62   - <section flex="20" layout="column" style="min-width: 100px;" >
63   - <tb-entity-type-select hide-label style="padding-left: 10px;"
64   - ng-model="entityAlias.entityType"
65   - allowed-entity-types="vm.allowedEntityTypes">
66   - </tb-entity-type-select>
67   - </section>
68   - <section flex="60" layout="column">
  57 + <section flex="80" layout="column">
69 58 <tb-entity-filter style="padding-left: 10px;"
70   - entity-type="entityAlias.entityType"
71   - ng-model="entityAlias.entityFilter"
72   - on-matching-entity-change="vm.onFilterEntityChanged(entity, entityAlias)">
  59 + allowed-entity-types="vm.allowedEntityTypes"
  60 + ng-model="entityAlias.filter"
  61 + on-matching-entity-change="vm.onFilterEntityChanged(entity, stateEntity, entityAlias)">
73 62 </tb-entity-filter>
74 63 </section>
75 64 <md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
... ...
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +/*@ngInject*/
  18 +export default function EntityFilterDialogController($scope, $mdDialog, $q, entityService, types, isAdd, allowedEntityTypes, filter) {
  19 +
  20 + var vm = this;
  21 +
  22 + vm.types = types;
  23 + vm.isAdd = isAdd;
  24 + vm.allowedEntityTypes = allowedEntityTypes;
  25 + vm.filter = filter;
  26 +
  27 + vm.cancel = cancel;
  28 + vm.save = save;
  29 +
  30 + $scope.$watch('vm.filter.type', function (newType, prevType) {
  31 + if (newType && newType != prevType) {
  32 + updateFilter();
  33 + }
  34 + });
  35 +
  36 + $scope.$watch('theForm.$pristine', function() {
  37 + if ($scope.theForm && !$scope.theForm.$pristine) {
  38 + $scope.theForm.$setValidity('entityFilter', true);
  39 + }
  40 + });
  41 +
  42 + function updateFilter() {
  43 + var filter = {};
  44 + filter.type = vm.filter.type;
  45 + filter.resolveMultiple = vm.filter.resolveMultiple;
  46 + switch (filter.type) {
  47 + case types.aliasFilterType.entityList.value:
  48 + filter.entityType = null;
  49 + filter.entityList = [];
  50 + filter.stateEntity = false;
  51 + break;
  52 + case types.aliasFilterType.entityName.value:
  53 + filter.entityType = null;
  54 + filter.entityNameFilter = '';
  55 + break;
  56 + //TODO:
  57 + }
  58 + vm.filter = filter;
  59 + }
  60 +
  61 + function validate() {
  62 + var deferred = $q.defer();
  63 + var validationResult = {
  64 + entity: null,
  65 + stateEntity: false
  66 + }
  67 + entityService.resolveAliasFilter(vm.filter).then(
  68 + function success(result) {
  69 + validationResult.stateEntity = result.stateEntity;
  70 + var entities = result.entities;
  71 + if (entities.length) {
  72 + validationResult.entity = entities[0];
  73 + }
  74 + deferred.resolve(validationResult);
  75 + },
  76 + function fail() {
  77 + deferred.reject();
  78 + }
  79 + );
  80 + return deferred.promise;
  81 + }
  82 +
  83 + function cancel() {
  84 + $mdDialog.cancel();
  85 + }
  86 +
  87 + function save() {
  88 + $scope.theForm.$setPristine();
  89 + validate().then(
  90 + function success(validationResult) {
  91 + $mdDialog.hide({
  92 + filter: vm.filter,
  93 + entity: validationResult.entity,
  94 + stateEntity: validationResult.stateEntity
  95 + });
  96 + },
  97 + function fail() {
  98 + $scope.theForm.$setValidity('entityFilter', false);
  99 + }
  100 + )
  101 + }
  102 +
  103 +}
  104 +
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<md-dialog class="tb-entity-filter-dialog" style="width: 600px;" aria-label="{{ 'alias.entity-filter' | translate }}">
  19 + <form name="theForm" ng-submit="vm.save()">
  20 + <md-toolbar>
  21 + <div class="md-toolbar-tools">
  22 + <h2>{{ (vm.isAdd ? 'alias.create-entity-filter' : 'alias.edit-entity-filter') | translate }}</h2>
  23 + <span flex></span>
  24 + <md-button class="md-icon-button" ng-click="vm.cancel()">
  25 + <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
  26 + </md-button>
  27 + </div>
  28 + </md-toolbar>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  31 + <md-dialog-content>
  32 + <div class="md-dialog-content">
  33 + <fieldset ng-disabled="loading">
  34 + <div flex layout="column">
  35 + <md-input-container>
  36 + <label>{{ 'alias.filter-type' | translate }}</label>
  37 + <md-select required name="filterType"
  38 + ng-model="vm.filter.type" aria-label="{{ 'alias.filter-type' | translate }}">
  39 + <md-option ng-repeat="type in vm.types.aliasFilterType" ng-value="type.value">
  40 + {{type.name | translate}}
  41 + </md-option>
  42 + </md-select>
  43 + <div ng-messages="theForm.filterType.$error">
  44 + <div ng-message="required" translate>alias.filter-type-required</div>
  45 + </div>
  46 + </md-input-container>
  47 + <section layout="column" ng-if="vm.filter.type == vm.types.aliasFilterType.entityList.value" id="entityListFilter">
  48 + <md-checkbox flex aria-label="{{ 'alias.use-state-entity' | translate }}"
  49 + ng-model="vm.filter.stateEntity">{{ 'alias.use-state-entity' | translate }}
  50 + </md-checkbox>
  51 + <tb-entity-type-select
  52 + ng-if="!vm.filter.stateEntity"
  53 + ng-model="vm.filter.entityType"
  54 + the-form="theForm"
  55 + ng-disabled="vm.filter.stateEntity"
  56 + tb-required="!vm.filter.stateEntity"
  57 + allowed-entity-types="vm.allowedEntityTypes">
  58 + </tb-entity-type-select>
  59 + <tb-entity-list
  60 + ng-if="!vm.filter.stateEntity"
  61 + ng-model="vm.filter.entityList"
  62 + ng-disabled="vm.filter.stateEntity"
  63 + tb-required="!vm.filter.stateEntity"
  64 + entity-type="vm.filter.entityType">
  65 + </tb-entity-list>
  66 + </section>
  67 + <section flex layout="column" ng-if="vm.filter.type == vm.types.aliasFilterType.entityName.value" id="entityNameFilter">
  68 + <tb-entity-type-select
  69 + ng-model="vm.filter.entityType"
  70 + the-form="theForm"
  71 + tb-required="true"
  72 + allowed-entity-types="vm.allowedEntityTypes">
  73 + </tb-entity-type-select>
  74 + <md-input-container flex>
  75 + <label translate>entity.name-starts-with</label>
  76 + <input required name="entityNameFilter"
  77 + ng-model="vm.filter.entityNameFilter"
  78 + aria-label="{{ 'entity.name-starts-with' | translate }}">
  79 + <div ng-messages="theForm.entityNameFilter.$error">
  80 + <div ng-message="required" translate>entity.entity-name-filter-required</div>
  81 + </div>
  82 +
  83 + </md-input-container>
  84 + </section>
  85 + <div class="tb-error-messages" ng-messages="theForm.$error" role="alert">
  86 + <div translate ng-message="entityFilter" class="tb-error-message">alias.entity-filter-no-entity-matched</div>
  87 + </div>
  88 + </div>
  89 + </fieldset>
  90 + </div>
  91 + </md-dialog-content>
  92 + <md-dialog-actions layout="row">
  93 + <span flex></span>
  94 + <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
  95 + {{ 'action.save' | translate }}
  96 + </md-button>
  97 + <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  98 + </md-dialog-actions>
  99 + </form>
  100 +</md-dialog>
\ No newline at end of file
... ...
... ... @@ -17,13 +17,16 @@
17 17 /* eslint-disable import/no-unresolved, import/default */
18 18
19 19 import entityFilterTemplate from './entity-filter.tpl.html';
  20 +import entityFilterDialogTemplate from './entity-filter-dialog.tpl.html';
20 21
21 22 /* eslint-enable import/no-unresolved, import/default */
22 23
  24 +import EntityFilterDialogController from './entity-filter-dialog.controller';
  25 +
23 26 import './entity-filter.scss';
24 27
25 28 /*@ngInject*/
26   -export default function EntityFilterDirective($compile, $templateCache, $q, entityService) {
  29 +export default function EntityFilterDirective($compile, $templateCache, $q, $document, $mdDialog, types) {
27 30
28 31 var linker = function (scope, element, attrs, ngModelCtrl) {
29 32
... ... @@ -31,8 +34,9 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
31 34 element.html(template);
32 35
33 36 scope.ngModelCtrl = ngModelCtrl;
  37 + scope.types = types;
34 38
35   - scope.fetchEntities = function(searchText, limit) {
  39 + /* scope.fetchEntities = function(searchText, limit) {
36 40 var deferred = $q.defer();
37 41 entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(function success(result) {
38 42 if (result) {
... ... @@ -44,13 +48,13 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
44 48 deferred.reject();
45 49 });
46 50 return deferred.promise;
47   - }
  51 + }*/
48 52
49 53 scope.updateValidity = function() {
50 54 if (ngModelCtrl.$viewValue) {
51 55 var value = ngModelCtrl.$viewValue;
52   - var valid;
53   - if (value.useFilter) {
  56 + ngModelCtrl.$setValidity('filter', value.type ? true : false);
  57 + /*if (value.useFilter) {
54 58 ngModelCtrl.$setValidity('entityList', true);
55 59 if (angular.isDefined(value.entityNameFilter) && value.entityNameFilter.length > 0) {
56 60 ngModelCtrl.$setValidity('entityNameFilter', true);
... ... @@ -64,18 +68,22 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
64 68 ngModelCtrl.$setValidity('entityNameFilterDeviceMatch', true);
65 69 valid = angular.isDefined(value.entityList) && value.entityList.length > 0;
66 70 ngModelCtrl.$setValidity('entityList', valid);
67   - }
  71 + }*/
  72 +
68 73 }
69 74 }
70 75
71 76 ngModelCtrl.$render = function () {
72   - destroyWatchers();
73   - scope.model = {
74   - useFilter: false,
75   - entityList: [],
76   - entityNameFilter: ''
77   - }
  77 + //destroyWatchers();
78 78 if (ngModelCtrl.$viewValue) {
  79 + scope.model = angular.copy(ngModelCtrl.$viewValue);
  80 + } else {
  81 + scope.model = {
  82 + type: null,
  83 + resolveMultiple: false
  84 + }
  85 + }
  86 + /* if (ngModelCtrl.$viewValue) {
79 87 var value = ngModelCtrl.$viewValue;
80 88 var model = scope.model;
81 89 model.useFilter = value.useFilter === true ? true: false;
... ... @@ -96,10 +104,52 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
96 104 }
97 105 }
98 106 )
  107 + }*/
  108 + }
  109 +
  110 + scope.$watch('model.resolveMultiple', function () {
  111 + if (ngModelCtrl.$viewValue) {
  112 + var value = ngModelCtrl.$viewValue;
  113 + value.resolveMultiple = scope.model.resolveMultiple;
  114 + ngModelCtrl.$setViewValue(value);
  115 + scope.updateValidity();
99 116 }
  117 + });
  118 +
  119 + scope.editFilter = function($event) {
  120 + openEntityFilterDialog($event, false);
  121 + }
  122 +
  123 + scope.createFilter = function($event) {
  124 + openEntityFilterDialog($event, true);
100 125 }
101 126
102   - function updateMatchingEntity() {
  127 + function openEntityFilterDialog($event, isAdd) {
  128 + $mdDialog.show({
  129 + controller: EntityFilterDialogController,
  130 + controllerAs: 'vm',
  131 + templateUrl: entityFilterDialogTemplate,
  132 + locals: {
  133 + isAdd: isAdd,
  134 + allowedEntityTypes: scope.allowedEntityTypes,
  135 + filter: angular.copy(scope.model)
  136 + },
  137 + parent: angular.element($document[0].body),
  138 + fullscreen: true,
  139 + skipHide: true,
  140 + targetEvent: $event
  141 + }).then(function (result) {
  142 + scope.model = result.filter;
  143 + ngModelCtrl.$setViewValue(result.filter);
  144 + scope.updateValidity();
  145 + if (scope.onMatchingEntityChange) {
  146 + scope.onMatchingEntityChange({entity: result.entity, stateEntity: result.stateEntity});
  147 + }
  148 + }, function () {
  149 + });
  150 + }
  151 +
  152 + /* function updateMatchingEntity() {
103 153 if (scope.model.useFilter) {
104 154 scope.model.matchingEntity = scope.model.matchingFilterEntity;
105 155 } else {
... ... @@ -206,7 +256,7 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
206 256 }
207 257 }
208 258 });
209   - }
  259 + }*/
210 260
211 261 $compile(element.contents())(scope);
212 262
... ... @@ -217,8 +267,7 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
217 267 require: "^ngModel",
218 268 link: linker,
219 269 scope: {
220   - entityType: '=',
221   - isEdit: '=',
  270 + allowedEntityTypes: '=?',
222 271 onMatchingEntityChange: '&'
223 272 }
224 273 };
... ...
... ... @@ -17,7 +17,7 @@
17 17 -->
18 18 <section layout='column' class="tb-entity-filter">
19 19 <section layout='row'>
20   - <section layout="column" flex ng-show="!model.useFilter">
  20 + <!--section layout="column" flex ng-show="!model.useFilter">
21 21 <md-chips flex
22 22 id="entity_list_chips"
23 23 ng-required="!useFilter"
... ... @@ -51,17 +51,44 @@
51 51 <label translate>entity.name-starts-with</label>
52 52 <input ng-model="model.entityNameFilter" aria-label="{{ 'entity.name-starts-with' | translate }}">
53 53 </md-input-container>
  54 + </section-->
  55 + <section layout="row" flex layout-align="start center">
  56 + <div flex ng-if="model.type">{{ types.aliasFilterType[model.type].name | translate }}</div>
  57 + <md-button ng-if="model.type" ng-disabled="loading" class="md-icon-button md-primary"
  58 + style="min-width: 40px;"
  59 + ng-click="editFilter($event)"
  60 + aria-label="{{ 'alias.edit-entity-filter' | translate }}">
  61 + <md-tooltip md-direction="top">
  62 + {{ 'alias.edit-entity-filter' | translate }}
  63 + </md-tooltip>
  64 + <md-icon aria-label="{{ 'alias.edit-entity-filter' | translate }}"
  65 + class="material-icons">
  66 + edit
  67 + </md-icon>
  68 + </md-button>
  69 + <div ng-if="!model.type" layout="row" layout-align="center start">
  70 + <md-button ng-disabled="loading" class="md-primary md-raised"
  71 + ng-click="createFilter($event)"
  72 + aria-label="{{ 'alias.create-entity-filter' | translate }}">
  73 + <md-icon aria-label="{{ 'alias.create-entity-filter' | translate }}"
  74 + class="material-icons">
  75 + add
  76 + </md-icon>
  77 + {{ 'alias.create-entity-filter' | translate }}
  78 + </md-button>
  79 + </div>
54 80 </section>
55 81 <section class="tb-filter-switch" layout="column" layout-align="center center">
56   - <label class="tb-small filter-label" translate>entity.use-entity-name-filter</label>
57   - <md-switch class="filter-switch" ng-model="model.useFilter" aria-label="use-filter-switcher">
  82 + <label class="tb-small filter-label" translate>alias.resolve-multiple</label>
  83 + <md-switch class="filter-switch" ng-model="model.resolveMultiple" aria-label="resolve-multiple-switcher">
58 84 </md-switch>
59 85 </section>
60 86 </section>
61 87 <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert">
62   - <div translate ng-message="entityList" class="tb-error-message">entity.entity-list-empty</div>
  88 + <div translate ng-message="filter" class="tb-error-message">alias.entity-filter-required</div>
  89 + <!--div translate ng-message="entityList" class="tb-error-message">entity.entity-list-empty</div>
63 90 <div translate ng-message="entityNameFilter" class="tb-error-message">entity.entity-name-filter-required</div>
64 91 <div translate translate-values='{ entity: model.entityNameFilter }' ng-message="entityNameFilterEntityMatch"
65   - class="tb-error-message">entity.entity-name-filter-no-entity-matched</div>
  92 + class="tb-error-message">entity.entity-name-filter-no-entity-matched</div-->
66 93 </div>
67 94 </section>
\ No newline at end of file
... ...
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +/* eslint-disable import/no-unresolved, import/default */
  18 +
  19 +import entityListTemplate from './entity-list.tpl.html';
  20 +
  21 +/* eslint-enable import/no-unresolved, import/default */
  22 +
  23 +import './entity-list.scss';
  24 +
  25 +/*@ngInject*/
  26 +export default function EntityListDirective($compile, $templateCache, $q, $mdUtil, entityService) {
  27 +
  28 + var linker = function (scope, element, attrs, ngModelCtrl) {
  29 +
  30 + var template = $templateCache.get(entityListTemplate);
  31 + element.html(template);
  32 +
  33 + scope.ngModelCtrl = ngModelCtrl;
  34 +
  35 + scope.$watch('tbRequired', function () {
  36 + scope.updateValidity();
  37 + });
  38 +
  39 + scope.fetchEntities = function(searchText, limit) {
  40 + var deferred = $q.defer();
  41 + entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(
  42 + function success(result) {
  43 + if (result) {
  44 + deferred.resolve(result);
  45 + } else {
  46 + deferred.resolve([]);
  47 + }
  48 + },
  49 + function fail() {
  50 + deferred.reject();
  51 + }
  52 + );
  53 + return deferred.promise;
  54 + }
  55 +
  56 + scope.updateValidity = function() {
  57 + var value = ngModelCtrl.$viewValue;
  58 + var valid = !scope.tbRequired || value && value.length > 0;
  59 + ngModelCtrl.$setValidity('entityList', valid);
  60 + }
  61 +
  62 + ngModelCtrl.$render = function () {
  63 + destroyWatchers();
  64 + var value = ngModelCtrl.$viewValue;
  65 + scope.entityList = [];
  66 + if (value && value.length > 0) {
  67 + entityService.getEntities(scope.entityType, value).then(function (entities) {
  68 + scope.entityList = entities;
  69 + initWatchers();
  70 + });
  71 + } else {
  72 + initWatchers();
  73 + }
  74 + }
  75 +
  76 + function initWatchers() {
  77 + scope.entityTypeDeregistration = scope.$watch('entityType', function (newEntityType, prevEntityType) {
  78 + if (!angular.equals(newEntityType, prevEntityType)) {
  79 + scope.entityList = [];
  80 + }
  81 + });
  82 + scope.entityListDeregistration = scope.$watch('entityList', function () {
  83 + var ids = [];
  84 + if (scope.entityList && scope.entityList.length > 0) {
  85 + for (var i=0;i<scope.entityList.length;i++) {
  86 + ids.push(scope.entityList[i].id.id);
  87 + }
  88 + }
  89 + var value = ngModelCtrl.$viewValue;
  90 + if (!angular.equals(ids, value)) {
  91 + ngModelCtrl.$setViewValue(ids);
  92 + }
  93 + scope.updateValidity();
  94 + }, true);
  95 + }
  96 +
  97 + function destroyWatchers() {
  98 + if (scope.entityTypeDeregistration) {
  99 + scope.entityTypeDeregistration();
  100 + scope.entityTypeDeregistration = null;
  101 + }
  102 + if (scope.entityListDeregistration) {
  103 + scope.entityListDeregistration();
  104 + scope.entityListDeregistration = null;
  105 + }
  106 + }
  107 +
  108 + $compile(element.contents())(scope);
  109 +
  110 + $mdUtil.nextTick(function(){
  111 + var inputElement = angular.element('input', element);
  112 + inputElement.on('blur', function() {
  113 + scope.inputTouched = true;
  114 + } );
  115 + });
  116 +
  117 + }
  118 +
  119 + return {
  120 + restrict: "E",
  121 + require: "^ngModel",
  122 + link: linker,
  123 + scope: {
  124 + disabled:'=ngDisabled',
  125 + tbRequired: '=?',
  126 + entityType: '='
  127 + }
  128 + };
  129 +
  130 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +/*.tb-entity-list {
  18 + #entity_list_chips {
  19 + .md-chips {
  20 + padding-bottom: 1px;
  21 + }
  22 + }
  23 + .tb-error-messages {
  24 + margin-top: -11px;
  25 + height: 35px;
  26 + .tb-error-message {
  27 + padding-left: 1px;
  28 + }
  29 + }
  30 +}*/
\ No newline at end of file
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +
  19 +<section flex layout='column' class="tb-entity-list">
  20 + <md-chips flex
  21 + readonly="disabled"
  22 + id="entity_list_chips"
  23 + ng-required="tbRequired"
  24 + ng-model="entityList"
  25 + md-autocomplete-snap
  26 + md-require-match="true">
  27 + <md-autocomplete
  28 + md-no-cache="true"
  29 + id="entity"
  30 + md-selected-item="selectedEntity"
  31 + md-search-text="entitySearchText"
  32 + md-items="item in fetchEntities(entitySearchText, 10)"
  33 + md-item-text="item.name"
  34 + md-min-length="0"
  35 + placeholder="{{ 'entity.entity-list' | translate }}">
  36 + <md-item-template>
  37 + <span md-highlight-text="entitySearchText" md-highlight-flags="^i">{{item.name}}</span>
  38 + </md-item-template>
  39 + <md-not-found>
  40 + <span translate translate-values='{ entity: entitySearchText }'>entity.no-entities-matching</span>
  41 + </md-not-found>
  42 + </md-autocomplete>
  43 + <md-chip-template>
  44 + <span>
  45 + <strong>{{$chip.name}}</strong>
  46 + </span>
  47 + </md-chip-template>
  48 + </md-chips>
  49 + <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" ng-if="inputTouched" role="alert">
  50 + <div translate ng-message="entityList" class="tb-error-message">entity.entity-list-empty</div>
  51 + </div>
  52 +</section>
\ No newline at end of file
... ...
... ... @@ -19,6 +19,7 @@ import EntityTypeSelectDirective from './entity-type-select.directive';
19 19 import EntitySubtypeSelectDirective from './entity-subtype-select.directive';
20 20 import EntitySubtypeAutocompleteDirective from './entity-subtype-autocomplete.directive';
21 21 import EntityAutocompleteDirective from './entity-autocomplete.directive';
  22 +import EntityListDirective from './entity-list.directive';
22 23 import EntitySelectDirective from './entity-select.directive';
23 24 import EntityFilterDirective from './entity-filter.directive';
24 25 import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller';
... ... @@ -38,6 +39,7 @@ export default angular.module('thingsboard.entity', [])
38 39 .directive('tbEntitySubtypeSelect', EntitySubtypeSelectDirective)
39 40 .directive('tbEntitySubtypeAutocomplete', EntitySubtypeAutocompleteDirective)
40 41 .directive('tbEntityAutocomplete', EntityAutocompleteDirective)
  42 + .directive('tbEntityList', EntityListDirective)
41 43 .directive('tbEntitySelect', EntitySelectDirective)
42 44 .directive('tbEntityFilter', EntityFilterDirective)
43 45 .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective)
... ...
... ... @@ -112,6 +112,25 @@ export default angular.module('thingsboard.locale', [])
112 112 "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
113 113 "alarm-required": "Alarm is required"
114 114 },
  115 + "alias": {
  116 + "filter-type-entity-list": "Entity list",
  117 + "filter-type-entity-name": "Entity name",
  118 + "filter-type-asset-type": "Asset type",
  119 + "filter-type-device-type": "Device type",
  120 + "filter-type-relations-query": "Relations query",
  121 + "filter-type-asset-search-query": "Asset search query",
  122 + "filter-type-device-search-query": "Device search query",
  123 + "entity-filter": "Entity filter",
  124 + "create-entity-filter": "Create entity filter",
  125 + "edit-entity-filter": "Edit entity filter",
  126 + "entity-filter-required": "Entity filter is required.",
  127 + "resolve-multiple": "Multiple",
  128 + "filter-type": "Filter type",
  129 + "filter-type-required": "Filter type is required.",
  130 + "use-state-entity": "Use state entity",
  131 + "state-entity": "State entity",
  132 + "entity-filter-no-entity-matched": "No entities matching specified filter were found.",
  133 + },
115 134 "asset": {
116 135 "asset": "Asset",
117 136 "assets": "Assets",
... ...
... ... @@ -264,14 +264,9 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) {
264 264 }
265 265 dashboardUtils.addWidgetToLayout(theDashboard, targetState, targetLayout, widget, originalColumns, originalSize, row, column);
266 266 if (callAliasUpdateFunction) {
267   - onAliasesUpdateFunction().then(
268   - function() {
269   - deferred.resolve(theDashboard);
270   - }
271   - );
272   - } else {
273   - deferred.resolve(theDashboard);
  267 + onAliasesUpdateFunction();
274 268 }
  269 + deferred.resolve(theDashboard);
275 270 return deferred.promise;
276 271 }
277 272
... ...