thingsboard-developers

Details

diff --git a/ui/src/app/extension/extension-dialog.controller.js b/ui/src/app/extension/extension-dialog.controller.js
index cc8d7c3..cfec9cf 100644
--- a/ui/src/app/extension/extension-dialog.controller.js
+++ b/ui/src/app/extension/extension-dialog.controller.js
@@ -40,8 +40,6 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate,
 
 
     vm.extensionTypeChange = function () {
-        // $scope.theForm.$setPristine();
-        // $scope.theForm.$setUntouched();
 
         if (vm.extension.type === "HTTP") {
             vm.extension.configuration = {
@@ -68,28 +66,49 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate,
     vm.save = save;
     function save() {
         saveTransformers();
-        if(vm.isAdd) {
-            vm.allExtensions.push(vm.extension);
+
+        let $errorElement = angular.element('[name=theForm]').find('.ng-invalid');
+
+        if ($errorElement.length) {
+
+            let $mdDialogScroll = angular.element('md-dialog-content').scrollTop();
+            let $mdDialogTop = angular.element('md-dialog-content').offset().top;
+            let $errorElementTop = angular.element('[name=theForm]').find('.ng-invalid').eq(0).offset().top;
+
+
+            if ($errorElementTop !== $mdDialogTop) {
+                angular.element('md-dialog-content').animate({
+                    scrollTop: $mdDialogScroll + ($errorElementTop - $mdDialogTop) - 20
+                }, 500);
+                $errorElement.eq(0).focus();
+            }
+
         } else {
-            var index = vm.allExtensions.indexOf(extension);
-            if(index > -1) {
-                vm.allExtensions[index] = vm.extension;
+
+            if(vm.isAdd) {
+                vm.allExtensions.push(vm.extension);
+            } else {
+                var index = vm.allExtensions.indexOf(extension);
+                if(index > -1) {
+                    vm.allExtensions[index] = vm.extension;
+                }
             }
-        }
 
-        var editedValue = angular.toJson(vm.allExtensions);
+            var editedValue = angular.toJson(vm.allExtensions);
 
-        attributeService
-            .saveEntityAttributes(
-                vm.entityType,
-                vm.entityId,
-                types.attributesScope.shared.value,
-                [{key:"configuration", value:editedValue}]
-            )
-            .then(function success() {
+            attributeService
+                .saveEntityAttributes(
+                    vm.entityType,
+                    vm.entityId,
+                    types.attributesScope.shared.value,
+                    [{key:"configuration", value:editedValue}]
+                )
+                .then(function success() {
                     $scope.theForm.$setPristine();
                     $mdDialog.hide();
-            });
+                });
+
+        }
     }
     
     vm.validateId = function() {
diff --git a/ui/src/app/extension/extension-dialog.tpl.html b/ui/src/app/extension/extension-dialog.tpl.html
index b4fac57..2336a60 100644
--- a/ui/src/app/extension/extension-dialog.tpl.html
+++ b/ui/src/app/extension/extension-dialog.tpl.html
@@ -16,7 +16,7 @@
 
 -->
 <md-dialog class="extensionDialog" aria-label="{{ (vm.isAdd ? 'extension.add' : 'extension.edit' ) | translate }}">
-    <form name="theForm" ng-submit="vm.save()">
+    <form name="theForm" ng-submit="vm.save()" novalidate>
         <md-toolbar>
             <div class="md-toolbar-tools">
                 <h2 translate>{{ vm.isAdd ? 'extension.add' : 'extension.edit'}}</h2>
@@ -70,10 +70,9 @@
         </md-dialog-content>
 
         <md-dialog-actions layout="row">
-            <span flex></span>
             <md-button type="submit"
-                       ng-disabled="loading || theForm.$invalid || !theForm.$dirty"
-                       class="md-raised md-primary">
+                       class="md-raised md-primary"
+            >
                 {{ (vm.isAdd  ? 'action.add' : 'action.save') | translate }}
             </md-button>
 
diff --git a/ui/src/app/extension/extensions-forms/extension-form-http.directive.js b/ui/src/app/extension/extensions-forms/extension-form-http.directive.js
index 9a07f2b..4c09078 100644
--- a/ui/src/app/extension/extensions-forms/extension-form-http.directive.js
+++ b/ui/src/app/extension/extensions-forms/extension-form-http.directive.js
@@ -53,36 +53,14 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr
             }
         };
 
-        if(scope.isAdd) {
-            scope.converterConfigs = [];
-            scope.config.converterConfigurations = scope.converterConfigs;
-        } else {
-            scope.converterConfigs = scope.config.converterConfigurations;
-        }
-
-        scope.updateValidity = function() {
-            var valid = scope.converterConfigs && scope.converterConfigs.length > 0;
-            scope.theForm.$setValidity('converterConfigs', valid);
-            if(scope.converterConfigs.length) {
-                for(let i=0;i<scope.converterConfigs.length;i++) {
-                    if(!scope.converterConfigs[i].converters.length) {
-                        scope.theForm.$setValidity('converters', false);
-                        break;
-                    } else {
-                        scope.theForm.$setValidity('converters', true);
-                    }
-                }
-            }
-        };
-
-        scope.$watch('converterConfigs', function() {
-            scope.updateValidity();
-        }, true);
 
         scope.addConverterConfig = function() {
             var newConverterConfig = {converterId:"", converters:[]};
             scope.converterConfigs.push(newConverterConfig);
-        }
+
+            scope.converterConfigs[scope.converterConfigs.length - 1].converters = [];
+            scope.addConverter(scope.converterConfigs[scope.converterConfigs.length - 1].converters);
+        };
 
         scope.removeConverterConfig = function(config) {
             var index = scope.converterConfigs.indexOf(config);
@@ -90,12 +68,17 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr
                 scope.converterConfigs.splice(index, 1);
             }
             scope.theForm.$setDirty();
-        }
+        };
 
         scope.addConverter = function(converters) {
-            var newConverter = {deviceNameJsonExpression:"", deviceTypeJsonExpression:"", attributes:[], timeseries:[]};
+            var newConverter = {
+                deviceNameJsonExpression:"",
+                deviceTypeJsonExpression:"",
+                attributes:[],
+                timeseries:[]
+            };
             converters.push(newConverter);
-        }
+        };
 
         scope.removeConverter = function(converter, converters) {
             var index = converters.indexOf(converter);
@@ -103,12 +86,12 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr
                 converters.splice(index, 1);
             }
             scope.theForm.$setDirty();
