Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard into develop/3.0

pull/2486/head
Igor Kulikov 7 years ago
parent
commit
25ba81394d
  1. 4
      ui/src/app/api/widget.service.js
  2. 5
      ui/src/app/common/types.constant.js
  3. 32
      ui/src/app/components/json-content.directive.js
  4. 2
      ui/src/app/components/json-content.scss
  5. 8
      ui/src/app/components/json-content.tpl.html
  6. 10
      ui/src/app/components/json-object-edit.directive.js
  7. 13
      ui/src/app/components/json-object-edit.scss
  8. 8
      ui/src/app/components/json-object-edit.tpl.html
  9. 45
      ui/src/app/components/tb-json-to-string.directive.js
  10. 38
      ui/src/app/entity/attribute/add-attribute-dialog.controller.js
  11. 34
      ui/src/app/entity/attribute/add-attribute-dialog.tpl.html
  12. 1
      ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
  13. 20
      ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.tpl.html
  14. 35
      ui/src/app/entity/attribute/attribute-dialog-edit-json.controller.js
  15. 24
      ui/src/app/entity/attribute/attribute-dialog-edit-json.scss
  16. 54
      ui/src/app/entity/attribute/attribute-dialog-edit-json.tpl.html
  17. 143
      ui/src/app/entity/attribute/attribute-table.directive.js
  18. 8
      ui/src/app/entity/attribute/attribute-table.scss
  19. 4
      ui/src/app/entity/attribute/attribute-table.tpl.html
  20. 58
      ui/src/app/entity/attribute/edit-attribute-value.controller.js
  21. 30
      ui/src/app/entity/attribute/edit-attribute-value.tpl.html
  22. 2
      ui/src/app/locale/locale.constant-el_GR.json
  23. 8
      ui/src/app/locale/locale.constant-en_US.json
  24. 4
      ui/src/app/locale/locale.constant-ru_RU.json
  25. 4
      ui/src/app/locale/locale.constant-uk_UA.json

4
ui/src/app/api/widget.service.js

@ -29,6 +29,8 @@ import thingsboardWebCameraInputWidget from '../widget/lib/web-camera-input-widg
import thingsboardRpcWidgets from '../widget/lib/rpc';
import thingsboardJsonToString from '../components/tb-json-to-string.directive';
import TbFlot from '../widget/lib/flot-widget';
import TbAnalogueLinearGauge from '../widget/lib/analogue-linear-gauge';
import TbAnalogueRadialGauge from '../widget/lib/analogue-radial-gauge';
@ -52,7 +54,7 @@ export default angular.module('thingsboard.api.widget', ['oc.lazyLoad', thingsbo
thingsboardTimeseriesTableWidget, thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget,
thingsboardEntitiesHierarchyWidget, thingsboardExtensionsTableWidget, thingsboardDateRangeNavigatorWidget,
thingsboardMultipleInputWidget, thingsboardWebCameraInputWidget, thingsboardRpcWidgets, thingsboardTypes,
thingsboardUtils, TripAnimationWidget])
thingsboardUtils, thingsboardJsonToString, TripAnimationWidget])
.factory('widgetService', WidgetService)
.name;

5
ui/src/app/common/types.constant.js

