Commit 67b5c5e2f0620dfe02f9c1dbed32bc4626b6b6b8
Committed by
GitHub
Merge pull request #215 from thingsboard/feature/TB-72
TB-72: Add ability to edit JSON additional info of relations.
Showing
6 changed files
with
187 additions
and
33 deletions
ui/src/app/entity/relation/relation-dialog.controller.js
renamed from
ui/src/app/entity/relation/add-relation-dialog.controller.js
@@ -13,42 +13,113 @@ | @@ -13,42 +13,113 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
17 | +import 'brace/ext/language_tools'; | ||
18 | +import 'brace/mode/json'; | ||
19 | +import 'brace/theme/github'; | ||
20 | +import beautify from 'js-beautify'; | ||
21 | + | ||
22 | +import './relation-dialog.scss'; | ||
23 | + | ||
24 | +const js_beautify = beautify.js; | ||
25 | + | ||
16 | /*@ngInject*/ | 26 | /*@ngInject*/ |
17 | -export default function AddRelationDialogController($scope, $mdDialog, types, entityRelationService, direction, entityId) { | 27 | +export default function RelationDialogController($scope, $mdDialog, types, entityRelationService, isAdd, direction, relation, showingCallback) { |
18 | 28 | ||
19 | var vm = this; | 29 | var vm = this; |
20 | 30 | ||
21 | vm.types = types; | 31 | vm.types = types; |
32 | + vm.isAdd = isAdd; | ||
22 | vm.direction = direction; | 33 | vm.direction = direction; |
23 | - vm.targetEntityId = {}; | ||
24 | 34 | ||
25 | - vm.relation = {}; | ||
26 | - if (vm.direction == vm.types.entitySearchDirection.from) { | ||
27 | - vm.relation.from = entityId; | 35 | + showingCallback.onShowing = function(scope, element) { |
36 | + updateEditorSize(element); | ||
37 | + } | ||
38 | + | ||
39 | + vm.relationAdditionalInfoOptions = { | ||
40 | + useWrapMode: false, | ||
41 | + mode: 'json', | ||
42 | + showGutter: false, | ||
43 | + showPrintMargin: false, | ||
44 | + theme: 'github', | ||
45 | + advanced: { | ||
46 | + enableSnippets: false, | ||
47 | + enableBasicAutocompletion: false, | ||
48 | + enableLiveAutocompletion: false | ||
49 | + }, | ||
50 | + onLoad: function (_ace) { | ||
51 | + vm.editor = _ace; | ||
52 | + } | ||
53 | + }; | ||
54 | + | ||
55 | + vm.relation = relation; | ||
56 | + if (vm.isAdd) { | ||
57 | + vm.relation.type = types.entityRelationType.contains; | ||
58 | + vm.targetEntityId = {}; | ||
28 | } else { | 59 | } else { |
29 | - vm.relation.to = entityId; | 60 | + if (vm.direction == vm.types.entitySearchDirection.from) { |
61 | + vm.targetEntityId = vm.relation.to; | ||
62 | + } else { | ||
63 | + vm.targetEntityId = vm.relation.from; | ||
64 | + } | ||
30 | } | 65 | } |
31 | - vm.relation.type = types.entityRelationType.contains; | ||
32 | 66 | ||
33 | - vm.add = add; | 67 | + vm.save = save; |
34 | vm.cancel = cancel; | 68 | vm.cancel = cancel; |
35 | 69 | ||
70 | + vm.additionalInfo = ''; | ||
71 | + | ||
72 | + if (vm.relation.additionalInfo) { | ||
73 | + vm.additionalInfo = angular.toJson(vm.relation.additionalInfo); | ||
74 | + vm.additionalInfo = js_beautify(vm.additionalInfo, {indent_size: 4}); | ||
75 | + } | ||
76 | + | ||
77 | + $scope.$watch('vm.additionalInfo', () => { | ||
78 | + $scope.theForm.$setValidity("additionalInfo", true); | ||
79 | + }); | ||
80 | + | ||
81 | + function updateEditorSize(element) { | ||
82 | + var newWidth = 600; | ||
83 | + var newHeight = 200; | ||
84 | + angular.element('#tb-relation-additional-info', element).height(newHeight.toString() + "px") | ||
85 | + .width(newWidth.toString() + "px"); | ||
86 | + vm.editor.resize(); | ||
87 | + } | ||
88 | + | ||
36 | function cancel() { | 89 | function cancel() { |
37 | $mdDialog.cancel(); | 90 | $mdDialog.cancel(); |
38 | } | 91 | } |
39 | 92 | ||
40 | - function add() { | ||
41 | - if (vm.direction == vm.types.entitySearchDirection.from) { | ||
42 | - vm.relation.to = vm.targetEntityId; | ||
43 | - } else { | ||
44 | - vm.relation.from = vm.targetEntityId; | 93 | + function save() { |
94 | + if (vm.isAdd) { | ||
95 | + if (vm.direction == vm.types.entitySearchDirection.from) { | ||
96 | + vm.relation.to = vm.targetEntityId; | ||
97 | + } else { | ||
98 | + vm.relation.from = vm.targetEntityId; | ||
99 | + } | ||
45 | } | 100 | } |
46 | $scope.theForm.$setPristine(); | 101 | $scope.theForm.$setPristine(); |
47 | - entityRelationService.saveRelation(vm.relation).then( | ||
48 | - function success() { | ||
49 | - $mdDialog.hide(); | 102 | + |
103 | + var valid = true; | ||
104 | + if (vm.additionalInfo && vm.additionalInfo.length) { | ||
105 | + try { | ||
106 | + vm.relation.additionalInfo = angular.fromJson(vm.additionalInfo); | ||
107 | + } catch(e) { | ||
108 | + valid = false; | ||
50 | } | 109 | } |
51 | - ); | 110 | + } else { |
111 | + vm.relation.additionalInfo = null; | ||
112 | + } | ||
113 | + | ||
114 | + $scope.theForm.$setValidity("additionalInfo", valid); | ||
115 | + | ||
116 | + if (valid) { | ||
117 | + entityRelationService.saveRelation(vm.relation).then( | ||
118 | + function success() { | ||
119 | + $mdDialog.hide(); | ||
120 | + } | ||
121 | + ); | ||
122 | + } | ||
52 | } | 123 | } |
53 | 124 | ||
54 | } | 125 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | + | ||
17 | +.tb-relation-additional-info-panel { | ||
18 | + margin-left: 15px; | ||
19 | + border: 1px solid #C0C0C0; | ||
20 | + height: 100%; | ||
21 | + #tb-relation-additional-info { | ||
22 | + min-width: 600px; | ||
23 | + min-height: 200px; | ||
24 | + width: 100%; | ||
25 | + height: 100%; | ||
26 | + } | ||
27 | +} |
ui/src/app/entity/relation/relation-dialog.tpl.html
renamed from
ui/src/app/entity/relation/add-relation-dialog.tpl.html
@@ -15,11 +15,11 @@ | @@ -15,11 +15,11 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<md-dialog aria-label="{{ 'relation.add' | translate }}" style="min-width: 400px;"> | ||
19 | - <form name="theForm" ng-submit="vm.add()"> | 18 | +<md-dialog aria-label="{{ (vm.isAdd ? 'relation.add' : 'relation.edit' ) | translate }}" style="min-width: 400px;"> |
19 | + <form name="theForm" ng-submit="vm.save()"> | ||
20 | <md-toolbar> | 20 | <md-toolbar> |
21 | <div class="md-toolbar-tools"> | 21 | <div class="md-toolbar-tools"> |
22 | - <h2 translate>relation.add</h2> | 22 | + <h2 translate>{{ vm.isAdd ? 'relation.add' : 'relation.edit'}}</h2> |
23 | <span flex></span> | 23 | <span flex></span> |
24 | <md-button class="md-icon-button" ng-click="vm.cancel()"> | 24 | <md-button class="md-icon-button" ng-click="vm.cancel()"> |
25 | <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> | 25 | <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
@@ -32,17 +32,29 @@ | @@ -32,17 +32,29 @@ | ||
32 | <div class="md-dialog-content"> | 32 | <div class="md-dialog-content"> |
33 | <md-content class="md-padding" layout="column"> | 33 | <md-content class="md-padding" layout="column"> |
34 | <fieldset ng-disabled="loading"> | 34 | <fieldset ng-disabled="loading"> |
35 | - <tb-relation-type-autocomplete ng-model="vm.relation.type" | 35 | + <tb-relation-type-autocomplete ng-disabled="!vm.isAdd" |
36 | + ng-model="vm.relation.type" | ||
36 | tb-required="true" | 37 | tb-required="true" |
37 | ng-disabled="loading"> | 38 | ng-disabled="loading"> |
38 | </tb-relation-type-autocomplete> | 39 | </tb-relation-type-autocomplete> |
39 | <small>{{(vm.direction == vm.types.entitySearchDirection.from ? | 40 | <small>{{(vm.direction == vm.types.entitySearchDirection.from ? |
40 | 'relation.to-entity' : 'relation.from-entity') | translate}}</small> | 41 | 'relation.to-entity' : 'relation.from-entity') | translate}}</small> |
41 | <tb-entity-select flex | 42 | <tb-entity-select flex |
43 | + ng-disabled="!vm.isAdd" | ||
42 | the-form="theForm" | 44 | the-form="theForm" |
43 | tb-required="true" | 45 | tb-required="true" |
44 | ng-model="vm.targetEntityId"> | 46 | ng-model="vm.targetEntityId"> |
45 | </tb-entity-select> | 47 | </tb-entity-select> |
48 | + <div class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>relation.additional-info</div> | ||
49 | + <div flex class="tb-relation-additional-info-panel" layout="column"> | ||
50 | + <div flex id="tb-relation-additional-info" | ||
51 | + ui-ace="vm.relationAdditionalInfoOptions" | ||
52 | + ng-model="vm.additionalInfo"> | ||
53 | + </div> | ||
54 | + </div> | ||
55 | + <div class="tb-error-messages" ng-messages="theForm.$error" role="alert"> | ||
56 | + <div translate ng-message="additionalInfo" class="tb-error-message">relation.invalid-additional-info</div> | ||
57 | + </div> | ||
46 | </fieldset> | 58 | </fieldset> |
47 | </md-content> | 59 | </md-content> |
48 | </div> | 60 | </div> |
@@ -51,7 +63,7 @@ | @@ -51,7 +63,7 @@ | ||
51 | <span flex></span> | 63 | <span flex></span> |
52 | <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" | 64 | <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" |
53 | class="md-raised md-primary"> | 65 | class="md-raised md-primary"> |
54 | - {{ 'action.add' | translate }} | 66 | + {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} |
55 | </md-button> | 67 | </md-button> |
56 | <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | | 68 | <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | |
57 | translate }} | 69 | translate }} |
@@ -19,11 +19,11 @@ import './relation-table.scss'; | @@ -19,11 +19,11 @@ import './relation-table.scss'; | ||
19 | /* eslint-disable import/no-unresolved, import/default */ | 19 | /* eslint-disable import/no-unresolved, import/default */ |
20 | 20 | ||
21 | import relationTableTemplate from './relation-table.tpl.html'; | 21 | import relationTableTemplate from './relation-table.tpl.html'; |
22 | -import addRelationTemplate from './add-relation-dialog.tpl.html'; | 22 | +import relationTemplate from './relation-dialog.tpl.html'; |
23 | 23 | ||
24 | /* eslint-enable import/no-unresolved, import/default */ | 24 | /* eslint-enable import/no-unresolved, import/default */ |
25 | 25 | ||
26 | -import AddRelationController from './add-relation-dialog.controller'; | 26 | +import RelationController from './relation-dialog.controller'; |
27 | 27 | ||
28 | /*@ngInject*/ | 28 | /*@ngInject*/ |
29 | export default function RelationTable() { | 29 | export default function RelationTable() { |
@@ -66,6 +66,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | @@ -66,6 +66,7 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | ||
66 | vm.onReorder = onReorder; | 66 | vm.onReorder = onReorder; |
67 | vm.onPaginate = onPaginate; | 67 | vm.onPaginate = onPaginate; |
68 | vm.addRelation = addRelation; | 68 | vm.addRelation = addRelation; |
69 | + vm.editRelation = editRelation; | ||
69 | vm.deleteRelation = deleteRelation; | 70 | vm.deleteRelation = deleteRelation; |
70 | vm.deleteRelations = deleteRelations; | 71 | vm.deleteRelations = deleteRelations; |
71 | vm.reloadRelations = reloadRelations; | 72 | vm.reloadRelations = reloadRelations; |
@@ -110,18 +111,52 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | @@ -110,18 +111,52 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ | ||
110 | if ($event) { | 111 | if ($event) { |
111 | $event.stopPropagation(); | 112 | $event.stopPropagation(); |
112 | } | 113 | } |
113 | - var entityId = { | ||
114 | - id: vm.entityId, | ||
115 | - entityType: vm.entityType | ||
116 | - }; | 114 | + openRelationDialog($event); |
115 | + } | ||
116 | + | ||
117 | + function editRelation($event, relation) { | ||
118 | + if ($event) { | ||
119 | + $event.stopPropagation(); | ||
120 | + } | ||
121 | + openRelationDialog($event, relation); | ||
122 | + } | ||
123 | + | ||
124 | + function openRelationDialog($event, relation) { | ||
125 | + if ($event) { | ||
126 | + $event.stopPropagation(); | ||
127 | + } | ||
128 | + var isAdd = false; | ||
129 | + if (!relation) { | ||
130 | + isAdd = true; | ||
131 | + var entityId = { | ||
132 | + id: vm.entityId, | ||
133 | + entityType: vm.entityType | ||
134 | + }; | ||
135 | + relation = {}; | ||
136 | + if (vm.direction == vm.types.entitySearchDirection.from) { | ||
137 | + relation.from = entityId; | ||
138 | + } else { | ||
139 | + relation.to = entityId; | ||
140 | + } | ||
141 | + } | ||
142 | + var onShowingCallback = { | ||
143 | + onShowing: function(){} | ||
144 | + } | ||
117 | $mdDialog.show({ | 145 | $mdDialog.show({ |
118 | - controller: AddRelationController, | 146 | + controller: RelationController, |
119 | controllerAs: 'vm', | 147 | controllerAs: 'vm', |
120 | - templateUrl: addRelationTemplate, | 148 | + templateUrl: relationTemplate, |
121 | parent: angular.element($document[0].body), | 149 | parent: angular.element($document[0].body), |
122 | - locals: { direction: vm.direction, entityId: entityId }, | 150 | + locals: { isAdd: isAdd, |
151 | + direction: vm.direction, | ||
152 | + relation: relation, | ||
153 | + showingCallback: onShowingCallback}, | ||
154 | + targetEvent: $event, | ||
123 | fullscreen: true, | 155 | fullscreen: true, |
124 | - targetEvent: $event | 156 | + skipHide: true, |
157 | + onShowing: function(scope, element) { | ||
158 | + onShowingCallback.onShowing(scope, element); | ||
159 | + } | ||
125 | }).then(function () { | 160 | }).then(function () { |
126 | reloadRelations(); | 161 | reloadRelations(); |
127 | }, function () { | 162 | }, function () { |
@@ -112,6 +112,12 @@ | @@ -112,6 +112,12 @@ | ||
112 | <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.from">{{ relation.toName }}</td> | 112 | <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.from">{{ relation.toName }}</td> |
113 | <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.to">{{ relation.fromName }}</td> | 113 | <td md-cell ng-if="vm.direction == vm.types.entitySearchDirection.to">{{ relation.fromName }}</td> |
114 | <td md-cell class="tb-action-cell"> | 114 | <td md-cell class="tb-action-cell"> |
115 | + <md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}" ng-click="vm.editRelation($event, relation)"> | ||
116 | + <md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">edit</md-icon> | ||
117 | + <md-tooltip md-direction="top"> | ||
118 | + {{ 'relation.edit' | translate }} | ||
119 | + </md-tooltip> | ||
120 | + </md-button> | ||
115 | <md-button class="md-icon-button" aria-label="{{ 'action.delete' | translate }}" ng-click="vm.deleteRelation($event, relation)"> | 121 | <md-button class="md-icon-button" aria-label="{{ 'action.delete' | translate }}" ng-click="vm.deleteRelation($event, relation)"> |
116 | <md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">delete</md-icon> | 122 | <md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">delete</md-icon> |
117 | <md-tooltip md-direction="top"> | 123 | <md-tooltip md-direction="top"> |
@@ -888,6 +888,7 @@ export default angular.module('thingsboard.locale', []) | @@ -888,6 +888,7 @@ export default angular.module('thingsboard.locale', []) | ||
888 | "relation-type-required": "Relation type is required.", | 888 | "relation-type-required": "Relation type is required.", |
889 | "any-relation-type": "Any type", | 889 | "any-relation-type": "Any type", |
890 | "add": "Add relation", | 890 | "add": "Add relation", |
891 | + "edit": "Edit relation", | ||
891 | "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?", | 892 | "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?", |
892 | "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.", | 893 | "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.", |
893 | "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?", | 894 | "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?", |
@@ -899,7 +900,9 @@ export default angular.module('thingsboard.locale', []) | @@ -899,7 +900,9 @@ export default angular.module('thingsboard.locale', []) | ||
899 | "remove-relation-filter": "Remove relation filter", | 900 | "remove-relation-filter": "Remove relation filter", |
900 | "add-relation-filter": "Add relation filter", | 901 | "add-relation-filter": "Add relation filter", |
901 | "any-relation": "Any relation", | 902 | "any-relation": "Any relation", |
902 | - "relation-filters": "Relation filters" | 903 | + "relation-filters": "Relation filters", |
904 | + "additional-info": "Additional info (JSON)", | ||
905 | + "invalid-additional-info": "Unable to parse additional info json." | ||
903 | }, | 906 | }, |
904 | "rule": { | 907 | "rule": { |
905 | "rule": "Rule", | 908 | "rule": "Rule", |