-        }
+        };
 
         scope.addAttribute = function(attributes) {
             var newAttribute = {type:"", key:"", value:""};
             attributes.push(newAttribute);
-        }
+        };
 
         scope.removeAttribute = function(attribute, attributes) {
             var index = attributes.indexOf(attribute);
@@ -116,11 +99,44 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr
                 attributes.splice(index, 1);
             }
             scope.theForm.$setDirty();
+        };
+
+
+
+
+
+        if(scope.isAdd) {
+            scope.converterConfigs = scope.config.converterConfigurations;
+            scope.addConverterConfig();
+        } else {
+            scope.converterConfigs = scope.config.converterConfigurations;
         }
 
+
+
+        scope.updateValidity = function() {
+            let valid = scope.converterConfigs && scope.converterConfigs.length > 0;
+            scope.theForm.$setValidity('converterConfigs', valid);
+            if(scope.converterConfigs.length) {
+                for(let i=0; i<scope.converterConfigs.length; i++) {
+                    if(!scope.converterConfigs[i].converters.length) {
+                        scope.theForm.$setValidity('converters', false);
+                        break;
+                    } else {
+                        scope.theForm.$setValidity('converters', true);
+                    }
+                }
+            }
+        };
+
+        scope.$watch('converterConfigs', function() {
+            scope.updateValidity();
+        }, true);
+
+
         scope.transformerTypeChange = function(attribute) {
             attribute.transformer = "";
-        }
+        };
 
         scope.validateTransformer = function (model, editorName) {
             if(model && model.length) {
@@ -131,10 +147,10 @@ export default function ExtensionFormHttpDirective($compile, $templateCache, $tr
                     scope.theForm[editorName].$setValidity('transformerJSON', false);
                 }
             }
-        }
+        };
         
         $compile(element.contents())(scope);
-    }
+    };
 
     return {
         restrict: "A",
diff --git a/ui/src/app/extension/extensions-forms/extension-form-http.tpl.html b/ui/src/app/extension/extensions-forms/extension-form-http.tpl.html
index eb3bb3e..6416656 100644
--- a/ui/src/app/extension/extensions-forms/extension-form-http.tpl.html
+++ b/ui/src/app/extension/extensions-forms/extension-form-http.tpl.html
@@ -33,8 +33,12 @@
                     </div>
                     <div ng-if="converterConfigs.length > 0">
                         <ol class="list-group">
-                            <li class="list-group-item" ng-repeat="(configIndex,config) in converterConfigs">
-                                <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeConverterConfig(config)">
+                            <li class="list-group-item" ng-repeat="(configIndex, config) in converterConfigs">
+                                <md-button aria-label="{{ 'action.remove' | translate }}"
+                                           class="md-icon-button"
+                                           ng-click="removeConverterConfig(config)"
+                                           ng-hide="converterConfigs.length < 2"
+                                >
                                     <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon>
                                     <md-tooltip md-direction="top">
                                         {{ 'action.remove' | translate }}
@@ -65,8 +69,14 @@
                                                     </div>
                                                     <div ng-if="config.converters.length > 0">
                                                         <ol class="list-group">
-                                                            <li class="list-group-item" ng-repeat="(converterIndex,converter) in config.converters">
-                                                                <md-button aria-label="{{ 'action.remove' | translate }}" class="md-icon-button" ng-click="removeConverter(converter, config.converters)">
+                                                            <li class="list-group-item"
+                                                                ng-repeat="(converterIndex,converter) in config.converters"
+                                                            >
+                                                                <md-button aria-label="{{ 'action.remove' | translate }}"
+                                                                           class="md-icon-button"
+                                                                           ng-click="removeConverter(converter, config.converters)"
+                                                                           ng-hide="config.converters.length < 2"
+                                                                >
                                                                     <ng-md-icon icon="close" aria-label="{{ 'action.remove' | translate }}"></ng-md-icon>
                                                                     <md-tooltip md-direction="top">
                                                                         {{ 'action.remove' | translate }}
diff --git a/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html b/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html
index e19984e..8c959db 100644
--- a/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html
+++ b/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html
@@ -216,7 +216,7 @@
                                                         </div>
                                                     </md-input-container>
 
-                                                    <div class="tb-container">
+                                                    <div class="tb-container" ng-class="{'ng-invalid':!server.keystore.file}">
                                                         <span ng-init='fieldsToFill = {"fileName":"fileName", "file":"file"}'></span>
                                                         <label class="tb-label" translate>extension.opc-keystore-location</label>
                                                         <div flow-init="{singleFile:true}" flow-file-added='fileAdded($file, server.keystore, fieldsToFill)' class="tb-file-select-container">