committed by
GitHub
75 changed files with 3732 additions and 1254 deletions
@ -0,0 +1,279 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
const varsRegex = /\$\{([^\}]*)\}/g; |
||||
|
|
||||
|
export default class AliasController { |
||||
|
|
||||
|
constructor($scope, $q, $filter, utils, types, entityService, stateController, entityAliases) { |
||||
|
this.$scope = $scope; |
||||
|
this.$q = $q; |
||||
|
this.$filter = $filter; |
||||
|
this.utils = utils; |
||||
|
this.types = types; |
||||
|
this.entityService = entityService; |
||||
|
this.stateController = stateController; |
||||
|
this.entityAliases = angular.copy(entityAliases); |
||||
|
this.resolvedAliases = {}; |
||||
|
this.resolvedAliasesPromise = {}; |
||||
|
this.resolvedAliasesToStateEntities = {}; |
||||
|
} |
||||
|
|
||||
|
updateEntityAliases(newEntityAliases) { |
||||
|
var changedAliasIds = []; |
||||
|
for (var aliasId in newEntityAliases) { |
||||
|
var newEntityAlias = newEntityAliases[aliasId]; |
||||
|
var prevEntityAlias = this.entityAliases[aliasId]; |
||||
|
if (!angular.equals(newEntityAlias, prevEntityAlias)) { |
||||
|
changedAliasIds.push(aliasId); |
||||
|
this.setAliasUnresolved(aliasId); |
||||
|
} |
||||
|
} |
||||
|
for (aliasId in this.entityAliases) { |
||||
|
if (!newEntityAliases[aliasId]) { |
||||
|
changedAliasIds.push(aliasId); |
||||
|
this.setAliasUnresolved(aliasId); |
||||
|
} |
||||
|
} |
||||
|
this.entityAliases = angular.copy(newEntityAliases); |
||||
|
if (changedAliasIds.length) { |
||||
|
this.$scope.$broadcast('entityAliasesChanged', changedAliasIds); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
dashboardStateChanged() { |
||||
|
var newEntityId = this.stateController.getStateParams().entityId; |
||||
|
var changedAliasIds = []; |
||||
|
for (var aliasId in this.resolvedAliasesToStateEntities) { |
||||
|
var prevEntityId = this.resolvedAliasesToStateEntities[aliasId]; |
||||
|
if (!angular.equals(newEntityId, prevEntityId)) { |
||||
|
changedAliasIds.push(aliasId); |
||||
|
this.setAliasUnresolved(aliasId); |
||||
|
} |
||||
|
} |
||||
|
if (changedAliasIds.length) { |
||||
|
this.$scope.$broadcast('entityAliasesChanged', changedAliasIds); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
setAliasUnresolved(aliasId) { |
||||
|
delete this.resolvedAliases[aliasId]; |
||||
|
delete this.resolvedAliasesPromise[aliasId]; |
||||
|
delete this.resolvedAliasesToStateEntities[aliasId]; |
||||
|
} |
||||
|
|
||||
|
getEntityAliases() { |
||||
|
return this.entityAliases; |
||||
|
} |
||||
|
|
||||
|
getAliasInfo(aliasId) { |
||||
|
var deferred = this.$q.defer(); |
||||
|
var aliasInfo = this.resolvedAliases[aliasId]; |
||||
|
if (aliasInfo) { |
||||
|
deferred.resolve(aliasInfo); |
||||
|
return deferred.promise; |
||||
|
} else if (this.resolvedAliasesPromise[aliasId]) { |
||||
|
return this.resolvedAliasesPromise[aliasId]; |
||||
|
} else { |
||||
|
this.resolvedAliasesPromise[aliasId] = deferred.promise; |
||||
|
var aliasCtrl = this; |
||||
|
var entityAlias = this.entityAliases[aliasId]; |
||||
|
if (entityAlias) { |
||||
|
this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then( |
||||
|
function success(aliasInfo) { |
||||
|
aliasCtrl.resolvedAliases[aliasId] = aliasInfo; |
||||
|
if (aliasInfo.stateEntity) { |
||||
|
aliasCtrl.resolvedAliasesToStateEntities[aliasId] = |
||||
|
aliasCtrl.stateController.getStateParams().entityId; |
||||
|
} |
||||
|
aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId); |
||||
|
deferred.resolve(aliasInfo); |
||||
|
}, |
||||
|
function fail() { |
||||
|
deferred.reject(); |
||||
|
} |
||||
|
); |
||||
|
} else { |
||||
|
deferred.reject(); |
||||
|
} |
||||
|
return this.resolvedAliasesPromise[aliasId]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
resolveDatasource(datasource) { |
||||
|
var deferred = this.$q.defer(); |
||||
|
if (datasource.type === this.types.datasourceType.entity) { |
||||
|
if (datasource.entityAliasId) { |
||||
|
this.getAliasInfo(datasource.entityAliasId).then( |
||||
|
function success(aliasInfo) { |
||||
|
datasource.aliasName = aliasInfo.alias; |
||||
|
if (aliasInfo.resolveMultiple) { |
||||
|
var newDatasource; |
||||
|
var resolvedEntities = aliasInfo.resolvedEntities; |
||||
|
if (resolvedEntities && resolvedEntities.length) { |
||||
|
var datasources = []; |
||||
|
for (var i=0;i<resolvedEntities.length;i++) { |
||||
|
var resolvedEntity = resolvedEntities[i]; |
||||
|
newDatasource = angular.copy(datasource); |
||||
|
newDatasource.entityId = resolvedEntity.id; |
||||
|
newDatasource.entityType = resolvedEntity.entityType; |
||||
|
newDatasource.entityName = resolvedEntity.name; |
||||
|
newDatasource.name = resolvedEntity.name; |
||||
|
newDatasource.generated = i > 0 ? true : false; |
||||
|
datasources.push(newDatasource); |
||||
|
} |
||||
|
deferred.resolve(datasources); |
||||
|
} else { |
||||
|
if (aliasInfo.stateEntity) { |
||||
|
newDatasource = angular.copy(datasource); |
||||
|
newDatasource.unresolvedStateEntity = true; |
||||
|
deferred.resolve([newDatasource]); |
||||
|
} else { |
||||
|
deferred.reject(); |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
var entity = aliasInfo.currentEntity; |
||||
|
if (entity) { |
||||
|
datasource.entityId = entity.id; |
||||
|
datasource.entityType = entity.entityType; |
||||
|
datasource.entityName = entity.name; |
||||
|
datasource.name = entity.name; |
||||
|
deferred.resolve([datasource]); |
||||
|
} else { |
||||
|
if (aliasInfo.stateEntity) { |
||||
|
datasource.unresolvedStateEntity = true; |
||||
|
deferred.resolve([datasource]); |
||||
|
} else { |
||||
|
deferred.reject(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
function fail() { |
||||
|
deferred.reject(); |
||||
|
} |
||||
|
); |
||||
|
} else { // entityId
|
||||
|
datasource.aliasName = datasource.entityName; |
||||
|
datasource.name = datasource.entityName; |
||||
|
deferred.resolve([datasource]); |
||||
|
} |
||||
|
} else { // function
|
||||
|
deferred.resolve([datasource]); |
||||
|
} |
||||
|
return deferred.promise; |
||||
|
} |
||||
|
|
||||
|
resolveDatasources(datasources) { |
||||
|
|
||||
|
function updateDataKeyLabel(dataKey, datasource) { |
||||
|
if (!dataKey.pattern) { |
||||
|
dataKey.pattern = angular.copy(dataKey.label); |
||||
|
} |
||||
|
var pattern = dataKey.pattern; |
||||
|
var label = dataKey.pattern; |
||||
|
var match = varsRegex.exec(pattern); |
||||
|
while (match !== null) { |
||||
|
var variable = match[0]; |
||||
|
var variableName = match[1]; |
||||
|
if (variableName === 'dsName') { |
||||
|
label = label.split(variable).join(datasource.name); |
||||
|
} else if (variableName === 'entityName') { |
||||
|
label = label.split(variable).join(datasource.entityName); |
||||
|
} else if (variableName === 'deviceName') { |
||||
|
label = label.split(variable).join(datasource.entityName); |
||||
|
} else if (variableName === 'aliasName') { |
||||
|
label = label.split(variable).join(datasource.aliasName); |
||||
|
} |
||||
|
match = varsRegex.exec(pattern); |
||||
|
} |
||||
|
dataKey.label = label; |
||||
|
} |
||||
|
|
||||
|
function updateDatasourceKeyLabels(datasource) { |
||||
|
for (var dk = 0; dk < datasource.dataKeys.length; dk++) { |
||||
|
updateDataKeyLabel(datasource.dataKeys[dk], datasource); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var deferred = this.$q.defer(); |
||||
|
var newDatasources = angular.copy(datasources); |
||||
|
var datasorceResolveTasks = []; |
||||
|
var aliasCtrl = this; |
||||
|
newDatasources.forEach(function (datasource) { |
||||
|
var resolveDatasourceTask = aliasCtrl.resolveDatasource(datasource); |
||||
|
datasorceResolveTasks.push(resolveDatasourceTask); |
||||
|
}); |
||||
|
this.$q.all(datasorceResolveTasks).then( |
||||
|
function success(datasourcesArrays) { |
||||
|
var datasources = [].concat.apply([], datasourcesArrays); |
||||
|
datasources = aliasCtrl.$filter('orderBy')(datasources, '+generated'); |
||||
|
var index = 0; |
||||
|
var functionIndex = 0; |
||||
|
datasources.forEach(function(datasource) { |
||||
|
if (datasource.type === aliasCtrl.types.datasourceType.function) { |
||||
|
var name; |
||||
|
if (datasource.name && datasource.name.length) { |
||||
|
name = datasource.name; |
||||
|
} else { |
||||
|
functionIndex++; |
||||
|
name = aliasCtrl.types.datasourceType.function; |
||||
|
if (functionIndex > 1) { |
||||
|
name += ' ' + functionIndex; |
||||
|
} |
||||
|
} |
||||
|
datasource.name = name; |
||||
|
datasource.aliasName = name; |
||||
|
datasource.entityName = name; |
||||
|
} else if (datasource.unresolvedStateEntity) { |
||||
|
datasource.name = "Unresolved"; |
||||
|
datasource.entityName = "Unresolved"; |
||||
|
} |
||||
|
datasource.dataKeys.forEach(function(dataKey) { |
||||
|
if (datasource.generated) { |
||||
|
dataKey._hash = Math.random(); |
||||
|
dataKey.color = aliasCtrl.utils.getMaterialColor(index); |
||||
|
} |
||||
|
index++; |
||||
|
}); |
||||
|
updateDatasourceKeyLabels(datasource); |
||||
|
}); |
||||
|
deferred.resolve(datasources); |
||||
|
}, |
||||
|
function fail() { |
||||
|
deferred.reject(); |
||||
|
} |
||||
|
); |
||||
|
return deferred.promise; |
||||
|
} |
||||
|
|
||||
|
getInstantAliasInfo(aliasId) { |
||||
|
return this.resolvedAliases[aliasId]; |
||||
|
} |
||||
|
|
||||
|
updateCurrentAliasEntity(aliasId, currentEntity) { |
||||
|
var aliasInfo = this.resolvedAliases[aliasId]; |
||||
|
if (aliasInfo) { |
||||
|
var prevCurrentEntity = aliasInfo.currentEntity; |
||||
|
if (!angular.equals(currentEntity, prevCurrentEntity)) { |
||||
|
aliasInfo.currentEntity = currentEntity; |
||||
|
this.$scope.$broadcast('entityAliasesChanged', [aliasId]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, $filter, types, aliasController, onEntityAliasesUpdate) { |
||||
|
|
||||
|
var vm = this; |
||||
|
vm._mdPanelRef = mdPanelRef; |
||||
|
vm.aliasController = aliasController; |
||||
|
vm.onEntityAliasesUpdate = onEntityAliasesUpdate; |
||||
|
vm.entityAliases = {}; |
||||
|
vm.entityAliasesInfo = {}; |
||||
|
|
||||
|
vm.currentAliasEntityChanged = currentAliasEntityChanged; |
||||
|
|
||||
|
var allEntityAliases = vm.aliasController.getEntityAliases(); |
||||
|
for (var aliasId in allEntityAliases) { |
||||
|
var aliasInfo = vm.aliasController.getInstantAliasInfo(aliasId); |
||||
|
if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity) { |
||||
|
vm.entityAliasesInfo[aliasId] = angular.copy(aliasInfo); |
||||
|
vm.entityAliasesInfo[aliasId].selectedId = aliasInfo.currentEntity.id; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function currentAliasEntityChanged(aliasId, selectedId) { |
||||
|
var resolvedEntities = vm.entityAliasesInfo[aliasId].resolvedEntities; |
||||
|
var selected = $filter('filter')(resolvedEntities, {id: selectedId}); |
||||
|
if (selected && selected.length) { |
||||
|
vm.aliasController.updateCurrentAliasEntity(aliasId, selected[0]); |
||||
|
if (onEntityAliasesUpdate) { |
||||
|
onEntityAliasesUpdate(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,109 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
import './entity-alias-dialog.scss'; |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function EntityAliasDialogController($scope, $mdDialog, $q, $filter, utils, entityService, types, isAdd, allowedEntityTypes, entityAliases, alias) { |
||||
|
|
||||
|
var vm = this; |
||||
|
|
||||
|
vm.types = types; |
||||
|
vm.isAdd = isAdd; |
||||
|
vm.allowedEntityTypes = allowedEntityTypes; |
||||
|
if (angular.isArray(entityAliases)) { |
||||
|
vm.entityAliases = entityAliases; |
||||
|
} else { |
||||
|
vm.entityAliases = []; |
||||
|
for (var aliasId in entityAliases) { |
||||
|
vm.entityAliases.push(entityAliases[aliasId]); |
||||
|
} |
||||
|
} |
||||
|
if (vm.isAdd && !alias) { |
||||
|
vm.alias = { |
||||
|
alias: '', |
||||
|
filter: { |
||||
|
resolveMultiple: false |
||||
|
} |
||||
|
}; |
||||
|
} else { |
||||
|
vm.alias = alias; |
||||
|
} |
||||
|
|
||||
|
vm.cancel = cancel; |
||||
|
vm.save = save; |
||||
|
|
||||
|
$scope.$watch('vm.alias.alias', function (newAlias) { |
||||
|
if (newAlias) { |
||||
|
var valid = true; |
||||
|
var result = $filter('filter')(vm.entityAliases, {alias: newAlias}, true); |
||||
|
if (result && result.length) { |
||||
|
if (vm.isAdd || vm.alias.id != result[0].id) { |
||||
|
valid = false; |
||||
|
} |
||||
|
} |
||||
|
$scope.theForm.aliasName.$setValidity('duplicateAliasName', valid); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
$scope.$watch('theForm.$pristine', function() { |
||||
|
if ($scope.theForm && !$scope.theForm.$pristine) { |
||||
|
$scope.theForm.$setValidity('entityFilter', true); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
function validate() { |
||||
|
var deferred = $q.defer(); |
||||
|
var validationResult = { |
||||
|
entity: null, |
||||
|
stateEntity: false |
||||
|
} |
||||
|
entityService.resolveAliasFilter(vm.alias.filter, null, 1).then( |
||||
|
function success(result) { |
||||
|
validationResult.stateEntity = result.stateEntity; |
||||
|
var entities = result.entities; |
||||
|
if (entities.length) { |
||||
|
validationResult.entity = entities[0]; |
||||
|
} |
||||
|
deferred.resolve(validationResult); |
||||
|
}, |
||||
|
function fail() { |
||||
|
deferred.reject(); |
||||
|
} |
||||
|
); |
||||
|
return deferred.promise; |
||||
|
} |
||||
|
|
||||
|
function cancel() { |
||||
|
$mdDialog.cancel(); |
||||
|
} |
||||
|
|
||||
|
function save() { |
||||
|
$scope.theForm.$setPristine(); |
||||
|
validate().then( |
||||
|
function success() { |
||||
|
if (vm.isAdd) { |
||||
|
vm.alias.id = utils.guid(); |
||||
|
} |
||||
|
$mdDialog.hide(vm.alias); |
||||
|
}, |
||||
|
function fail() { |
||||
|
$scope.theForm.$setValidity('entityFilter', false); |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2017 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<md-dialog class="tb-entity-alias-dialog" style="width: 600px;" aria-label="{{ (vm.isAdd ? 'alias.add' : 'alias.edit') | translate }}"> |
||||
|
<form name="theForm" ng-submit="vm.save()"> |
||||
|
<md-toolbar> |
||||
|
<div class="md-toolbar-tools"> |
||||
|
<h2>{{ (vm.isAdd ? 'alias.add' : 'alias.edit') | translate }}</h2> |
||||
|
<span flex></span> |
||||
|
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
||||
|
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
||||
|
</md-button> |
||||
|
</div> |
||||
|
</md-toolbar> |
||||
|
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> |
||||
|
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> |
||||
|
<md-dialog-content> |
||||
|
<div class="md-dialog-content"> |
||||
|
<fieldset ng-disabled="loading"> |
||||
|
<div flex layout="column"> |
||||
|
<div layout="row"> |
||||
|
<md-input-container flex class="md-block"> |
||||
|
<label translate>alias.name</label> |
||||
|
<input required name="aliasName" |
||||
|
ng-model="vm.alias.alias" |
||||
|
aria-label="{{ 'alias.name' | translate }}"> |
||||
|
<div ng-messages="theForm.aliasName.$error"> |
||||
|
<div ng-message="required" translate>alias.name-required</div> |
||||
|
<div ng-message="duplicateAliasName" translate>alias.duplicate-alias</div> |
||||
|
</div> |
||||
|
</md-input-container> |
||||
|
<section class="tb-resolve-multiple-switch" layout="column" layout-align="start center"> |
||||
|
<label class="tb-small resolve-multiple-label" translate>alias.resolve-multiple</label> |
||||
|
<md-switch class="resolve-multiple-switch" ng-model="vm.alias.filter.resolveMultiple" |
||||
|
aria-label="{{ 'alias.resolve-multiple' | translate }}"> |
||||
|
</md-switch> |
||||
|
</section> |
||||
|
</div> |
||||
|
<tb-entity-filter |
||||
|
ng-model="vm.alias.filter" |
||||
|
allowed-entity-types="vm.allowedEntityTypes" |
||||
|
the-form="theForm"> |
||||
|
</tb-entity-filter> |
||||
|
<div class="tb-error-messages" ng-messages="theForm.$error" role="alert"> |
||||
|
<div translate ng-message="entityFilter" class="tb-error-message">alias.entity-filter-no-entity-matched</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</fieldset> |
||||
|
</div> |
||||
|
</md-dialog-content> |
||||
|
<md-dialog-actions layout="row"> |
||||
|
<span flex></span> |
||||
|
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> |
||||
|
{{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} |
||||
|
</md-button> |
||||
|
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> |
||||
|
</md-dialog-actions> |
||||
|
</form> |
||||
|
</md-dialog> |
||||
@ -0,0 +1,49 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
.tb-aliases-dialog { |
||||
|
.md-dialog-content { |
||||
|
padding-bottom: 0px; |
||||
|
padding-top: 0px; |
||||
|
} |
||||
|
.tb-aliases-header { |
||||
|
min-height: 40px; |
||||
|
padding: 0 34px 0 34px; |
||||
|
margin: 5px; |
||||
|
.tb-header-label { |
||||
|
font-size: 14px; |
||||
|
color: rgba(0, 0, 0, 0.570588); |
||||
|
} |
||||
|
} |
||||
|
.tb-alias { |
||||
|
padding: 0 0 0 10px; |
||||
|
margin: 5px; |
||||
|
md-input-container { |
||||
|
margin: 0px; |
||||
|
} |
||||
|
.tb-resolve-multiple-switch { |
||||
|
padding-left: 10px; |
||||
|
.resolve-multiple-switch { |
||||
|
margin: 0; |
||||
|
} |
||||
|
} |
||||
|
.md-button { |
||||
|
&.md-icon-button { |
||||
|
margin: 0px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,105 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2017 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<md-dialog class="tb-aliases-dialog" style="width: 700px;" aria-label="{{ vm.title | translate }}"> |
||||
|
<form name="theForm" ng-submit="vm.save()"> |
||||
|
<md-toolbar> |
||||
|
<div class="md-toolbar-tools"> |
||||
|
<h2>{{ vm.title | translate }}</h2> |
||||
|
<span flex></span> |
||||
|
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
||||
|
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
||||
|
</md-button> |
||||
|
</div> |
||||
|
</md-toolbar> |
||||
|
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> |
||||
|
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> |
||||
|
<div class="tb-aliases-header" flex layout="row" layout-align="start center"> |
||||
|
<span flex="5"></span> |
||||
|
<div flex layout="row" layout-align="start center"> |
||||
|
<span class="tb-header-label" translate flex="20" style="min-width: 150px;">alias.name</span> |
||||
|
<span class="tb-header-label" translate flex="70" style="padding-left: 10px;">alias.entity-filter</span> |
||||
|
<span class="tb-header-label" translate flex="10" style="padding-left: 10px; min-width: 120px;">alias.resolve-multiple</span> |
||||
|
<span style="min-width: 80px;"></span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<md-divider></md-divider> |
||||
|
<md-dialog-content> |
||||
|
<div class="md-dialog-content"> |
||||
|
<fieldset ng-disabled="loading"> |
||||
|
<div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index"> |
||||
|
<span flex="5">{{$index + 1}}.</span> |
||||
|
<di class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center"> |
||||
|
<md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block"> |
||||
|
<input required name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias"> |
||||
|
<div ng-messages="aliasForm.alias.$error"> |
||||
|
<div translate ng-message="required">entity.alias-required</div> |
||||
|
</div> |
||||
|
</md-input-container> |
||||
|
<tb-entity-filter-view |
||||
|
flex="70" style="padding-left: 10px;" |
||||
|
ng-model="entityAlias.filter"> |
||||
|
</tb-entity-filter-view> |
||||
|
<section flex="10" style="padding-left: 10px; min-width: 120px;" |
||||
|
class="tb-resolve-multiple-switch" |
||||
|
layout="column" |
||||
|
layout-align="center center"> |
||||
|
<md-switch class="resolve-multiple-switch" |
||||
|
ng-model="entityAlias.filter.resolveMultiple" |
||||
|
aria-label="resolve-multiple-switcher"> |
||||
|
</md-switch> |
||||
|
</section> |
||||
|
<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;" |
||||
|
ng-click="vm.editAlias($event, entityAlias)" aria-label="{{ 'action.edit' | translate }}"> |
||||
|
<md-tooltip md-direction="top"> |
||||
|
{{ 'alias.edit' | translate }} |
||||
|
</md-tooltip> |
||||
|
<md-icon aria-label="{{ 'alias.edit' | translate }}" class="material-icons"> |
||||
|
edit |
||||
|
</md-icon> |
||||
|
</md-button> |
||||
|
<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;" |
||||
|
ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}"> |
||||
|
<md-tooltip md-direction="top"> |
||||
|
{{ 'entity.remove-alias' | translate }} |
||||
|
</md-tooltip> |
||||
|
<md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons"> |
||||
|
close |
||||
|
</md-icon> |
||||
|
</md-button> |
||||
|
</di> |
||||
|
</div> |
||||
|
</fieldset> |
||||
|
</div> |
||||
|
</md-dialog-content> |
||||
|
<md-dialog-actions layout="row"> |
||||
|
<md-button ng-show="!vm.disableAdd" ng-disabled="loading" class="md-primary md-raised" |
||||
|
ng-click="vm.addAlias($event)" |
||||
|
aria-label="{{ 'alias.add' | translate }}"> |
||||
|
<md-tooltip md-direction="top"> |
||||
|
{{ 'alias.add' | translate }} |
||||
|
</md-tooltip> |
||||
|
<span translate>alias.add</span> |
||||
|
</md-button> |
||||
|
<span flex></span> |
||||
|
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> |
||||
|
{{ 'action.save' | translate }} |
||||
|
</md-button> |
||||
|
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> |
||||
|
</md-dialog-actions> |
||||
|
</form> |
||||
|
</md-dialog> |
||||
@ -1,106 +0,0 @@ |
|||||
<!-- |
|
||||
|
|
||||
Copyright © 2016-2017 The Thingsboard Authors |
|
||||
|
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
you may not use this file except in compliance with the License. |
|
||||
You may obtain a copy of the License at |
|
||||
|
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
|
||||
|
|
||||
Unless required by applicable law or agreed to in writing, software |
|
||||
distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
See the License for the specific language governing permissions and |
|
||||
limitations under the License. |
|
||||
|
|
||||
--> |
|
||||
<md-dialog class="tb-aliases-dialog" style="width: 700px;" aria-label="{{ vm.title | translate }}"> |
|
||||
<form name="theForm" ng-submit="vm.save()"> |
|
||||
<md-toolbar> |
|
||||
<div class="md-toolbar-tools"> |
|
||||
<h2>{{ vm.isSingleEntityAlias ? ('entity.configure-alias' | translate:vm.singleEntityAlias ) : (vm.title | translate) }}</h2> |
|
||||
<span flex></span> |
|
||||
<md-button class="md-icon-button" ng-click="vm.cancel()"> |
|
||||
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
|
||||
</md-button> |
|
||||
</div> |
|
||||
</md-toolbar> |
|
||||
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> |
|
||||
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> |
|
||||
<md-dialog-content> |
|
||||
<div class="md-dialog-content"> |
|
||||
<fieldset ng-disabled="loading"> |
|
||||
<div ng-show="vm.isSingleEntityAlias" layout="row"> |
|
||||
<tb-entity-type-select style="min-width: 100px;" |
|
||||
ng-model="vm.singleEntityAlias.entityType" |
|
||||
allowed-entity-types="vm.allowedEntityTypes"> |
|
||||
</tb-entity-type-select> |
|
||||
<tb-entity-filter flex entity-type="vm.singleEntityAlias.entityType" ng-model="vm.singleEntityAlias.entityFilter"> |
|
||||
</tb-entity-filter> |
|
||||
</div> |
|
||||
<div ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center"> |
|
||||
<span flex="5"></span> |
|
||||
<div flex layout="row" layout-align="start center" |
|
||||
style="padding: 0 0 0 10px; margin: 5px;"> |
|
||||
<span translate flex="20" style="min-width: 100px;">entity.alias</span> |
|
||||
<span translate flex="20" style="min-width: 100px;">entity.type</span> |
|
||||
<span translate flex="60" style="min-width: 190px; padding-left: 10px;">entity.entities</span> |
|
||||
<span style="min-width: 40px;"></span> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div ng-show="!vm.isSingleEntityAlias" style="max-height: 500px; overflow: auto; padding-bottom: 20px;"> |
|
||||
<div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index"> |
|
||||
<span flex="5">{{$index + 1}}.</span> |
|
||||
<div class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center"> |
|
||||
<md-input-container flex="20" style="min-width: 100px;" md-no-float class="md-block"> |
|
||||
<input required ng-change="entityAlias.changed=true" name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias"> |
|
||||
<div ng-messages="aliasForm.alias.$error"> |
|
||||
<div translate ng-message="required">entity.alias-required</div> |
|
||||
</div> |
|
||||
</md-input-container> |
|
||||
<section flex="20" layout="column" style="min-width: 100px;" > |
|
||||
<tb-entity-type-select hide-label style="padding-left: 10px;" |
|
||||
ng-model="entityAlias.entityType" |
|
||||
allowed-entity-types="vm.allowedEntityTypes"> |
|
||||
</tb-entity-type-select> |
|
||||
</section> |
|
||||
<section flex="60" layout="column"> |
|
||||
<tb-entity-filter style="padding-left: 10px;" |
|
||||
entity-type="entityAlias.entityType" |
|
||||
ng-model="entityAlias.entityFilter" |
|
||||
on-matching-entity-change="vm.onFilterEntityChanged(entity, entityAlias)"> |
|
||||
</tb-entity-filter> |
|
||||
</section> |
|
||||
<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;" |
|
||||
ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}"> |
|
||||
<md-tooltip md-direction="top"> |
|
||||
{{ 'entity.remove-alias' | translate }} |
|
||||
</md-tooltip> |
|
||||
<md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons"> |
|
||||
close |
|
||||
</md-icon> |
|
||||
</md-button> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div ng-show="!vm.isSingleEntityAlias && !vm.disableAdd" style="padding-bottom: 10px;"> |
|
||||
<md-button ng-disabled="loading" class="md-primary md-raised" ng-click="vm.addAlias($event)" aria-label="{{ 'action.add' | translate }}"> |
|
||||
<md-tooltip md-direction="top"> |
|
||||
{{ 'entity.add-alias' | translate }} |
|
||||
</md-tooltip> |
|
||||
<span translate>action.add</span> |
|
||||
</md-button> |
|
||||
</div> |
|
||||
</fieldset> |
|
||||
</div> |
|
||||
</md-dialog-content> |
|
||||
<md-dialog-actions layout="row"> |
|
||||
<span flex></span> |
|
||||
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> |
|
||||
{{ 'action.save' | translate }} |
|
||||
</md-button> |
|
||||
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> |
|
||||
</md-dialog-actions> |
|
||||
</form> |
|
||||
</md-dialog> |
|
||||
@ -0,0 +1,205 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
/* eslint-disable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import entityFilterViewTemplate from './entity-filter-view.tpl.html'; |
||||
|
|
||||
|
/* eslint-enable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import './entity-filter-view.scss'; |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function EntityFilterViewDirective($compile, $templateCache, $q, $document, $mdDialog, $translate, types/*, entityService*/) { |
||||
|
|
||||
|
var linker = function (scope, element, attrs, ngModelCtrl) { |
||||
|
|
||||
|
var template = $templateCache.get(entityFilterViewTemplate); |
||||
|
element.html(template); |
||||
|
|
||||
|
scope.ngModelCtrl = ngModelCtrl; |
||||
|
scope.types = types; |
||||
|
scope.filterDisplayValue = ''; |
||||
|
|
||||
|
scope.$watch('filter', function () { |
||||
|
scope.updateDisplayValue(); |
||||
|
}); |
||||
|
|
||||
|
scope.updateDisplayValue = function() { |
||||
|
if (scope.filter && scope.filter.type) { |
||||
|
var entityType; |
||||
|
var prefix; |
||||
|
switch (scope.filter.type) { |
||||
|
case types.aliasFilterType.entityList.value: |
||||
|
entityType = scope.filter.entityType; |
||||
|
var count = scope.filter.entityList.length; |
||||
|
scope.filterDisplayValue = $translate.instant(types.entityTypeTranslations[entityType].list, {count: count}, 'messageformat'); |
||||
|
break; |
||||
|
case types.aliasFilterType.entityName.value: |
||||
|
entityType = scope.filter.entityType; |
||||
|
prefix = scope.filter.entityNameFilter; |
||||
|
scope.filterDisplayValue = $translate.instant(types.entityTypeTranslations[entityType].nameStartsWith, {prefix: prefix}); |
||||
|
break; |
||||
|
case types.aliasFilterType.stateEntity.value: |
||||
|
scope.filterDisplayValue = $translate.instant('alias.filter-type-state-entity-description'); |
||||
|
break; |
||||
|
case types.aliasFilterType.assetType.value: |
||||
|
var assetType = scope.filter.assetType; |
||||
|
prefix = scope.filter.assetNameFilter; |
||||
|
if (prefix && prefix.length) { |
||||
|
scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-type-and-name-description', {assetType: assetType, prefix: prefix}); |
||||
|
} else { |
||||
|
scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-type-description', {assetType: assetType}); |
||||
|
} |
||||
|
break; |
||||
|
case types.aliasFilterType.deviceType.value: |
||||
|
var deviceType = scope.filter.deviceType; |
||||
|
prefix = scope.filter.deviceNameFilter; |
||||
|
if (prefix && prefix.length) { |
||||
|
scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-and-name-description', {deviceType: deviceType, prefix: prefix}); |
||||
|
} else { |
||||
|
scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-description', {deviceType: deviceType}); |
||||
|
} |
||||
|
break; |
||||
|
case types.aliasFilterType.relationsQuery.value: |
||||
|
var rootEntityText; |
||||
|
var directionText; |
||||
|
var allEntitiesText = $translate.instant('alias.all-entities'); |
||||
|
var anyRelationText = $translate.instant('alias.any-relation'); |
||||
|
if (scope.filter.rootStateEntity) { |
||||
|
rootEntityText = $translate.instant('alias.state-entity'); |
||||
|
} else { |
||||
|
rootEntityText = $translate.instant(types.entityTypeTranslations[scope.filter.rootEntity.entityType].type); |
||||
|
} |
||||
|
directionText = $translate.instant('relation.direction-type.' + scope.filter.direction); |
||||
|
var relationFilters = scope.filter.filters; |
||||
|
if (relationFilters && relationFilters.length) { |
||||
|
var relationFiltersDisplayValues = []; |
||||
|
relationFilters.forEach(function(relationFilter) { |
||||
|
var entitiesText; |
||||
|
if (relationFilter.entityTypes && relationFilter.entityTypes.length) { |
||||
|
var entitiesNamesList = []; |
||||
|
relationFilter.entityTypes.forEach(function(entityType) { |
||||
|
entitiesNamesList.push( |
||||
|
$translate.instant(types.entityTypeTranslations[entityType].typePlural) |
||||
|
); |
||||
|
}); |
||||
|
entitiesText = entitiesNamesList.join(', '); |
||||
|
} else { |
||||
|
entitiesText = allEntitiesText; |
||||
|
} |
||||
|
var relationTypeText; |
||||
|
if (relationFilter.relationType && relationFilter.relationType.length) { |
||||
|
relationTypeText = "'" + relationFilter.relationType + "'"; |
||||
|
} else { |
||||
|
relationTypeText = anyRelationText; |
||||
|
} |
||||
|
var relationFilterDisplayValue = $translate.instant('alias.filter-type-relations-query-description', |
||||
|
{ |
||||
|
entities: entitiesText, |
||||
|
relationType: relationTypeText, |
||||
|
direction: directionText, |
||||
|
rootEntity: rootEntityText |
||||
|
} |
||||
|
); |
||||
|
relationFiltersDisplayValues.push(relationFilterDisplayValue); |
||||
|
}); |
||||
|
scope.filterDisplayValue = relationFiltersDisplayValues.join(', '); |
||||
|
} else { |
||||
|
scope.filterDisplayValue = $translate.instant('alias.filter-type-relations-query-description', |
||||
|
{ |
||||
|
entities: allEntitiesText, |
||||
|
relationType: anyRelationText, |
||||
|
direction: directionText, |
||||
|
rootEntity: rootEntityText |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
break; |
||||
|
case types.aliasFilterType.assetSearchQuery.value: |
||||
|
case types.aliasFilterType.deviceSearchQuery.value: |
||||
|
allEntitiesText = $translate.instant('alias.all-entities'); |
||||
|
anyRelationText = $translate.instant('alias.any-relation'); |
||||
|
if (scope.filter.rootStateEntity) { |
||||
|
rootEntityText = $translate.instant('alias.state-entity'); |
||||
|
} else { |
||||
|
rootEntityText = $translate.instant(types.entityTypeTranslations[scope.filter.rootEntity.entityType].type); |
||||
|
} |
||||
|
directionText = $translate.instant('relation.direction-type.' + scope.filter.direction); |
||||
|
var relationTypeText; |
||||
|
if (scope.filter.relationType && scope.filter.relationType.length) { |
||||
|
relationTypeText = "'" + scope.filter.relationType + "'"; |
||||
|
} else { |
||||
|
relationTypeText = anyRelationText; |
||||
|
} |
||||
|
|
||||
|
var translationValues = { |
||||
|
relationType: relationTypeText, |
||||
|
direction: directionText, |
||||
|
rootEntity: rootEntityText |
||||
|
} |
||||
|
|
||||
|
if (scope.filter.type == types.aliasFilterType.assetSearchQuery.value) { |
||||
|
var assetTypesQuoted = []; |
||||
|
scope.filter.assetTypes.forEach(function(assetType) { |
||||
|
assetTypesQuoted.push("'"+assetType+"'"); |
||||
|
}); |
||||
|
var assetTypesText = assetTypesQuoted.join(', '); |
||||
|
translationValues.assetTypes = assetTypesText; |
||||
|
scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-search-query-description', |
||||
|
translationValues |
||||
|
); |
||||
|
} else { |
||||
|
var deviceTypesQuoted = []; |
||||
|
scope.filter.deviceTypes.forEach(function(deviceType) { |
||||
|
deviceTypesQuoted.push("'"+deviceType+"'"); |
||||
|
}); |
||||
|
var deviceTypesText = deviceTypesQuoted.join(', '); |
||||
|
translationValues.deviceTypes = deviceTypesText; |
||||
|
scope.filterDisplayValue = $translate.instant('alias.filter-type-device-search-query-description', |
||||
|
translationValues |
||||
|
); |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
scope.filterDisplayValue = scope.filter.type; |
||||
|
break; |
||||
|
} |
||||
|
} else { |
||||
|
scope.filterDisplayValue = ''; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ngModelCtrl.$render = function () { |
||||
|
if (ngModelCtrl.$viewValue) { |
||||
|
scope.filter = ngModelCtrl.$viewValue; |
||||
|
} else { |
||||
|
scope.filter = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$compile(element.contents())(scope); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
restrict: "E", |
||||
|
require: "^ngModel", |
||||
|
link: linker, |
||||
|
scope: true |
||||
|
}; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2017 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
|
||||
|
<div layout='column' class="tb-entity-filter-view"> |
||||
|
<div ng-if="!filter || !filter.type" class="entity-filter-empty" translate>alias.no-entity-filter-specified</div> |
||||
|
<div ng-if="filter && filter.type" layout="column"> |
||||
|
<div class="entity-filter-value">{{ filterDisplayValue }}</div> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,130 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
/* eslint-disable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import entityListTemplate from './entity-list.tpl.html'; |
||||
|
|
||||
|
/* eslint-enable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import './entity-list.scss'; |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function EntityListDirective($compile, $templateCache, $q, $mdUtil, entityService) { |
||||
|
|
||||
|
var linker = function (scope, element, attrs, ngModelCtrl) { |
||||
|
|
||||
|
var template = $templateCache.get(entityListTemplate); |
||||
|
element.html(template); |
||||
|
|
||||
|
scope.ngModelCtrl = ngModelCtrl; |
||||
|
|
||||
|
scope.$watch('tbRequired', function () { |
||||
|
scope.updateValidity(); |
||||
|
}); |
||||
|
|
||||
|
scope.fetchEntities = function(searchText, limit) { |
||||
|
var deferred = $q.defer(); |
||||
|
entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then( |
||||
|
function success(result) { |
||||
|
if (result) { |
||||
|
deferred.resolve(result); |
||||
|
} else { |
||||
|
deferred.resolve([]); |
||||
|
} |
||||
|
}, |
||||
|
function fail() { |
||||
|
deferred.reject(); |
||||
|
} |
||||
|
); |
||||
|
return deferred.promise; |
||||
|
} |
||||
|
|
||||
|
scope.updateValidity = function() { |
||||
|
var value = ngModelCtrl.$viewValue; |
||||
|
var valid = !scope.tbRequired || value && value.length > 0; |
||||
|
ngModelCtrl.$setValidity('entityList', valid); |
||||
|
} |
||||
|
|
||||
|
ngModelCtrl.$render = function () { |
||||
|
destroyWatchers(); |
||||
|
var value = ngModelCtrl.$viewValue; |
||||
|
scope.entityList = []; |
||||
|
if (value && value.length > 0) { |
||||
|
entityService.getEntities(scope.entityType, value).then(function (entities) { |
||||
|
scope.entityList = entities; |
||||
|
initWatchers(); |
||||
|
}); |
||||
|
} else { |
||||
|
initWatchers(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function initWatchers() { |
||||
|
scope.entityTypeDeregistration = scope.$watch('entityType', function (newEntityType, prevEntityType) { |
||||
|
if (!angular.equals(newEntityType, prevEntityType)) { |
||||
|
scope.entityList = []; |
||||
|
} |
||||
|
}); |
||||
|
scope.entityListDeregistration = scope.$watch('entityList', function () { |
||||
|
var ids = []; |
||||
|
if (scope.entityList && scope.entityList.length > 0) { |
||||
|
for (var i=0;i<scope.entityList.length;i++) { |
||||
|
ids.push(scope.entityList[i].id.id); |
||||
|
} |
||||
|
} |
||||
|
var value = ngModelCtrl.$viewValue; |
||||
|
if (!angular.equals(ids, value)) { |
||||
|
ngModelCtrl.$setViewValue(ids); |
||||
|
} |
||||
|
scope.updateValidity(); |
||||
|
}, true); |
||||
|
} |
||||
|
|
||||
|
function destroyWatchers() { |
||||
|
if (scope.entityTypeDeregistration) { |
||||
|
scope.entityTypeDeregistration(); |
||||
|
scope.entityTypeDeregistration = null; |
||||
|
} |
||||
|
if (scope.entityListDeregistration) { |
||||
|
scope.entityListDeregistration(); |
||||
|
scope.entityListDeregistration = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$compile(element.contents())(scope); |
||||
|
|
||||
|
$mdUtil.nextTick(function(){ |
||||
|
var inputElement = angular.element('input', element); |
||||
|
inputElement.on('blur', function() { |
||||
|
scope.inputTouched = true; |
||||
|
} ); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
restrict: "E", |
||||
|
require: "^ngModel", |
||||
|
link: linker, |
||||
|
scope: { |
||||
|
disabled:'=ngDisabled', |
||||
|
tbRequired: '=?', |
||||
|
entityType: '=' |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
/*.tb-entity-list { |
||||
|
#entity_list_chips { |
||||
|
.md-chips { |
||||
|
padding-bottom: 1px; |
||||
|
} |
||||
|
} |
||||
|
.tb-error-messages { |
||||
|
margin-top: -11px; |
||||
|
height: 35px; |
||||
|
.tb-error-message { |
||||
|
padding-left: 1px; |
||||
|
} |
||||
|
} |
||||
|
}*/ |
||||
@ -0,0 +1,52 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2017 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
|
||||
|
<section flex layout='column' class="tb-entity-list"> |
||||
|
<md-chips flex |
||||
|
readonly="disabled" |
||||
|
id="entity_list_chips" |
||||
|
ng-required="tbRequired" |
||||
|
ng-model="entityList" |
||||
|
md-autocomplete-snap |
||||
|
md-require-match="true"> |
||||
|
<md-autocomplete |
||||
|
md-no-cache="true" |
||||
|
id="entity" |
||||
|
md-selected-item="selectedEntity" |
||||
|
md-search-text="entitySearchText" |
||||
|
md-items="item in fetchEntities(entitySearchText, 10)" |
||||
|
md-item-text="item.name" |
||||
|
md-min-length="0" |
||||
|
placeholder="{{ 'entity.entity-list' | translate }}"> |
||||
|
<md-item-template> |
||||
|
<span md-highlight-text="entitySearchText" md-highlight-flags="^i">{{item.name}}</span> |
||||
|
</md-item-template> |
||||
|
<md-not-found> |
||||
|
<span translate translate-values='{ entity: entitySearchText }'>entity.no-entities-matching</span> |
||||
|
</md-not-found> |
||||
|
</md-autocomplete> |
||||
|
<md-chip-template> |
||||
|
<span> |
||||
|
<strong>{{$chip.name}}</strong> |
||||
|
</span> |
||||
|
</md-chip-template> |
||||
|
</md-chips> |
||||
|
<div class="tb-error-messages" ng-messages="ngModelCtrl.$error" ng-if="inputTouched" role="alert"> |
||||
|
<div translate ng-message="entityList" class="tb-error-message">entity.entity-list-empty</div> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,146 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
/* eslint-disable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import entitySubtypeListTemplate from './entity-subtype-list.tpl.html'; |
||||
|
|
||||
|
/* eslint-enable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import './entity-subtype-list.scss'; |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function EntitySubtypeListDirective($compile, $templateCache, $q, $mdUtil, $translate, $filter, types, assetService, deviceService) { |
||||
|
|
||||
|
var linker = function (scope, element, attrs, ngModelCtrl) { |
||||
|
|
||||
|
var template = $templateCache.get(entitySubtypeListTemplate); |
||||
|
element.html(template); |
||||
|
|
||||
|
scope.ngModelCtrl = ngModelCtrl; |
||||
|
|
||||
|
|
||||
|
scope.entitySubtypesList = []; |
||||
|
scope.entitySubtypes = null; |
||||
|
|
||||
|
if (scope.entityType == types.entityType.asset) { |
||||
|
scope.placeholder = scope.tbRequired ? $translate.instant('asset.enter-asset-type') |
||||
|
: $translate.instant('asset.any-asset'); |
||||
|
scope.secondaryPlaceholder = '+' + $translate.instant('asset.asset-type'); |
||||
|
scope.noSubtypesMathingText = 'asset.no-asset-types-matching'; |
||||
|
scope.subtypeListEmptyText = 'asset.asset-type-list-empty'; |
||||
|
} else if (scope.entityType == types.entityType.device) { |
||||
|
scope.placeholder = scope.tbRequired ? $translate.instant('device.enter-device-type') |
||||
|
: $translate.instant('device.any-device'); |
||||
|
scope.secondaryPlaceholder = '+' + $translate.instant('device.device-type'); |
||||
|
scope.noSubtypesMathingText = 'device.no-device-types-matching'; |
||||
|
scope.subtypeListEmptyText = 'device.device-type-list-empty'; |
||||
|
} |
||||
|
|
||||
|
scope.$watch('tbRequired', function () { |
||||
|
scope.updateValidity(); |
||||
|
}); |
||||
|
|
||||
|
scope.fetchEntitySubtypes = function(searchText) { |
||||
|
var deferred = $q.defer(); |
||||
|
loadSubTypes().then( |
||||
|
function success(subTypes) { |
||||
|
var result = $filter('filter')(subTypes, {'$': searchText}); |
||||
|
if (result && result.length) { |
||||
|
deferred.resolve(result); |
||||
|
} else { |
||||
|
deferred.resolve([searchText]); |
||||
|
} |
||||
|
}, |
||||
|
function fail() { |
||||
|
deferred.reject(); |
||||
|
} |
||||
|
); |
||||
|
return deferred.promise; |
||||
|
} |
||||
|
|
||||
|
scope.updateValidity = function() { |
||||
|
var value = ngModelCtrl.$viewValue; |
||||
|
var valid = !scope.tbRequired || value && value.length > 0; |
||||
|
ngModelCtrl.$setValidity('entitySubtypesList', valid); |
||||
|
} |
||||
|
|
||||
|
ngModelCtrl.$render = function () { |
||||
|
scope.entitySubtypesList = ngModelCtrl.$viewValue; |
||||
|
if (!scope.entitySubtypesList) { |
||||
|
scope.entitySubtypesList = []; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
scope.$watch('entitySubtypesList', function () { |
||||
|
ngModelCtrl.$setViewValue(scope.entitySubtypesList); |
||||
|
scope.updateValidity(); |
||||
|
}, true); |
||||
|
|
||||
|
function loadSubTypes() { |
||||
|
var deferred = $q.defer(); |
||||
|
if (!scope.entitySubtypes) { |
||||
|
var entitySubtypesPromise; |
||||
|
if (scope.entityType == types.entityType.asset) { |
||||
|
entitySubtypesPromise = assetService.getAssetTypes(); |
||||
|
} else if (scope.entityType == types.entityType.device) { |
||||
|
entitySubtypesPromise = deviceService.getDeviceTypes(); |
||||
|
} |
||||
|
if (entitySubtypesPromise) { |
||||
|
entitySubtypesPromise.then( |
||||
|
function success(types) { |
||||
|
scope.entitySubtypes = []; |
||||
|
types.forEach(function (type) { |
||||
|
scope.entitySubtypes.push(type.type); |
||||
|
}); |
||||
|
deferred.resolve(scope.entitySubtypes); |
||||
|
}, |
||||
|
function fail() { |
||||
|
deferred.reject(); |
||||
|
} |
||||
|
); |
||||
|
} else { |
||||
|
deferred.reject(); |
||||
|
} |
||||
|
} else { |
||||
|
deferred.resolve(scope.entitySubtypes); |
||||
|
} |
||||
|
return deferred.promise; |
||||
|
} |
||||
|
|
||||
|
$compile(element.contents())(scope); |
||||
|
|
||||
|
$mdUtil.nextTick(function(){ |
||||
|
var inputElement = angular.element('input', element); |
||||
|
inputElement.on('blur', function() { |
||||
|
scope.inputTouched = true; |
||||
|
} ); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
restrict: "E", |
||||
|
require: "^ngModel", |
||||
|
link: linker, |
||||
|
scope: { |
||||
|
disabled:'=ngDisabled', |
||||
|
tbRequired: '=?', |
||||
|
entityType: "=" |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
/*.tb-entity-subtype-list { |
||||
|
#entity_subtype_list_chips { |
||||
|
.md-chips { |
||||
|
padding-bottom: 1px; |
||||
|
} |
||||
|
} |
||||
|
.tb-error-messages { |
||||
|
margin-top: -11px; |
||||
|
height: 35px; |
||||
|
.tb-error-message { |
||||
|
padding-left: 1px; |
||||
|
} |
||||
|
} |
||||
|
}*/ |
||||
@ -0,0 +1,54 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2017 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
|
||||
|
<section flex layout='column' class="tb-entity-subtype-list"> |
||||
|
<md-chips flex |
||||
|
readonly="disabled" |
||||
|
id="entity_subtype_list_chips" |
||||
|
ng-required="tbRequired" |
||||
|
ng-model="entitySubtypesList" |
||||
|
placeholder="{{placeholder}}" |
||||
|
secondary-placeholder="{{secondaryPlaceholder}}" |
||||
|
md-autocomplete-snap |
||||
|
md-require-match="false"> |
||||
|
<md-autocomplete |
||||
|
md-no-cache="true" |
||||
|
id="entitySubtype" |
||||
|
md-selected-item="selectedEntitySubtype" |
||||
|
md-search-text="entitySubtypeSearchText" |
||||
|
md-items="item in fetchEntitySubtypes(entitySubtypeSearchText)" |
||||
|
md-item-text="item" |
||||
|
md-min-length="0" |
||||
|
placeholder="{{ (!entitySubtypesList || !entitySubtypesList.length) ? placeholder : secondaryPlaceholder }}"> |
||||
|
<md-item-template> |
||||
|
<span md-highlight-text="entitySubtypeSearchText" md-highlight-flags="^i">{{item}}</span> |
||||
|
</md-item-template> |
||||
|
<md-not-found> |
||||
|
<span translate translate-values='{ entitySubtype: entitySubtypeSearchText }'>{{noSubtypesMathingText}}</span> |
||||
|
</md-not-found> |
||||
|
</md-autocomplete> |
||||
|
<md-chip-template> |
||||
|
<span> |
||||
|
<strong>{{$chip}}</strong> |
||||
|
</span> |
||||
|
</md-chip-template> |
||||
|
</md-chips> |
||||
|
<div class="tb-error-messages" ng-messages="ngModelCtrl.$error" ng-if="inputTouched && tbRequired" role="alert"> |
||||
|
<div translate ng-message="entitySubtypesList" class="tb-error-message">{{subtypeListEmptyText}}</div> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,111 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
/* eslint-disable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import entityTypeListTemplate from './entity-type-list.tpl.html'; |
||||
|
|
||||
|
/* eslint-enable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import './entity-type-list.scss'; |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function EntityTypeListDirective($compile, $templateCache, $q, $mdUtil, $translate, $filter, types, entityService) { |
||||
|
|
||||
|
var linker = function (scope, element, attrs, ngModelCtrl) { |
||||
|
|
||||
|
var template = $templateCache.get(entityTypeListTemplate); |
||||
|
element.html(template); |
||||
|
|
||||
|
scope.ngModelCtrl = ngModelCtrl; |
||||
|
|
||||
|
scope.placeholder = scope.tbRequired ? $translate.instant('entity.enter-entity-type') |
||||
|
: $translate.instant('entity.any-entity'); |
||||
|
scope.secondaryPlaceholder = '+' + $translate.instant('entity.entity-type'); |
||||
|
|
||||
|
var entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes); |
||||
|
scope.entityTypesList = []; |
||||
|
for (var type in entityTypes) { |
||||
|
var entityTypeInfo = {}; |
||||
|
entityTypeInfo.value = entityTypes[type]; |
||||
|
entityTypeInfo.name = $translate.instant(types.entityTypeTranslations[entityTypeInfo.value].type) + ''; |
||||
|
scope.entityTypesList.push(entityTypeInfo); |
||||
|
} |
||||
|
|
||||
|
scope.$watch('tbRequired', function () { |
||||
|
scope.updateValidity(); |
||||
|
}); |
||||
|
|
||||
|
scope.fetchEntityTypes = function(searchText) { |
||||
|
var deferred = $q.defer(); |
||||
|
var entityTypes = $filter('filter')(scope.entityTypesList, {name: searchText}); |
||||
|
deferred.resolve(entityTypes); |
||||
|
return deferred.promise; |
||||
|
} |
||||
|
|
||||
|
scope.updateValidity = function() { |
||||
|
var value = ngModelCtrl.$viewValue; |
||||
|
var valid = !scope.tbRequired || value && value.length > 0; |
||||
|
ngModelCtrl.$setValidity('entityTypeList', valid); |
||||
|
} |
||||
|
|
||||
|
ngModelCtrl.$render = function () { |
||||
|
scope.entityTypeList = []; |
||||
|
var value = ngModelCtrl.$viewValue; |
||||
|
if (value && value.length) { |
||||
|
value.forEach(function(type) { |
||||
|
var entityTypeInfo = {}; |
||||
|
entityTypeInfo.value = type; |
||||
|
entityTypeInfo.name = $translate.instant(types.entityTypeTranslations[entityTypeInfo.value].type) + ''; |
||||
|
scope.entityTypeList.push(entityTypeInfo); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
scope.$watch('entityTypeList', function () { |
||||
|
var values = []; |
||||
|
if (scope.entityTypeList && scope.entityTypeList.length) { |
||||
|
scope.entityTypeList.forEach(function(entityType) { |
||||
|
values.push(entityType.value); |
||||
|
}); |
||||
|
} |
||||
|
ngModelCtrl.$setViewValue(values); |
||||
|
scope.updateValidity(); |
||||
|
}, true); |
||||
|
|
||||
|
$compile(element.contents())(scope); |
||||
|
|
||||
|
$mdUtil.nextTick(function(){ |
||||
|
var inputElement = angular.element('input', element); |
||||
|
inputElement.on('blur', function() { |
||||
|
scope.inputTouched = true; |
||||
|
} ); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
restrict: "E", |
||||
|
require: "^ngModel", |
||||
|
link: linker, |
||||
|
scope: { |
||||
|
disabled:'=ngDisabled', |
||||
|
tbRequired: '=?', |
||||
|
allowedEntityTypes: '=?' |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
/*.tb-entity-type-list { |
||||
|
#entity_type_list_chips { |
||||
|
.md-chips { |
||||
|
padding-bottom: 1px; |
||||
|
} |
||||
|
} |
||||
|
.tb-error-messages { |
||||
|
margin-top: -11px; |
||||
|
height: 35px; |
||||
|
.tb-error-message { |
||||
|
padding-left: 1px; |
||||
|
} |
||||
|
} |
||||
|
}*/ |
||||
@ -0,0 +1,54 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2017 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
|
||||
|
<section flex layout='column' class="tb-entity-type-list"> |
||||
|
<md-chips flex |
||||
|
readonly="disabled" |
||||
|
id="entity_type_list_chips" |
||||
|
ng-required="tbRequired" |
||||
|
ng-model="entityTypeList" |
||||
|
placeholder="{{placeholder}}" |
||||
|
secondary-placeholder="{{secondaryPlaceholder}}" |
||||
|
md-autocomplete-snap |
||||
|
md-require-match="true"> |
||||
|
<md-autocomplete |
||||
|
md-no-cache="true" |
||||
|
id="entityType" |
||||
|
md-selected-item="selectedEntityType" |
||||
|
md-search-text="entityTypeSearchText" |
||||
|
md-items="item in fetchEntityTypes(entityTypeSearchText)" |
||||
|
md-item-text="item.name" |
||||
|
md-min-length="0" |
||||
|
placeholder="{{ (!entityTypeList || !entityTypeList.length) ? placeholder : secondaryPlaceholder }}"> |
||||
|
<md-item-template> |
||||
|
<span md-highlight-text="entityTypeSearchText" md-highlight-flags="^i">{{item.name}}</span> |
||||
|
</md-item-template> |
||||
|
<md-not-found> |
||||
|
<span translate translate-values='{ entityType: entityTypeSearchText }'>entity.no-entity-types-matching</span> |
||||
|
</md-not-found> |
||||
|
</md-autocomplete> |
||||
|
<md-chip-template> |
||||
|
<span> |
||||
|
<strong>{{$chip.name}}</strong> |
||||
|
</span> |
||||
|
</md-chip-template> |
||||
|
</md-chips> |
||||
|
<div class="tb-error-messages" ng-messages="ngModelCtrl.$error" ng-if="inputTouched && tbRequired" role="alert"> |
||||
|
<div translate ng-message="entityTypeList" class="tb-error-message">entity.entity-type-list-empty</div> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,85 @@ |
|||||
|
/* |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
import './relation-filters.scss'; |
||||
|
|
||||
|
/* eslint-disable import/no-unresolved, import/default */ |
||||
|
|
||||
|
import relationFiltersTemplate from './relation-filters.tpl.html'; |
||||
|
|
||||
|
/* eslint-enable import/no-unresolved, import/default */ |
||||
|
|
||||
|
/*@ngInject*/ |
||||
|
export default function RelationFilters($compile, $templateCache) { |
||||
|
|
||||
|
return { |
||||
|
restrict: "E", |
||||
|
require: "^ngModel", |
||||
|
scope: { |
||||
|
allowedEntityTypes: '=?' |
||||
|
}, |
||||
|
link: linker |
||||
|
}; |
||||
|
|
||||
|
function linker( scope, element, attrs, ngModelCtrl ) { |
||||
|
|
||||
|
var template = $templateCache.get(relationFiltersTemplate); |
||||
|
element.html(template); |
||||
|
|
||||
|
scope.relationFilters = []; |
||||
|
|
||||
|
scope.addFilter = addFilter; |
||||
|
scope.removeFilter = removeFilter; |
||||
|
|
||||
|
ngModelCtrl.$render = function () { |
||||
|
if (ngModelCtrl.$viewValue) { |
||||
|
var value = ngModelCtrl.$viewValue; |
||||
|
value.forEach(function (filter) { |
||||
|
scope.relationFilters.push(filter); |
||||
|
}); |
||||
|
} |
||||
|
scope.$watch('relationFilters', function (newVal, prevVal) { |
||||
|
if (!angular.equals(newVal, prevVal)) { |
||||
|
updateValue(); |
||||
|
} |
||||
|
}, true); |
||||
|
} |
||||
|
|
||||
|
function addFilter() { |
||||
|
var filter = { |
||||
|
relationType: null, |
||||
|
entityTypes: [] |
||||
|
}; |
||||
|
scope.relationFilters.push(filter); |
||||
|
} |
||||
|
|
||||
|
function removeFilter($event, filter) { |
||||
|
var index = scope.relationFilters.indexOf(filter); |
||||
|
if (index > -1) { |
||||
|
scope.relationFilters.splice(index, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function updateValue() { |
||||
|
var value = []; |
||||
|
scope.relationFilters.forEach(function (filter) { |
||||
|
value.push(filter); |
||||
|
}); |
||||
|
ngModelCtrl.$setViewValue(value); |
||||
|
} |
||||
|
$compile(element.contents())(scope); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2017 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
.tb-relation-filters { |
||||
|
.header { |
||||
|
padding-left: 5px; |
||||
|
padding-right: 5px; |
||||
|
padding-bottom: 5px; |
||||
|
.cell { |
||||
|
padding-left: 5px; |
||||
|
padding-right: 5px; |
||||
|
color: rgba(0,0,0,.54); |
||||
|
font-size: 12px; |
||||
|
font-weight: 700; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
} |
||||
|
.body { |
||||
|
padding-left: 5px; |
||||
|
padding-right: 5px; |
||||
|
max-height: 300px; |
||||
|
overflow: auto; |
||||
|
padding-bottom: 20px; |
||||
|
.row { |
||||
|
padding-top: 5px; |
||||
|
} |
||||
|
.cell { |
||||
|
padding-left: 5px; |
||||
|
padding-right: 5px; |
||||
|
|
||||
|
md-select { |
||||
|
margin: 0 0 24px; |
||||
|
} |
||||
|
|
||||
|
md-input-container { |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
md-chips-wrap { |
||||
|
padding: 0px; |
||||
|
margin: 0 0 24px; |
||||
|
.md-chip-input-container { |
||||
|
margin: 0; |
||||
|
} |
||||
|
md-autocomplete { |
||||
|
height: 30px; |
||||
|
md-autocomplete-wrap { |
||||
|
height: 30px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.md-chips .md-chip-input-container input { |
||||
|
padding: 2px 2px 2px; |
||||
|
height: 26px; |
||||
|
line-height: 26px; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.md-button { |
||||
|
margin: 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2017 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<div class="tb-relation-filters"> |
||||
|
<div class="header" ng-show="relationFilters.length"> |
||||
|
<div layout="row" layout-align="start center"> |
||||
|
<span class="cell" style="width: 200px; min-width: 200px;" translate>relation.type</span> |
||||
|
<span class="cell" flex translate>entity.entity-types</span> |
||||
|
<span class="cell" style="width: 40px; min-width: 40px;"> </span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="body" ng-show="relationFilters.length"> |
||||
|
<div class="row" ng-form name="relationFilterForm" flex layout="row" layout-align="start center" ng-repeat="filter in relationFilters track by $index"> |
||||
|
<div class="md-whiteframe-1dp" flex layout="row" layout-align="start center"> |
||||
|
<tb-relation-type-autocomplete class="cell" style="width: 200px; min-width: 200px;" |
||||
|
hide-label |
||||
|
the-form="relationFilterForm" |
||||
|
ng-model="filter.relationType" |
||||
|
tb-required="false"> |
||||
|
</tb-relation-type-autocomplete> |
||||
|
<tb-entity-type-list class="cell" flex |
||||
|
ng-model="filter.entityTypes" |
||||
|
allowed-entity-types="allowedEntityTypes" |
||||
|
tb-required="false"> |
||||
|
</tb-entity-type-list> |
||||
|
<md-button ng-disabled="loading" class="md-icon-button md-primary" style="width: 40px; min-width: 40px;" |
||||
|
ng-click="removeFilter($event, filter)" aria-label="{{ 'action.remove' | translate }}"> |
||||
|
<md-tooltip md-direction="top"> |
||||
|
{{ 'relation.remove-relation-filter' | translate }} |
||||
|
</md-tooltip> |
||||
|
<md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons"> |
||||
|
close |
||||
|
</md-icon> |
||||
|
</md-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="any-filter" ng-show="!relationFilters.length"> |
||||
|
<span layout-align="center center" |
||||
|
class="tb-prompt" translate>relation.any-relation</span> |
||||
|
</div> |
||||
|
<div> |
||||
|
<md-button ng-disabled="loading" class="md-primary md-raised" ng-click="addFilter($event)" aria-label="{{ 'action.add' | translate }}"> |
||||
|
<md-tooltip md-direction="top"> |
||||
|
{{ 'relation.add-relation-filter' | translate }} |
||||
|
</md-tooltip> |
||||
|
<md-icon aria-label="{{ 'action.add' | translate }}" class="material-icons"> |
||||
|
add |
||||
|
</md-icon> |
||||
|
{{ 'action.add' | translate }} |
||||
|
</md-button> |
||||
|
</div> |
||||
|
</div> |
||||
Loading…
Reference in new issue