@ -881,6 +881,11 @@ export default angular.module('thingsboard.types', [])
value: "boolean",
name: "value.boolean",
icon: "mdi:checkbox-marked-outline"
},
json: {
value: "json",
name: "value.json",
icon: "mdi:json"
}
},
widgetType: {

32
ui/src/app/components/json-content.directive.js

@ -57,9 +57,12 @@ function JsonContent($compile, $templateCache, toast, types, utils) {
updateEditorSize();
};
scope.beautifyJson = function () {
var res = js_beautify(scope.contentBody, {indent_size: 4, wrap_line_length: 60});
scope.contentBody = res;
scope.beautifyJSON = function () {
scope.contentBody = js_beautify(scope.contentBody, {indent_size: 4, wrap_line_length: 60});
};
scope.minifyJSON = function () {
scope.contentBody = angular.toJson(angular.fromJson(scope.contentBody));
};
function updateEditorSize() {
@ -116,7 +119,7 @@ function JsonContent($compile, $templateCache, toast, types, utils) {
scope.$watch('contentBody', function (newContent, oldContent) {
ngModelCtrl.$setViewValue(scope.contentBody);
if (!angular.equals(newContent, oldContent)) {
scope.contentValid = true;
scope.contentValid = scope.validate();
}
scope.updateValidity();
});
@ -139,15 +142,17 @@ function JsonContent($compile, $templateCache, toast, types, utils) {
}
return true;
} catch (e) {
var details = utils.parseException(e);
var errorInfo = 'Error:';
if (details.name) {
errorInfo += ' ' + details.name + ':';
}
if (details.message) {
errorInfo += ' ' + details.message;
if (!scope.hideErrorToast) {
var details = utils.parseException(e);
var errorInfo = 'Error:';
if (details.name) {
errorInfo += ' ' + details.name + ':';
}
if (details.message) {
errorInfo += ' ' + details.message;
}
scope.showError(errorInfo);
}
scope.showError(errorInfo);
return false;
}
};
@ -169,7 +174,7 @@ function JsonContent($compile, $templateCache, toast, types, utils) {
});
$compile(element.contents())(scope);
}
};
return {
restrict: "E",
@ -177,6 +182,7 @@ function JsonContent($compile, $templateCache, toast, types, utils) {
scope: {
contentType: '=',
validateContent: '=?',
hideErrorToast: '=?',
readonly:'=ngReadonly',
fillHeight:'=?'
},

2
ui/src/app/components/json-content.scss

@ -27,7 +27,7 @@ tb-json-content {
min-height: 15px;
padding: 4px;
margin: 0 5px 0 0;
font-size: .8rem;
font-size: 12px;
line-height: 15px;
color: #7b7b7b;
background: rgba(220, 220, 220, .35);

8
ui/src/app/components/json-content.tpl.html

@ -17,11 +17,15 @@
-->
<div style="background: #fff;" ng-class="{'fill-height': fillHeight}" tb-expand-fullscreen fullscreen-zindex="100" expand-button-id="expand-button" on-fullscreen-changed="onFullscreenChanged()" layout="column">
<div layout="row" layout-align="start center" style="height: 40px;" class="tb-json-content-toolbar">
<label class="tb-title no-padding">{{ label }}</label>
<label class="tb-title no-padding"
ng-class="{'tb-error': !contentValid}">{{ label }}</label>
<span flex></span>
<md-button ng-if="!readonly" class="tidy" aria-label="{{ 'js-func.tidy' | translate }}" ng-click="beautifyJson()">{{
<md-button ng-if="!readonly" class="tidy" aria-label="{{ 'js-func.tidy' | translate }}" ng-click="beautifyJSON()">{{
'js-func.tidy' | translate }}
</md-button>
<md-button ng-if="!readonly" class="tidy" aria-label="{{ 'js-func.mini' | translate }}" ng-click="minifyJSON()">{{
'js-func.mini' | translate }}
</md-button>
<md-button id="expand-button" aria-label="Fullscreen" class="md-icon-button tb-md-32 tb-fullscreen-button-style"></md-button>
</div>
<div flex id="tb-json-panel" class="tb-json-content-panel" layout="column">

10
ui/src/app/components/json-object-edit.directive.js

@ -50,6 +50,14 @@ function JsonObjectEdit($compile, $templateCache, $document, toast, utils) {
updateEditorSize();
};
scope.beautifyJSON = function () {
scope.contentBody = angular.toJson(scope.object, 4);
};
scope.minifyJSON = function () {
scope.contentBody = angular.toJson(scope.object);
};
function updateEditorSize() {
if (scope.json_editor) {
scope.json_editor.resize();
@ -169,7 +177,7 @@ function JsonObjectEdit($compile, $templateCache, $document, toast, utils) {
});
$compile(element.contents())(scope);
}
};
return {
restrict: "E",

13
ui/src/app/components/json-object-edit.scss

@ -21,6 +21,19 @@ tb-json-object-edit {
}
}
.tb-json-object-edit-toolbar {
.md-button.tidy {
min-width: 32px;
min-height: 15px;
padding: 4px;
margin: 0 5px 0 0;
font-size: 12px;
line-height: 15px;
color: #7b7b7b;
background: rgba(220, 220, 220, .35);
}
}
.tb-json-object-panel {
height: 100%;
margin-left: 15px;

8
ui/src/app/components/json-object-edit.tpl.html

@ -16,12 +16,18 @@
-->
<div style="background: #fff;" ng-class="{'fill-height': fillHeight}" tb-expand-fullscreen fullscreen-zindex="100" expand-button-id="expand-button" on-fullscreen-changed="onFullscreenChanged()" layout="column">
<div layout="row" layout-align="start center">
<div layout="row" layout-align="start center" class="tb-json-object-edit-toolbar">
<label class="tb-title no-padding"
ng-class="{'tb-required': required,
'tb-readonly': readonly,
'tb-error': !objectValid}">{{ label }}</label>
<span flex></span>
<md-button ng-if="!readonly" class="tidy" aria-label="{{ 'js-func.tidy' | translate }}" ng-click="beautifyJSON()">
{{'js-func.tidy' | translate }}
</md-button>
<md-button ng-if="!readonly" class="tidy" aria-label="{{ 'js-func.mini' | translate }}" ng-click="minifyJSON()">
{{'js-func.mini' | translate }}
</md-button>
<md-button id="expand-button" aria-label="Fullscreen" class="md-icon-button tb-md-32 tb-fullscreen-button-style"></md-button>
</div>
<div flex id="tb-json-panel" class="tb-json-object-panel" layout="column">

45
ui/src/app/components/tb-json-to-string.directive.js

@ -0,0 +1,45 @@
/*
* Copyright © 2016-2020 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.
*/
export default angular.module('tbJsonToString', [])
.directive('tbJsonToString', InputJson)
.name;
function InputJson() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attr, ngModelCtrl) {
function into(input) {
try {
ngModelCtrl.$setValidity('invalidJSON', true);
return angular.fromJson(input);
} catch (e) {
ngModelCtrl.$setValidity('invalidJSON', false);
}
}
function out(data) {
try {
ngModelCtrl.$setValidity('invalidJSON', true);
return angular.toJson(data);
} catch (e) {
ngModelCtrl.$setValidity('invalidJSON', false);
}
}
ngModelCtrl.$parsers.push(into);
ngModelCtrl.$formatters.push(out);
}
};
}

38
ui/src/app/entity/attribute/add-attribute-dialog.controller.js

@ -13,10 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable import/no-unresolved, import/default */
import attributeDialogEditJsonTemplate from './attribute-dialog-edit-json.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
import AttributeDialogEditJsonController from './attribute-dialog-edit-json.controller';
/*@ngInject*/
export default function AddAttributeDialogController($scope, $mdDialog, types, attributeService, entityType, entityId, attributeScope) {
var vm = this;
let vm = this;
vm.attribute = {};
@ -40,11 +48,37 @@ export default function AddAttributeDialogController($scope, $mdDialog, types, a
);
}
$scope.$watch('vm.valueType', function() {
$scope.$watch('vm.valueType', function () {
if (vm.valueType === types.valueType.boolean) {
vm.attribute.value = false;
} else if (vm.valueType === types.valueType.json) {
vm.attribute.value = {};
} else {
vm.attribute.value = null;
}
});
vm.addJSON = ($event) => {
showJsonDialog($event, vm.attribute.value, false).then((response) => {
vm.attribute.value = response;
})
};
function showJsonDialog($event, jsonValue, readOnly) {
if ($event) {
$event.stopPropagation();
}
return $mdDialog.show({
controller: AttributeDialogEditJsonController,
controllerAs: 'vm',
templateUrl: attributeDialogEditJsonTemplate,
locals: {
jsonValue: jsonValue,
readOnly: readOnly
},
targetEvent: $event,
fullscreen: true,
multiple: true,
});
}
}

34
ui/src/app/entity/attribute/add-attribute-dialog.tpl.html

@ -26,7 +26,8 @@
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading"
ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
@ -58,7 +59,8 @@
</md-input-container>
<md-input-container ng-if="vm.valueType===vm.valueTypes.integer" flex="60" class="md-block">
<label translate>value.integer-value</label>
<input required name="value" type="number" step="1" ng-pattern="/^-?[0-9]+$/" ng-model="vm.attribute.value">
<input required name="value" type="number" step="1" ng-pattern="/^-?[0-9]+$/"
ng-model="vm.attribute.value">
<div ng-messages="theForm.value.$error">
<div translate ng-message="required">attribute.value-required</div>
<div translate ng-message="pattern">value.invalid-integer-value</div>
@ -71,11 +73,31 @@
<div translate ng-message="required">attribute.value-required</div>
</div>
</md-input-container>
<div layout="column" layout-align="center" flex="60" ng-if="vm.valueType===vm.valueTypes.boolean">
<md-checkbox ng-model="vm.attribute.value" style="margin-bottom: 0px;">
<div layout="column" layout-align="center" flex="60"
ng-if="vm.valueType===vm.valueTypes.boolean">
<md-checkbox ng-model="vm.attribute.value" style="margin-bottom: 0;">
{{ (vm.attribute.value ? 'value.true' : 'value.false') | translate }}
</md-checkbox>
</div>
<div layout="row" layout-align="center" flex="60"
ng-if="vm.valueType===vm.valueTypes.json" class="md-block">
<md-input-container flex class="md-block">
<label translate>value.json-value</label>
<input required tb-json-to-string name="value" ng-model="vm.attribute.value">
<div ng-messages="theForm.value.$error">
<div translate ng-message="required">attribute.value-required</div>
</div>
</md-input-container>
<md-button class="md-icon-button"
ng-click="vm.addJSON($event)"
style="margin: 18px 0;"
aria-label="{{ 'action.edit' | translate }}">
<md-tooltip md-direction="top">
{{ 'action.edit' | translate }}
</md-tooltip>
<ng-md-icon size="20" icon="open_in_new"></ng-md-icon>
</md-button>
</div>
</section>
</fieldset>
</md-content>
@ -87,8 +109,8 @@
class="md-raised md-primary">
{{ 'action.add' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
{{ 'action.cancel' | translate }}
</md-button>
</md-dialog-actions>
</form>

1
ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js

@ -151,5 +151,4 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog,
}
);
}
}

20
ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.tpl.html

@ -26,7 +26,8 @@
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading"
ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
@ -37,10 +38,10 @@
<section flex layout="column" style="width: 300px;">
<span translate style="padding-bottom: 10px;">dashboard.select-existing</span>
<tb-dashboard-autocomplete the-form="theForm"
ng-disabled="$root.loading || vm.addToDashboardType != 0"
tb-required="vm.addToDashboardType === 0"
ng-model="vm.dashboardId"
select-first-dashboard="false">
ng-disabled="$root.loading || vm.addToDashboardType != 0"
tb-required="vm.addToDashboardType === 0"
ng-model="vm.dashboardId"
select-first-dashboard="false">
</tb-dashboard-autocomplete>
</section>
</md-radio-button>
@ -49,7 +50,8 @@
<span translate>dashboard.create-new</span>
<md-input-container class="md-block">
<label translate>dashboard.new-dashboard-title</label>
<input ng-required="vm.addToDashboardType === 1" name="title" ng-model="vm.newDashboard.title">
<input ng-required="vm.addToDashboardType === 1" name="title"
ng-model="vm.newDashboard.title">
<div ng-messages="theForm.title.$error">
<div translate ng-message="required">dashboard.title-required</div>
</div>
@ -66,15 +68,15 @@
<md-checkbox
ng-model="vm.openDashboard"
aria-label="{{ 'dashboard.open-dashboard' | translate }}"
style="margin-bottom: 0px; padding-right: 20px;">
style="margin-bottom: 0; padding-right: 20px;">
{{ 'dashboard.open-dashboard' | translate }}
</md-checkbox>
<md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
class="md-raised md-primary">
{{ 'action.add' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
{{ 'action.cancel' | translate }}
</md-button>
</md-dialog-actions>
</form>

35
ui/src/app/entity/attribute/attribute-dialog-edit-json.controller.js

@ -0,0 +1,35 @@
/*
* Copyright © 2016-2020 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-enable import/no-unresolved, import/default */
import './attribute-dialog-edit-json.scss';
/*@ngInject*/
export default function AttributeDialogEditJsonController($mdDialog, types, jsonValue, readOnly) {
let vm = this;
vm.json = angular.toJson(jsonValue, 4);
vm.readOnly = readOnly;
vm.contentType = types.contentType.JSON.value;
vm.save = () => {
$mdDialog.hide(angular.fromJson(vm.json));
};
vm.cancel = () => {
$mdDialog.cancel();
};
}

24
ui/src/app/entity/attribute/attribute-dialog-edit-json.scss

@ -0,0 +1,24 @@
/**
* Copyright © 2016-2020 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.
*/
.attribute-edit-json-dialog{
min-width: 400px;
}
@media (max-width: 425px){
.attribute-edit-json-dialog {
min-width: 200px;
}
}

54
ui/src/app/entity/attribute/attribute-dialog-edit-json.tpl.html

@ -0,0 +1,54 @@
<!--
Copyright © 2016-2020 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="attribute-edit-json-dialog">
<form name="theForm" ng-submit="vm.save()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>{{ 'details.edit-json' | translate }}</h2>
<span flex></span>
<md-button class="md-icon-button"
ng-click="vm.cancel()"
aria-label="{{'action.close'|translate}}">
<ng-md-icon icon="close" aria-label="Close"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content>
<div class="md-dialog-content">
<tb-json-content
ng-model="vm.json"
label="{{ 'value.json-value' | translate }}"
content-type="vm.contentType"
validate-content="true"
hide-error-toast="true"
fill-height="false"
ng-readonly="vm.readOnly">
</tb-json-content>
</div>
</md-dialog-content>
<md-dialog-actions layout="row" layout-align="end center" class="action-buttons">
<md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
class="md-raised md-primary">
{{'action.save'|translate}}
</md-button>
<md-button id="cancel-btn" ng-disabled="$root.loading" ng-click="vm.cancel()">
{{'action.cancel'|translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

143
ui/src/app/entity/attribute/attribute-table.directive.js

@ -39,7 +39,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
element.html(template);
var getAttributeScopeByValue = function(attributeScopeValue) {
var getAttributeScopeByValue = function (attributeScopeValue) {
if (scope.types.latestTelemetry.value === attributeScopeValue) {
return scope.types.latestTelemetry;
}
@ -48,7 +48,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
return scope.attributeScopes[attrScope];
}
}
}
};
scope.types = types;
@ -87,14 +87,14 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
search: null
};
scope.$watch("entityId", function(newVal) {
scope.$watch("entityId", function (newVal) {
if (newVal) {
scope.resetFilter();
scope.getEntityAttributes(false, true);
}
});
scope.$watch("attributeScope", function(newVal, prevVal) {
scope.$watch("attributeScope", function (newVal, prevVal) {
if (newVal && !angular.equals(newVal, prevVal)) {
scope.mode = 'default';
scope.query.search = null;
@ -103,30 +103,30 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
}
});
scope.resetFilter = function() {
scope.resetFilter = function () {
scope.mode = 'default';
scope.query.search = null;
scope.selectedAttributes = [];
scope.attributeScope = getAttributeScopeByValue(attrs.defaultAttributeScope);
}
};
scope.enterFilterMode = function(event) {
scope.enterFilterMode = function (event) {
let $button = angular.element(event.currentTarget);
let $toolbarsContainer = $button.closest('.toolbarsContainer');
scope.query.search = '';
$timeout(()=>{
$timeout(() => {
$toolbarsContainer.find('.searchInput').focus();
})
}
};
scope.exitFilterMode = function() {
scope.exitFilterMode = function () {
scope.query.search = null;
scope.getEntityAttributes();
}
};
scope.$watch("query.search", function(newVal, prevVal) {
scope.$watch("query.search", function (newVal, prevVal) {
if (!angular.equals(newVal, prevVal) && scope.query.search != null) {
scope.getEntityAttributes();
}
@ -142,15 +142,15 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
}
}
scope.onReorder = function() {
scope.onReorder = function () {
scope.getEntityAttributes(false, false);
}
};
scope.onPaginate = function() {
scope.onPaginate = function () {
scope.getEntityAttributes(false, false);
}
};
scope.getEntityAttributes = function(forceUpdate, reset) {
scope.getEntityAttributes = function (forceUpdate, reset) {
if (scope.attributesDeferred) {
scope.attributesDeferred.resolve();
}
@ -163,7 +163,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
}
scope.checkSubscription();
scope.attributesDeferred = attributeService.getEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value,
scope.query, function(attributes, update, apply) {
scope.query, function (attributes, update, apply) {
success(attributes, update || forceUpdate, apply);
}
);
@ -176,9 +176,9 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
});
deferred.resolve();
}
}
};
scope.checkSubscription = function() {
scope.checkSubscription = function () {
var newSubscriptionId = null;
if (scope.entityId && scope.entityType && scope.attributeScope.clientSide && scope.mode != 'widget') {
newSubscriptionId = attributeService.subscribeForEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value);
@ -187,36 +187,38 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
}
scope.subscriptionId = newSubscriptionId;
}
};
scope.$on('$destroy', function() {
scope.$on('$destroy', function () {
if (scope.subscriptionId) {
attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
}
});
scope.editAttribute = function($event, attribute) {
scope.editAttribute = function ($event, attribute) {
if (!scope.attributeScope.clientSide) {
$event.stopPropagation();
$mdEditDialog.show({
controller: EditAttributeValueController,
templateUrl: editAttributeValueTemplate,
locals: {attributeValue: attribute.value,
save: function (model) {
var updatedAttribute = angular.copy(attribute);
updatedAttribute.value = model.value;
attributeService.saveEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value, [updatedAttribute]).then(
function success() {
scope.getEntityAttributes();
}
);
}},
locals: {
attributeValue: attribute.value,
save: function (model) {
var updatedAttribute = angular.copy(attribute);
updatedAttribute.value = model.value;
attributeService.saveEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value, [updatedAttribute]).then(
function success() {
scope.getEntityAttributes();
}
);
}
},
targetEvent: $event
});
}
}
};
scope.addAttribute = function($event) {
scope.addAttribute = function ($event) {
if (!scope.attributeScope.clientSide) {
$event.stopPropagation();
$mdDialog.show({
@ -224,16 +226,20 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
controllerAs: 'vm',
templateUrl: addAttributeDialogTemplate,
parent: angular.element($document[0].body),
locals: {entityType: scope.entityType, entityId: scope.entityId, attributeScope: scope.attributeScope.value},
locals: {
entityType: scope.entityType,
entityId: scope.entityId,
attributeScope: scope.attributeScope.value
},
fullscreen: true,
targetEvent: $event
}).then(function () {
scope.getEntityAttributes();
});
}
}
};
scope.deleteAttributes = function($event) {
scope.deleteAttributes = function ($event) {
if (!scope.attributeScope.clientSide) {
$event.stopPropagation();
var confirm = $mdDialog.confirm()
@ -244,33 +250,33 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
attributeService.deleteEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value, scope.selectedAttributes).then(
function success() {
scope.selectedAttributes = [];
scope.getEntityAttributes();
}
)
attributeService.deleteEntityAttributes(scope.entityType, scope.entityId, scope.attributeScope.value, scope.selectedAttributes).then(
function success() {
scope.selectedAttributes = [];
scope.getEntityAttributes();
}
)
});
}
}
};
scope.nextWidget = function() {
scope.nextWidget = function () {
$mdUtil.nextTick(function () {
if (scope.widgetsCarousel.index < scope.widgetsList.length - 1) {
scope.widgetsCarousel.index++;
}
});
}
};
scope.prevWidget = function() {
scope.prevWidget = function () {
$mdUtil.nextTick(function () {
if (scope.widgetsCarousel.index > 0) {
scope.widgetsCarousel.index--;
}
});
}
};
scope.enterWidgetMode = function() {
scope.enterWidgetMode = function () {
if (scope.widgetsIndexWatch) {
scope.widgetsIndexWatch();
@ -303,7 +309,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
entitiAliases[entityAlias.id] = entityAlias;
var stateController = {
getStateParams: function() {
getStateParams: function () {
return {};
}
};
@ -317,9 +323,9 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
type: types.datasourceType.entity,
entityAliasId: entityAlias.id,
dataKeys: []
}
};
var i = 0;
for (var attr =0; attr < scope.selectedAttributes.length;attr++) {
for (var attr = 0; attr < scope.selectedAttributes.length; attr++) {
var attribute = scope.selectedAttributes[attr];
var dataKey = {
name: attribute.key,
@ -328,12 +334,12 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
color: utils.getMaterialColor(i),
settings: {},
_hash: Math.random()
}
};
datasource.dataKeys.push(dataKey);
i++;
}
scope.widgetsIndexWatch = scope.$watch('widgetsCarousel.index', function(newVal, prevVal) {
scope.widgetsIndexWatch = scope.$watch('widgetsCarousel.index', function (newVal, prevVal) {
if (scope.mode === 'widget' && (newVal != prevVal)) {
var index = scope.widgetsCarousel.index;
for (var i = 0; i < scope.widgetsList.length; i++) {
@ -345,7 +351,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
}
});
scope.widgetsBundleWatch = scope.$watch('widgetsBundle', function(newVal, prevVal) {
scope.widgetsBundleWatch = scope.$watch('widgetsBundle', function (newVal, prevVal) {
if (scope.mode === 'widget' && (scope.firstBundle === true || newVal != prevVal)) {
scope.widgetsList = [];
scope.widgetsListCache = [];
@ -358,7 +364,7 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
widgetService.getBundleWidgetTypes(scope.widgetsBundle.alias, isSystem).then(
function success(widgetTypes) {
widgetTypes = $filter('orderBy')(widgetTypes, ['-descriptor.type','-createdTime']);
widgetTypes = $filter('orderBy')(widgetTypes, ['-descriptor.type', '-createdTime']);
for (var i = 0; i < widgetTypes.length; i++) {
var widgetType = widgetTypes[i];
@ -398,9 +404,9 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
}
}
});
}
};
scope.exitWidgetMode = function() {
scope.exitWidgetMode = function () {
if (scope.widgetsBundleWatch) {
scope.widgetsBundleWatch();
scope.widgetsBundleWatch = null;
@ -412,9 +418,9 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
scope.selectedWidgetsBundleAlias = null;
scope.mode = 'default';
scope.getEntityAttributes(true);
}
};
scope.addWidgetToDashboard = function($event) {
scope.addWidgetToDashboard = function ($event) {
if (scope.mode === 'widget' && scope.widgetsListCache.length > 0) {
var widget = scope.widgetsListCache[scope.widgetsCarousel.index][0];
$event.stopPropagation();
@ -423,21 +429,26 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
controllerAs: 'vm',
templateUrl: addWidgetToDashboardDialogTemplate,
parent: angular.element($document[0].body),
locals: {entityId: scope.entityId, entityType: scope.entityType, entityName: scope.entityName, widget: angular.copy(widget)},
locals: {
entityId: scope.entityId,
entityType: scope.entityType,
entityName: scope.entityName,
widget: angular.copy(widget)
},
fullscreen: true,
targetEvent: $event
}).then(function () {
});
}
}
};
scope.loading = function() {
scope.loading = function () {
return $rootScope.loading;
}
};
$compile(element.contents())(scope);
}
};
return {
restrict: "E",

8
ui/src/app/entity/attribute/attribute-table.scss

@ -58,3 +58,11 @@ md-toolbar.md-table-toolbar.alternate {
}
}
}
md-edit-dialog.tb-edit-dialog{
z-index: 78;
}
md-backdrop.md-edit-dialog-backdrop{
z-index: 77;
}

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

@ -77,9 +77,7 @@
</md-toolbar>
<md-toolbar class="md-table-toolbar alternate" ng-show="mode==='default' && selectedAttributes.length">
<div class="md-toolbar-tools">
<span translate="{{attributeScope === types.latestTelemetry
? 'attribute.selected-telemetry'
: 'attribute.selected-attributes'}}"
<span translate="{{(attributeScope === types.latestTelemetry) ? 'attribute.selected-telemetry' : 'attribute.selected-attributes'}}"
translate-values="{count: selectedAttributes.length}"
translate-interpolation="messageformat"></span>
<span flex></span>

58
ui/src/app/entity/attribute/edit-attribute-value.controller.js

@ -13,14 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-enable import/no-unresolved, import/default */
import AttributeDialogEditJsonController from "./attribute-dialog-edit-json.controller";
import attributeDialogEditJsonTemplate from "./attribute-dialog-edit-json.tpl.html";
/*@ngInject*/
export default function EditAttributeValueController($scope, $q, $element, types, attributeValue, save) {
export default function EditAttributeValueController($scope, $mdDialog, $q, $element, $document, types, attributeValue, save) {
$scope.valueTypes = types.valueType;
$scope.model = {};
$scope.model.value = attributeValue;
$scope.model = {
value: attributeValue
};
if ($scope.model.value === true || $scope.model.value === false) {
$scope.valueType = types.valueType.boolean;
@ -30,26 +35,27 @@ export default function EditAttributeValueController($scope, $q, $element, types
} else {
$scope.valueType = types.valueType.double;
}
} else if (angular.isObject($scope.model.value)) {
$scope.valueType = types.valueType.json;
} else {
$scope.valueType = types.valueType.string;
}
$scope.submit = submit;
$scope.dismiss = dismiss;
$scope.editJSON = editJSON;
function dismiss() {
$element.remove();
}
function update() {
if($scope.editDialog.$invalid) {
if ($scope.editDialog.$invalid) {
return $q.reject();
}
if(angular.isFunction(save)) {
if (angular.isFunction(save)) {
return $q.when(save($scope.model));
}
return $q.resolve();
}
@ -59,13 +65,45 @@ export default function EditAttributeValueController($scope, $q, $element, types
});
}
$scope.$watch('valueType', function(newVal, prevVal) {
if (newVal != prevVal) {
$scope.$watch('valueType', function (newVal, prevVal) {
if (newVal !== prevVal) {
if ($scope.valueType === types.valueType.boolean) {
$scope.model.value = false;
} else if ($scope.valueType === types.valueType.json) {
$scope.model.value = {};
} else {
$scope.model.value = null;
}
}
});
function editJSON($event) {
$scope.hideDialog = true;
showJsonDialog($event, $scope.model.value, false).then((response) => {
$scope.hideDialog = false;
if (!angular.equals(response, $scope.model.value)) {
$scope.editDialog.$setDirty();
}
$scope.model.value = response;
})
}
function showJsonDialog($event, jsonValue, readOnly) {
if ($event) {
$event.stopPropagation();
}
return $mdDialog.show({
controller: AttributeDialogEditJsonController,
controllerAs: 'vm',
templateUrl: attributeDialogEditJsonTemplate,
locals: {
jsonValue: jsonValue,
readOnly: readOnly
},
targetEvent: $event,
fullscreen: true,
multiple: true
});
}
}

30
ui/src/app/entity/attribute/edit-attribute-value.tpl.html

@ -15,8 +15,8 @@
limitations under the License.
-->
<md-edit-dialog>
<form name="editDialog" ng-submit="submit()">
<md-edit-dialog class="tb-edit-dialog">
<form name="editDialog" ng-submit="submit()" >
<div layout="column" class="md-content" style="width: 400px;">
<fieldset>
<section layout="row">
@ -38,7 +38,8 @@
</md-input-container>
<md-input-container ng-if="valueType===valueTypes.integer" flex="60" class="md-block">
<label translate>value.integer-value</label>
<input required name="value" type="number" step="1" ng-pattern="/^-?[0-9]+$/" ng-model="model.value">
<input required name="value" type="number" step="1" ng-pattern="/^-?[0-9]+$/"
ng-model="model.value">
<div ng-messages="editDialog.value.$error">
<div translate ng-message="required">attribute.value-required</div>
<div translate ng-message="pattern">value.invalid-integer-value</div>
@ -52,16 +53,31 @@
</div>
</md-input-container>
<div layout="column" layout-align="center" flex="60" ng-if="valueType===valueTypes.boolean">
<md-checkbox ng-model="model.value" style="margin-bottom: 0px;">
<md-checkbox ng-model="model.value" style="margin-bottom: 0;">
{{ (model.value ? 'value.true' : 'value.false') | translate }}
</md-checkbox>
</div>
<div layout="row" layout-align="center" ng-if="valueType===valueTypes.json" flex="60">
<md-input-container class="md-block">
<label translate>value.json-value</label>
<input required tb-json-to-string name="value" ng-model="model.value">
<div ng-messages="editDialog.value.$error">
<div translate ng-message="required">attribute.value-required</div>
</div>
</md-input-container>
<md-button class="md-icon-button" style="margin: 18px 0;"
ng-click="editJSON($event)" aria-label="{{ 'action.edit' | translate }}">
<md-tooltip md-direction="top">
{{ 'action.edit' | translate }}
</md-tooltip>
<ng-md-icon size="20" icon="open_in_new"></ng-md-icon>
</md-button>
</div>
</section>
</fieldset>
</div>
<div layout="row" layout-align="end" class="md-actions">
<md-button ng-click="dismiss()">{{ 'action.cancel' |
translate }}
<md-button ng-click="dismiss()">{{ 'action.cancel' | translate }}
</md-button>
<md-button ng-disabled="editDialog.$invalid || !editDialog.$dirty" type="submit"
class="md-raised md-primary">
@ -69,4 +85,4 @@
</md-button>
</div>
</form>
</md-edit-dialog>
</md-edit-dialog>

2
ui/src/app/locale/locale.constant-el_GR.json

@ -2232,7 +2232,7 @@
"last": "Τελευταίος",
"time-period": "Χρονική Περίοδος"
},
"user": {
"user": {
"user": "Χρήστης",
"users": "Χρήστες",
"management": "Διαχείριση Χρηστών",

8
ui/src/app/locale/locale.constant-en_US.json

@ -607,6 +607,7 @@
},
"details": {
"edit-mode": "Edit mode",
"edit-json": "Edit JSON",
"toggle-edit-mode": "Toggle edit mode"
},
"device": {
@ -1270,7 +1271,8 @@
"js-func": {
"no-return-error": "Function must return value!",
"return-type-mismatch": "Function must return value of '{{type}}' type!",
"tidy": "Tidy"
"tidy": "Tidy",
"mini": "Mini"
},
"key-val": {
"key": "Key",
@ -1593,7 +1595,9 @@
"boolean-value": "Boolean value",
"false": "False",
"true": "True",
"long": "Long"
"long": "Long",
"json": "JSON",
"json-value": "JSON value"
},
"widget": {
"widget-library": "Widgets Library",

4
ui/src/app/locale/locale.constant-ru_RU.json

@ -607,6 +607,7 @@
},
"details": {
"edit-mode": "Режим редактирования",
"edit-json": "Редактировать JSON",
"toggle-edit-mode": "Режим редактирования"
},
"device": {
@ -1191,8 +1192,7 @@
},
"js-func": {
"no-return-error": "Функция должна возвращать значение!",
"return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!",
"tidy": "Tidy"
"return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!"
},
"key-val": {
"key": "Ключ",

4
ui/src/app/locale/locale.constant-uk_UA.json

@ -724,6 +724,7 @@
"details": {
"details": "Деталі",
"edit-mode": "Режим редагування",
"edit-json": "Редагувати JSON",
"toggle-edit-mode": "Перемкнути режим редагування"
},
"device": {
@ -1606,8 +1607,7 @@
},
"js-func": {
"no-return-error": "Функція повинна повертати значення!",
"return-type-mismatch": "Функція повинна повернути значення типу '{{type}}'!",
"tidy": "Tidy"
"return-type-mismatch": "Функція повинна повернути значення типу '{{type}}'!"
},
"key-val": {
"key": "Ключ",

Loading…
Cancel
Save