]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: Use ng-bootstrap-form-validation
authorTiago Melo <tmelo@suse.com>
Thu, 2 May 2019 14:17:09 +0000 (14:17 +0000)
committerTiago Melo <tmelo@suse.com>
Wed, 10 Jul 2019 15:35:50 +0000 (15:35 +0000)
Signed-off-by: Tiago Melo <tmelo@suse.com>
43 files changed:
src/pybind/mgr/dashboard/frontend/package-lock.json
src/pybind/mgr/dashboard/frontend/package.json
src/pybind/mgr/dashboard/frontend/src/app/app.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-discovery-modal/iscsi-target-discovery-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-form/iscsi-target-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-iqn-settings-modal/iscsi-target-iqn-settings-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/mirroring.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/pool-edit-mode-modal/pool-edit-mode-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/pool-edit-peer-modal/pool-edit-peer-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-configuration-form/rbd-configuration-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-form/rbd-snapshot-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-restore-modal/rbd-trash-restore-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-form/configuration-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-form/mgr-module-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-modules.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-reweight-modal/osd-reweight-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form-client/nfs-form-client.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-capability-modal/rgw-user-capability-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-s3-key-modal/rgw-user-s3-key-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-subuser-modal/rgw-user-subuser-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts
src/pybind/mgr/dashboard/frontend/src/app/core/auth/auth.module.ts
src/pybind/mgr/dashboard/frontend/src/app/core/auth/login/login.component.html
src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-form/role-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/components/select/select.component.html
src/pybind/mgr/dashboard/frontend/src/styles.scss

index 350dee8d7912b6823aad80ddfbdf3080f7cf196e..753c114c2816c8acec462ad51ce7f2fdeade75d5 100644 (file)
         "tslib": "^1.9.0"
       }
     },
+    "ng-bootstrap-form-validation": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/ng-bootstrap-form-validation/-/ng-bootstrap-form-validation-4.0.0.tgz",
+      "integrity": "sha512-Whoc9To21hKMtI+yZRVsS19TJMKd5OFVsxJS42LvcOls/uNQBT7LYmS3CGWlwDSGOo85utx3hDHuGzjb78AIoA==",
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
     "ng2-charts": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-1.6.0.tgz",
index cc659f667736a471898f4fc320b8435d9aa6d79a..b84a8d680c7902772db2e0096c88d8f2f46b83e2 100644 (file)
@@ -75,6 +75,7 @@
     "lodash": "4.17.11",
     "moment": "2.24.0",
     "ng-block-ui": "2.1.1",
+    "ng-bootstrap-form-validation": "4.0.0",
     "ng2-charts": "1.6.0",
     "ng2-tree": "2.0.0-rc.11",
     "ngx-bootstrap": "4.3.0",
index 3da5eada655f13679149c058d6ec51c8037d3c03..2f94a0aeee1fd9d6025c1d62aa0c0bf6ece409d2 100644 (file)
@@ -7,6 +7,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
 import { JwtModule } from '@auth0/angular-jwt';
 import { I18n } from '@ngx-translate/i18n-polyfill';
 import { BlockUIModule } from 'ng-block-ui';
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { AccordionModule } from 'ngx-bootstrap/accordion';
 import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
 import { TabsModule } from 'ngx-bootstrap/tabs';
@@ -50,7 +51,8 @@ registerLocaleData(LocaleHelper.getLocaleData(), LocaleHelper.getLocale());
       config: {
         tokenGetter: jwtTokenGetter
       }
-    })
+    }),
+    NgBootstrapFormValidationModule.forRoot()
   ],
   exports: [SharedModule],
   providers: [
index ca56b9623aee3f73d30ac66dac2f232dd4b4857b..29b5692e5be6a1a9b474bc671eca6ce955abdba5 100644 (file)
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { RouterModule, Routes } from '@angular/router';
 
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { TreeModule } from 'ng2-tree';
 import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
 import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
@@ -62,7 +63,8 @@ import { RbdTrashRestoreModalComponent } from './rbd-trash-restore-modal/rbd-tra
     ModalModule.forRoot(),
     SharedModule,
     RouterModule,
-    TreeModule
+    TreeModule,
+    NgBootstrapFormValidationModule
   ],
   declarations: [
     RbdListComponent,
index 4e3679c3721487f8c1e1959bac8eee9df3730969..1940ff36118f2bad3e7f35a1b5973887dc2682ab 100644 (file)
@@ -9,8 +9,7 @@
           novalidate>
       <div class="modal-body">
         <!-- User -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': discoveryForm.showError('user', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-4"
                  for="user"
                  i18n>User</label>
                    class="form-control"
                    formControlName="user"
                    type="text">
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="discoveryForm.showError('user', formDir, 'required')"
                   i18n>This field is required.</span>
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="discoveryForm.showError('user', formDir, 'pattern')"
                   i18n>Usernames must have a length of 8 to 64 characters and
               can only contain letters, '.', '@', '-', '_' or ':'.</span>
@@ -31,8 +30,7 @@
         </div>
 
         <!-- Password -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': discoveryForm.showError('password', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-4"
                  for="password"
                  i18n>Password</label>
                 </button>
               </span>
             </div>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="discoveryForm.showError('password', formDir, 'required')"
                   i18n>This field is required.</span>
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="discoveryForm.showError('password', formDir, 'pattern')"
                   i18n>Passwords must have a length of 12 to 16 characters
               and can only contain letters, '@', '-', '_' or '/'.</span>
@@ -66,8 +64,7 @@
         </div>
 
         <!-- mutual_user -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': discoveryForm.showError('mutual_user', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-4"
                  for="mutual_user">
             <ng-container i18n>Mutual User</ng-container>
                    formControlName="mutual_user"
                    type="text">
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="discoveryForm.showError('mutual_user', formDir, 'required')"
                   i18n>This field is required.</span>
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="discoveryForm.showError('mutual_user', formDir, 'pattern')"
                   i18n>Usernames must have a length of 8 to 64 characters and
               can only contain letters, '.', '@', '-', '_' or ':'.</span>
@@ -90,8 +87,7 @@
         </div>
 
         <!-- mutual_password -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': discoveryForm.showError('mutual_password', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-4"
                  for="mutual_password"
                  i18n>Mutual Password</label>
                 </button>
               </span>
             </div>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="discoveryForm.showError('mutual_password', formDir, 'required')"
                   i18n>This field is required.</span>
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="discoveryForm.showError('mutual_password', formDir, 'pattern')"
                   i18n>Passwords must have a length of 12 to 16 characters and
               can only contain letters, '@', '-', '_' or '/'.</span>
index b3bcb700656ac0f19c9ea4b111cc0aabe2837ad4..6377f4fc82bc26c1faf78564b8fa9e0a72e74f72 100644 (file)
@@ -10,8 +10,7 @@
 
       <div class="card-body">
         <!-- Target IQN -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': targetForm.showError('target_iqn', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="target_iqn">
             <ng-container i18n>Target IQN</ng-container>
               </span>
             </div>
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="targetForm.showError('target_iqn', formDir, 'required')"
                   i18n>This field is required.</span>
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="targetForm.showError('target_iqn', formDir, 'pattern')"
                   i18n>IQN has wrong pattern.</span>
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="targetForm.showError('target_iqn', formDir, 'iqn')">
               <ng-container i18n>An IQN has the following notation
                 'iqn.$year-$month.$reversedAddress:$definedName'</ng-container>
@@ -63,8 +62,7 @@
         </div>
 
         <!-- Portals -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': targetForm.showError('portals', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="portals">
             <ng-container i18n>Portals</ng-container>
@@ -89,7 +87,7 @@
               </div>
             </ng-container>
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="targetForm.showError('portals', formDir, 'minGateways')"
                   i18n>At least {{ minimum_gateways }} gateways are required.</span>
 
         </div>
 
         <!-- Images -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': targetForm.showError('disks', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="disks"
                  i18n>Images</label>
               </span>
             </ng-container>
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="targetForm.showError('disks', formDir, 'required')"
                   i18n>At least 1 image is required.</span>
 
               </div>
               <div class="card-body">
                 <!-- Initiator: Name -->
-                <div class="form-group row"
-                     [ngClass]="{':invalid': initiator.showError('client_iqn', formDir)}">
+                <div class="form-group row">
                   <label class="col-form-label col-sm-3"
                          for="client_iqn">
                     <ng-container i18n>Client IQN</ng-container>
                            formControlName="client_iqn"
                            (blur)="updatedInitiatorSelector()">
 
-                    <span class="form-text text-muted"
+                    <span class="invalid-feedback"
                           *ngIf="initiator.showError('client_iqn', formDir, 'notUnique')"
                           i18n>Initiator IQN needs to be unique.</span>
 
-                    <span class="form-text text-muted"
+                    <span class="invalid-feedback"
                           *ngIf="initiator.showError('client_iqn', formDir, 'required')"
                           i18n>This field is required.</span>
 
-                    <span class="form-text text-muted"
+                    <span class="invalid-feedback"
                           *ngIf="initiator.showError('client_iqn', formDir, 'pattern')"
                           i18n>IQN has wrong pattern.</span>
                   </div>
 
                 <ng-container formGroupName="auth">
                   <!-- Initiator: User -->
-                  <div class="form-group row"
-                       [ngClass]="{':invalid': initiator.showError('user', formDir)}">
+                  <div class="form-group row">
                     <label class="col-form-label col-sm-3"
                            for="user"
                            i18n>User</label>
                              class="form-control"
                              formControlName="user"
                              type="text">
-                      <span class="form-text text-muted"
+                      <span class="invalid-feedback"
                             *ngIf="initiator.showError('user', formDir, 'required')"
                             i18n>This field is required.</span>
 
-                      <span class="form-text text-muted"
+                      <span class="invalid-feedback"
                             *ngIf="initiator.showError('user', formDir, 'pattern')"
                             i18n>Usernames must have a length of 8 to 64 characters and
                         can only contain letters, '.', '@', '-', '_' or ':'.</span>
                   </div>
 
                   <!-- Initiator: Password -->
-                  <div class="form-group row"
-                       [ngClass]="{':invalid': initiator.showError('password', formDir)}">
+                  <div class="form-group row">
                     <label class="col-form-label col-sm-3"
                            for="password"
                            i18n>Password</label>
                           </button>
                         </span>
                       </div>
-                      <span class="form-text text-muted"
+                      <span class="invalid-feedback"
                             *ngIf="initiator.showError('password', formDir, 'required')"
                             i18n>This field is required.</span>
 
-                      <span class="form-text text-muted"
+                      <span class="invalid-feedback"
                             *ngIf="initiator.showError('password', formDir, 'pattern')"
                             i18n>Passwords must have a length of 12 to 16 characters
                         and can only contain letters, '@', '-', '_' or '/'.</span>
 
 
                   <!-- Initiator: mutual_user -->
-                  <div class="form-group row"
-                       [ngClass]="{':invalid': initiator.showError('mutual_user', formDir)}">
+                  <div class="form-group row">
                     <label class="col-form-label col-sm-3"
                            for="mutual_user">
                       <ng-container i18n>Mutual User</ng-container>
                              formControlName="mutual_user"
                              type="text">
 
-                      <span class="form-text text-muted"
+                      <span class="invalid-feedback"
                             *ngIf="initiator.showError('mutual_user', formDir, 'required')"
                             i18n>This field is required.</span>
 
-                      <span class="form-text text-muted"
+                      <span class="invalid-feedback"
                             *ngIf="initiator.showError('mutual_user', formDir, 'pattern')"
                             i18n>Usernames must have a length of 8 to 64 characters and
                         can only contain letters, '.', '@', '-', '_' or ':'.</span>
                   </div>
 
                   <!-- Initiator: mutual_password -->
-                  <div class="form-group row"
-                       [ngClass]="{':invalid': initiator.showError('mutual_password', formDir)}">
+                  <div class="form-group row">
                     <label class="col-form-label col-sm-3"
                            for="mutual_password"
                            i18n>Mutual Password</label>
                           </button>
                         </span>
                       </div>
-                      <span class="form-text text-muted"
+                      <span class="invalid-feedback"
                             *ngIf="initiator.showError('mutual_password', formDir, 'required')"
                             i18n>This field is required.</span>
 
-                      <span class="form-text text-muted"
+                      <span class="invalid-feedback"
                             *ngIf="initiator.showError('mutual_password', formDir, 'pattern')"
                             i18n>Passwords must have a length of 12 to 16 characters and
                         can only contain letters, '@', '-', '_' or '/'.</span>
                 </ng-container>
 
                 <!-- Initiator: Images -->
-                <div class="form-group row"
-                     [ngClass]="{':invalid': initiator.showError('luns', formDir)}">
+                <div class="form-group row">
                   <label class="col-form-label col-sm-3"
                          for="luns"
                          i18n>Images</label>
 
         <!-- Groups -->
         <div class="form-group row"
-             *ngIf="targetForm.getValue('acl_enabled')"
-             [ngClass]="{':invalid': targetForm.showError('groups', formDir)}">
+             *ngIf="targetForm.getValue('acl_enabled')">
           <label class="col-form-label col-sm-3"
                  for="initiators"
                  i18n>Groups</label>
                 </div>
 
                 <!-- Group: members -->
-                <div class="form-group row"
-                     [ngClass]="{':invalid': group.showError('members', formDir)}">
+                <div class="form-group row">
                   <label class="col-form-label col-sm-3"
                          for="members">
                     <ng-container i18n>Initiators</ng-container>
                 </div>
 
                 <!-- Group: disks -->
-                <div class="form-group row"
-                     [ngClass]="{':invalid': group.showError('disks', formDir)}">
+                <div class="form-group row">
                   <label class="col-form-label col-sm-3"
                          for="disks">
                     <ng-container i18n>Images</ng-container>
index 310a1ec2747d9bc26cf0f38290e6523397982082..777bbf2ec0145c98ef96ed13524d7f510f921d7e 100644 (file)
@@ -12,8 +12,7 @@
            i18n>Changing these parameters from their default values is usually not necessary.</p>
 
         <div class="form-group row"
-             *ngFor="let setting of settingsForm.controls | keyvalue"
-             [ngClass]="{':invalid': settingsForm.showError(setting.key, formDir)}">
+             *ngFor="let setting of settingsForm.controls | keyvalue">
           <div class="col-sm-12">
             <label class="col-form-label"
                    for="{{ setting.key }}">{{ setting.key }}</label>
index 932af78cd48ab69e562e04ee0c6f4a2a2ace1aa7..0e8f48280fc8ac880dbb9712304d1cb4d2ee82b6 100644 (file)
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { RouterModule } from '@angular/router';
 
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { TreeModule } from 'ng2-tree';
 import { AlertModule } from 'ngx-bootstrap/alert';
 import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
@@ -35,7 +36,8 @@ import { PoolListComponent } from './pool-list/pool-list.component';
     ModalModule.forRoot(),
     AlertModule.forRoot(),
     TooltipModule.forRoot(),
-    TreeModule
+    TreeModule,
+    NgBootstrapFormValidationModule
   ],
   declarations: [
     DaemonListComponent,
index 6a919d7b5281237e85b5975bd17ec3272d1d3c0f..a667f0eb186ac5400a2659b1c431b812b94ce629 100644 (file)
@@ -15,8 +15,7 @@
           <kbd>Update</kbd>.</ng-container>
         </p>
 
-        <div class="form-group"
-             [ngClass]="{':invalid': editModeForm.showError('mirrorMode', formDir)}">
+        <div class="form-group">
           <label class="col-form-label"
                  for="mirrorMode">
             <span i18n>Mode</span>
@@ -28,7 +27,7 @@
             <option *ngFor="let mirrorMode of mirrorModes"
                     [value]="mirrorMode.id">{{ mirrorMode.name }}</option>
           </select>
-          <span class="form-text text-muted"
+          <span class="invalid-feedback"
                 *ngIf="editModeForm.showError('mirrorMode', formDir, 'cannotDisable')"
                 i18n>Peer clusters must be removed prior to disabling mirror.</span>
         </div>
index 6dffc7d5cf6d268e5cce447d0b52c950c2f94e00..2e64b0b95b312aa40e1400c20a5fbdf98ce27731 100644 (file)
@@ -16,8 +16,7 @@
           <kbd>Submit</kbd>.</ng-container>
         </p>
 
-        <div class="form-group"
-             [ngClass]="{':invalid': editPeerForm.showError('clusterName', formDir)}">
+        <div class="form-group">
           <label class="col-form-label"
                  for="clusterName">
             <span i18n>Cluster Name</span>
                  name="clusterName"
                  formControlName="clusterName"
                  autofocus>
-          <span class="form-text text-muted"
+          <span class="invalid-feedback"
                 *ngIf="editPeerForm.showError('clusterName', formDir, 'required')"
                 i18n>This field is required.</span>
-          <span class="form-text text-muted"
+          <span class="invalid-feedback"
                 *ngIf="editPeerForm.showError('clusterName', formDir, 'invalidClusterName')"
                 i18n>The cluster name is not valid.</span>
         </div>
 
-        <div class="form-group"
-             [ngClass]="{':invalid': editPeerForm.showError('clientID', formDir)}">
+        <div class="form-group">
           <label class="col-form-label"
                  for="clientID">
             <span i18n>CephX ID</span>
                  id="clientID"
                  name="clientID"
                  formControlName="clientID">
-          <span class="form-text text-muted"
+          <span class="invalid-feedback"
                 *ngIf="editPeerForm.showError('clientID', formDir, 'required')"
                 i18n>This field is required.</span>
-          <span class="form-text text-muted"
+          <span class="invalid-feedback"
                 *ngIf="editPeerForm.showError('clientID', formDir, 'invalidClientID')"
                 i18n>The CephX ID is not valid.</span>
         </div>
 
-        <div class="form-group"
-             [ngClass]="{':invalid': editPeerForm.showError('monAddr', formDir)}">
+        <div class="form-group">
           <label class="col-form-label"
                  for="monAddr">
             <span i18n>Monitor Addresses</span>
                  id="monAddr"
                  name="monAddr"
                  formControlName="monAddr">
-          <span class="form-text text-muted"
+          <span class="invalid-feedback"
                 *ngIf="editPeerForm.showError('monAddr', formDir, 'invalidMonAddr')"
                 i18n>The monitory address is not valid.</span>
         </div>
 
-        <div class="form-group"
-             [ngClass]="{':invalid': editPeerForm.showError('key', formDir)}">
+        <div class="form-group">
           <label class="col-form-label"
                  for="key">
             <span i18n>CephX Key</span>
@@ -92,7 +88,7 @@
                  id="key"
                  name="key"
                  formControlName="key">
-          <span class="form-text text-muted"
+          <span class="invalid-feedback"
                 *ngIf="editPeerForm.showError('key', formDir, 'invalidKey')"
                 i18n>CephX key must be base64 encoded.</span>
         </div>
index 00bfcf9cbce94028d0d533d3736a82cec809108f..b79f30947f6fa6aa7154ead5ea4817248378c4b5 100644 (file)
@@ -1,74 +1,68 @@
-<fieldset #cfgFormGroup [formGroup]="form.get('configuration')">
+<fieldset #cfgFormGroup
+          [formGroup]="form.get('configuration')">
   <legend i18n>RBD Configuration</legend>
 
-  <div *ngFor="let section of rbdConfigurationService.sections" class="col-12">
+  <div *ngFor="let section of rbdConfigurationService.sections"
+       class="col-12">
     <h3 class="cd-header">
-      <span
-        (click)="toggleSectionVisibility(section.class)"
+      <span (click)="toggleSectionVisibility(section.class)"
         class="collapsible">{{ section.heading }} <i [ngClass]="!sectionVisibility[section.class] ? icons.addCircle : icons.minusCircle" aria-hidden="true"></i></span>
     </h3>
-    <div class="{{ section.class }}" [hidden]="!sectionVisibility[section.class]">
-      <div
-        class="form-group row"
-        *ngFor="let option of section.options"
-        [ngClass]="{':invalid': form.showError('configuration.' + option.name, cfgFormGroup)}">
-        <label
-          class="col-form-label col-sm-3"
-          [for]="option.name">{{ option.displayName }}<cd-helper>{{ option.description }}</cd-helper></label>
+    <div class="{{ section.class }}"
+         [hidden]="!sectionVisibility[section.class]">
+      <div class="form-group row"
+           *ngFor="let option of section.options">
+        <label class="col-form-label col-sm-3"
+               [for]="option.name">{{ option.displayName }}<cd-helper>{{ option.description }}</cd-helper></label>
 
         <div class="col-sm-9 {{ section.heading }}">
           <div class="input-group">
             <ng-container [ngSwitch]="option.type">
               <ng-container *ngSwitchCase="configurationType.milliseconds">
-                <input
-                  [id]="option.name"
-                  [name]="option.name"
-                  [formControlName]="option.name"
-                  type="text"
-                  class="form-control"
-                  [ngDataReady]="ngDataReady"
-                  cdMilliseconds>
+                <input [id]="option.name"
+                       [name]="option.name"
+                       [formControlName]="option.name"
+                       type="text"
+                       class="form-control"
+                       [ngDataReady]="ngDataReady"
+                       cdMilliseconds>
               </ng-container>
               <ng-container *ngSwitchCase="configurationType.bps">
-                <input
-                  [id]="option.name"
-                  [name]="option.name"
-                  [formControlName]="option.name"
-                  type="text"
-                  class="form-control"
-                  defaultUnit="b"
-                  [ngDataReady]="ngDataReady"
-                  cdDimlessBinaryPerSecond>
+                <input [id]="option.name"
+                       [name]="option.name"
+                       [formControlName]="option.name"
+                       type="text"
+                       class="form-control"
+                       defaultUnit="b"
+                       [ngDataReady]="ngDataReady"
+                       cdDimlessBinaryPerSecond>
               </ng-container>
               <ng-container *ngSwitchCase="configurationType.iops">
-                <input
-                  [id]="option.name"
-                  [name]="option.name"
-                  [formControlName]="option.name"
-                  type="text"
-                  class="form-control"
-                  [ngDataReady]="ngDataReady"
-                  cdIops>
+                <input [id]="option.name"
+                       [name]="option.name"
+                       [formControlName]="option.name"
+                       type="text"
+                       class="form-control"
+                       [ngDataReady]="ngDataReady"
+                       cdIops>
               </ng-container>
             </ng-container>
             <span class="input-group-append">
-              <button
-                class="btn btn-light"
-                type="button"
-                data-toggle="button"
-                [ngClass]="{'active': isDisabled(option.name)}"
-                title="Remove the local configuration value. The parent configuration value will be inherited and used instead."
-                i18n-title
-                (click)="reset(option.name)">
+              <button class="btn btn-light"
+                      type="button"
+                      data-toggle="button"
+                      [ngClass]="{'active': isDisabled(option.name)}"
+                      title="Remove the local configuration value. The parent configuration value will be inherited and used instead."
+                      i18n-title
+                      (click)="reset(option.name)">
                 <i [ngClass]="[icons.erase]"
                    aria-hidden="true"></i>
               </button>
             </span>
           </div>
-          <span
-            i18n
-            class="form-text text-muted"
-            *ngIf="form.showError('configuration.' + option.name, cfgFormGroup, 'min')">The mininum value is 0</span>
+          <span i18n
+                class="invalid-feedback"
+                *ngIf="form.showError('configuration.' + option.name, cfgFormGroup, 'min')">The mininum value is 0</span>
         </div>
       </div>
     </div>
index 93b6c4f82d058db53f1de0a97662a1b7ee34e20a..a94998968f301ae31672946e55667a1319095e36 100644 (file)
@@ -25,8 +25,7 @@
         </div>
 
         <!-- Name -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': rbdForm.showError('name', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="name">
             <ng-container i18n>Name</ng-container>
                    name="name"
                    formControlName="name"
                    autofocus>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="rbdForm.showError('name', formDir, 'required')">
               <ng-container i18n>This field is required.</ng-container>
             </span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="rbdForm.showError('name', formDir, 'pattern')">
               <ng-container i18n>'/' and '@' are not allowed.</ng-container>
             </span>
@@ -53,7 +52,6 @@
 
         <!-- Pool -->
         <div class="form-group row"
-             [ngClass]="{':invalid': rbdForm.showError('pool', formDir)}"
              (change)="onPoolChange($event.target.value)">
           <label class="col-form-label col-sm-3"
                  for="pool">
@@ -87,7 +85,7 @@
                       [value]="pool.pool_name">{{ pool.pool_name }}</option>
             </select>
             <span *ngIf="rbdForm.showError('pool', formDir, 'required')"
-                  class="form-text text-muted"
+                  class="invalid-feedback"
                   i18n>This field is required.</span>
           </div>
         </div>
 
         <!-- Data Pool -->
         <div class="form-group row"
-             [ngClass]="{':invalid': rbdForm.showError('dataPool', formDir)}"
              *ngIf="rbdForm.getValue('useDataPool')">
           <label class="col-form-label col-sm-3"
                  for="dataPool">
               <option *ngFor="let dataPool of dataPools"
                       [value]="dataPool.pool_name">{{ dataPool.pool_name }}</option>
             </select>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="rbdForm.showError('dataPool', formDir, 'required')"
                   i18n>This field is required.</span>
           </div>
         </div>
 
         <!-- Size -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': rbdForm.showError('size', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="size">
             <ng-container i18n>Size</ng-container>
                    placeholder="e.g., 10GiB"
                    defaultUnit="GiB"
                    cdDimlessBinary>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="rbdForm.showError('size', formDir, 'required')"
                   i18n>This field is required.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="rbdForm.showError('size', formDir, 'invalidSizeObject')"
                   i18n>You have to increase the size.</span>
           </div>
 
         <!-- Features -->
         <div class="form-group row"
-             [ngClass]="{':invalid': (formDir.submitted || rbdForm.get('features').dirty) && rbdForm.get('features').invalid}"
              formGroupName="features">
           <label i18n
                  class="col-sm-3 col-form-label"
                 i18n>Striping</h3>
 
             <!-- Object Size -->
-            <div class="form-group row"
-                 [ngClass]="{':invalid': rbdForm.showError('obj_size', formDir)}">
+            <div class="form-group row">
               <label i18n
                      class="col-form-label col-sm-3"
                      for="size">Object size</label>
             </div>
 
             <!-- stripingUnit -->
-            <div class="form-group row"
-                 [ngClass]="{':invalid': rbdForm.showError('stripingUnit', formDir)}">
+            <div class="form-group row">
               <label class="col-form-label col-sm-3"
                      for="stripingUnit">
                 <span i18n>Stripe unit</span>
                   <option *ngFor="let objectSize of objectSizes"
                           [value]="objectSize">{{ objectSize }}</option>
                 </select>
-                <span class="form-text text-muted"
+                <span class="invalid-feedback"
                       *ngIf="rbdForm.showError('stripingUnit', formDir, 'required')"
                       i18n>This field is required because stripe count is defined!</span>
-                <span class="form-text text-muted"
+                <span class="invalid-feedback"
                       *ngIf="rbdForm.showError('stripingUnit', formDir, 'invalidStripingUnit')"
                       i18n>Stripe unit is greater than object size.</span>
               </div>
             </div>
 
             <!-- Stripe Count -->
-            <div class="form-group row"
-                 [ngClass]="{':invalid': rbdForm.showError('stripingCount', formDir)}">
+            <div class="form-group row">
               <label class="col-form-label col-sm-3"
                      for="stripingCount">
                 <span i18n>Stripe count</span>
                        formControlName="stripingCount"
                        class="form-control"
                        type="number">
-                <span class="form-text text-muted"
+                <span class="invalid-feedback"
                       *ngIf="rbdForm.showError('stripingCount', formDir, 'required')"
                       i18n>This field is required because stripe unit is defined!</span>
-                <span class="form-text text-muted"
+                <span class="invalid-feedback"
                       *ngIf="rbdForm.showError('stripingCount', formDir, 'min')"
                       i18n>Stripe count must be greater than 0.</span>
               </div>
index d467c8f68b2b3529774473d251ab4ed76f9a7133..ad6f8479d393dba430f72d12ab3414e49f9dcc7f 100644 (file)
@@ -15,8 +15,7 @@
   <div class="modal-body">
 
     <!-- Name -->
-    <div class="form-group row"
-         [ngClass]="{':invalid': snapshotForm.showError('snapshotName', formDir)}">
+    <div class="form-group row">
       <label class="col-form-label col-sm-3"
              for="snapshotName">
         <ng-container i18n>Name</ng-container>
@@ -30,8 +29,8 @@
                name="snapshotName"
                formControlName="snapshotName"
                autofocus>
-        <span class="form-text text-muted"
-              *ngIf="snapshotForm.showError('snapshotName', formDir, 'required')">
+        <span class="invalid-feedback"
+              *ngIf="snapshotForm.showError('snapshotName', formDir, 'required')"
               i18n>This field is required.</span>
       </div>
     </div>
index c979172ee5773443688469605478ff3741565639..f772e42e2480ea4dae2d679ec4a4426fed2f8f51 100644 (file)
@@ -12,8 +12,7 @@
         <p i18n>To move <kbd>{{ poolName }}/{{ imageName }}</kbd> to trash,
           click <kbd>Move Image</kbd>. Optionally, you can pick an expiration date.</p>
 
-        <div class="form-group"
-             [ngClass]="{':invalid': moveForm.showError('expiresAt', formDir)}">
+        <div class="form-group">
           <label for="expires"
                  i18n>Protection expires at</label>
           <input type="text"
                  [bsConfig]="bsConfig"
                  formControlName="expiresAt"
                  bsDatepicker>
-          <span class="form-text text-muted"
+          <span class="invalid-feedback"
                 *ngIf="moveForm.showError('expiresAt', formDir, 'format')"
                 i18n>Wrong date format. Please use "YYYY-MM-DD HH:mm:ss".</span>
-          <span class="form-text text-muted"
+          <span class="invalid-feedback"
                 *ngIf="moveForm.showError('expiresAt', formDir, 'expired')"
                 i18n>Protection has already expired. Please pick a future date or leave it empty.</span>
         </div>
index 9af2cf6df5c41e3630966b1a89ae4debd7aa0bbf..718c0882062e97042ec1aa223e666709c528b1bd 100644 (file)
@@ -16,8 +16,7 @@
           <kbd i18n>Restore Image</kbd>.
         </p>
 
-        <div class="form-group"
-             [ngClass]="{':invalid': restoreForm.showError('name', formDir)}">
+        <div class="form-group">
           <label for="name"
                  i18n>New Name</label>
           <input type="text"
@@ -27,7 +26,7 @@
                  autocomplete="off"
                  formControlName="name"
                  autofocus>
-          <span class="form-text text-muted"
+          <span class="invalid-feedback"
                 *ngIf="restoreForm.showError('name', formDir, 'required')"
                 i18n>This field is required.</span>
         </div>
index 20f63d2226606f55508832ea212fe7e5dde39a65..f38476e1d1c635d376af6733e60bec02c23f64e5 100644 (file)
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { RouterModule } from '@angular/router';
 
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { TreeModule } from 'ng2-tree';
 import { AlertModule } from 'ngx-bootstrap/alert';
 import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
@@ -66,7 +67,8 @@ import { SilenceMatcherModalComponent } from './prometheus/silence-matcher-modal
     MgrModulesModule,
     TypeaheadModule.forRoot(),
     TimepickerModule.forRoot(),
-    BsDatepickerModule.forRoot()
+    BsDatepickerModule.forRoot(),
+    NgBootstrapFormValidationModule
   ],
   declarations: [
     HostsComponent,
index 17486d74a08d2b7bdc3d4f259c62330d8de680ff..0f535cb810ac9abf34da05e57c45416679df81d5 100644 (file)
             </div>
 
             <div class="form-group row"
-                 [ngClass]="{':invalid': configForm.showError(section, formDir)}"
                  *ngIf="type !== 'bool'">
               <label class="col-form-label col-sm-3"
                      [for]="section">{{ section }}
                        [placeholder]="humanReadableType"
                        [formControlName]="section"
                        [step]="getStep(type, this.configForm.getValue(section))">
-                <span class="form-text text-muted"
+                <span class="invalid-feedback"
                       *ngIf="configForm.showError(section, formDir, 'pattern')">
                   {{ patternHelpText }}
                 </span>
-                <span class="form-text text-muted"
+                <span class="invalid-feedback"
                       *ngIf="configForm.showError(section, formDir, 'invalidUuid')">
                   {{ patternHelpText }}
                 </span>
-                <span class="form-text text-muted"
+                <span class="invalid-feedback"
                       *ngIf="configForm.showError(section, formDir, 'max')"
                       i18n>The entered value is too high! It must not be greater than {{ maxValue }}.</span>
-                <span class="form-text text-muted"
+                <span class="invalid-feedback"
                       *ngIf="configForm.showError(section, formDir, 'min')"
                       i18n>The entered value is too low! It must not be lower than {{ minValue }}.</span>
               </div>
index 39c82bc20afb187594e1c0a21fe2dcdf31214828..462a270dd0bb0945422540858a632274cb285365 100644 (file)
@@ -14,7 +14,6 @@
            i18n>Edit Manager module</div>
       <div class="card-body">
         <div class="form-group row"
-             [ngClass]="{':invalid': mgrModuleForm.showError(moduleOption.value.name, frm)}"
              *ngFor="let moduleOption of moduleOptions | keyvalue">
 
           <!-- Field label -->
                 {{ value }}
               </option>
             </select>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="mgrModuleForm.showError(moduleOption.value.name, frm, 'invalidUuid')"
                   i18n>The entered value is not a valid UUID, e.g.: 67dcac9f-2c03-4d6c-b7bd-1210b3a259a8</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="mgrModuleForm.showError(moduleOption.value.name, frm, 'pattern')"
                   i18n>The entered value needs to be a valid IP address.</span>
           </div>
                    formControlName="{{ moduleOption.value.name }}"
                    min="{{ moduleOption.value.min }}"
                    max="{{ moduleOption.value.max }}">
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="mgrModuleForm.showError(moduleOption.value.name, frm, 'required')"
                   i18n>This field is required.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="mgrModuleForm.showError(moduleOption.value.name, frm, 'max')"
                   i18n>The entered value is too high! It must be lower or equal to {{ moduleOption.value.max }}.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="mgrModuleForm.showError(moduleOption.value.name, frm, 'min')"
                   i18n>The entered value is too low! It must be greater or equal to {{ moduleOption.value.min }}.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="mgrModuleForm.showError(moduleOption.value.name, frm, 'pattern')"
                   i18n>The entered value needs to be a number.</span>
           </div>
                    class="form-control"
                    type="number"
                    formControlName="{{ moduleOption.value.name }}">
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="mgrModuleForm.showError(moduleOption.value.name, frm, 'required')"
                   i18n>This field is required.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="mgrModuleForm.showError(moduleOption.value.name, frm, 'pattern')"
                   i18n>The entered value needs to be a number or decimal.</span>
           </div>
index 1abf09a629cb4171eb76d7e4642c5f728d1da8f5..9dd94c3605579601a51d78a0f3e7978dfbc37bec 100644 (file)
@@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
 import { NgModule } from '@angular/core';
 import { ReactiveFormsModule } from '@angular/forms';
 
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { TabsModule } from 'ngx-bootstrap/tabs';
 
 import { AppRoutingModule } from '../../../app-routing.module';
@@ -16,7 +17,8 @@ import { MgrModuleListComponent } from './mgr-module-list/mgr-module-list.compon
     CommonModule,
     ReactiveFormsModule,
     SharedModule,
-    TabsModule.forRoot()
+    TabsModule.forRoot(),
+    NgBootstrapFormValidationModule
   ],
   declarations: [MgrModuleListComponent, MgrModuleFormComponent, MgrModuleDetailsComponent]
 })
index 3371496a50d80daba772e444dc46279e98ae64fb..99cd318d1f0b138e2395d8f89ed2026924c47915 100755 (executable)
@@ -8,8 +8,7 @@
           novalidate>
       <div class="modal-body">
         <!-- Priority -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': osdRecvSpeedForm.showError('priority', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-6"
                  for="priority">
             <ng-container i18n>Priority</ng-container>
@@ -25,7 +24,7 @@
                 {{ priority.text }}
               </option>
             </select>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="osdRecvSpeedForm.showError('priority', formDir, 'required')"
                   i18n>This field is required.</span>
           </div>
@@ -50,9 +49,7 @@
 
         <!-- Priority values -->
         <div class="form-group row"
-             *ngFor="let attr of priorityAttrs | keyvalue"
-             [ngClass]="{':invalid': osdRecvSpeedForm.getValue('customizePriority') &&
-             osdRecvSpeedForm.showError(attr.key, formDir)}">
+             *ngFor="let attr of priorityAttrs | keyvalue">
           <label class="col-form-label col-sm-6"
                  [for]="attr.key">{{ attr.value.text }}
             <cd-helper *ngIf="attr.value.desc">{{ attr.value.desc }}</cd-helper>
                    [id]="attr.key"
                    [formControlName]="attr.key"
                    [readonly]="!osdRecvSpeedForm.getValue('customizePriority')">
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="osdRecvSpeedForm.getValue('customizePriority') &&
                   osdRecvSpeedForm.showError(attr.key, formDir, 'required')"
                   i18n>This field is required!</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="osdRecvSpeedForm.getValue('customizePriority') &&
                   osdRecvSpeedForm.showError(attr.key, formDir, 'pattern')"
                   i18n>{{ attr.value.patternHelpText }}</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="osdRecvSpeedForm.getValue('customizePriority') &&
                   osdRecvSpeedForm.showError(attr.key, formDir, 'max')"
                   i18n>The entered value is too high! It must not be greater than {{ attr.value.maxValue }}.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="osdRecvSpeedForm.getValue('customizePriority') &&
                   osdRecvSpeedForm.showError(attr.key, formDir, 'min')"
                   i18n>The entered value is too low! It must not be lower than {{ attr.value.minValue }}.</span>
index a282584b7ed6c972ac30ee3899ff2fac0fb6befb..d4cfce7e715422472fb314d8788bdb76a42f3e2a 100644 (file)
@@ -4,14 +4,14 @@
 
   <ng-container class="modal-content">
     <form [formGroup]="reweightForm">
-      <div class="modal-body" [ngClass]="{':invalid': weight.errors}">
+      <div class="modal-body">
         <div class="row">
           <label for="weight" class="col-sm-2 col-form-label">Weight</label>
           <div class="col-sm-10">
             <input id="weight" class="form-control" type="number"
                    step="0.1" formControlName="weight" min="0" max="1"
                    [value]="currentWeight">
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="weight.errors">
               <span *ngIf="weight.errors?.required"
                     i18n>This field is required.</span>
index e1b3a702fe6d94a6ab887adf0ffdb702d81ca190..7249f72e03d3e87916a946214719a6f9c15b906b 100644 (file)
@@ -24,8 +24,7 @@
 
           <div class="card-body">
             <!-- Addresses -->
-            <div class="form-group row"
-                 [ngClass]="{ ':invalid': showError(index, 'addresses', formDir) }">
+            <div class="form-group row">
               <label i18n
                      class="col-sm-3 col-form-label"
                      for="addresses">Addresses</label>
@@ -36,7 +35,7 @@
                        id="addresses"
                        formControlName="addresses"
                        placeholder="192.168.0.10, 192.168.1.0/8">
-                <span class="form-text text-muted">
+                <span class="invalid-feedback">
                   <span *ngIf="showError(index, 'addresses', formDir, 'required')"
                         i18n>Required field</span>
 
index 966dc3d02e19c86e4d100d0567c97e8f3d89d5aa..d7e9cdb7dd622394d6481c6688fe3658e8432908 100644 (file)
@@ -10,7 +10,6 @@
       <div class="card-body">
         <!-- cluster_id -->
         <div class="form-group row"
-             [ngClass]="{':invalid': nfsForm.showError('cluster_id', formDir)}"
              *ngIf="!isDefaultCluster">
           <label class="col-sm-3 col-form-label"
                  for="cluster_id">
               <option *ngFor="let cluster of allClusters"
                       [value]="cluster">{{ cluster }}</option>
             </select>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('cluster_id', formDir, 'required')"
                   i18n>Required field</span>
           </div>
         </div>
 
         <!-- daemons -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': nfsForm.showError('daemons', formDir)}">
+        <div class="form-group row">
           <label class="col-sm-3 col-form-label"
                  for="daemons">
             <ng-container i18n>Daemons</ng-container>
@@ -84,8 +82,7 @@
         <!-- FSAL -->
         <div formGroupName="fsal">
           <!-- Name -->
-          <div class="form-group row"
-               [ngClass]="{':invalid': nfsForm.showError('name', formDir)}">
+          <div class="form-group row">
             <label class="col-sm-3 col-form-label"
                    for="name">
               <ng-container i18n>Storage Backend</ng-container>
                 <option *ngFor="let fsal of allFsals"
                         [value]="fsal.value">{{ fsal.descr }}</option>
               </select>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="nfsForm.showError('name', formDir, 'required')"
                     i18n>Required field</span>
             </div>
 
           <!-- RGW user -->
           <div class="form-group row"
-               [ngClass]="{':invalid': nfsForm.showError('rgw_user_id', formDir)}"
                *ngIf="nfsForm.getValue('name') === 'RGW'">
             <label class="col-sm-3 col-form-label"
                    for="rgw_user_id">
                 <option *ngFor="let rgwUserId of allRgwUsers"
                         [value]="rgwUserId">{{ rgwUserId }}</option>
               </select>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="nfsForm.showError('rgw_user_id', formDir, 'required')"
                     i18n>Required field</span>
             </div>
 
           <!-- CephFS user_id -->
           <div class="form-group row"
-               [ngClass]="{':invalid': nfsForm.showError('user_id', formDir)}"
                *ngIf="nfsForm.getValue('name') === 'CEPH'">
             <label class="col-sm-3 col-form-label"
                    for="user_id">
                 <option *ngFor="let client of allCephxClients"
                         [value]="client">{{ client }}</option>
               </select>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="nfsForm.showError('user_id', formDir, 'required')"
                     i18n>Required field</span>
             </div>
 
           <!-- CephFS fs_name -->
           <div class="form-group row"
-               [ngClass]="{':invalid': nfsForm.showError('fs_name', formDir)}"
                *ngIf="nfsForm.getValue('name') === 'CEPH'">
             <label class="col-sm-3 col-form-label"
                    for="fs_name">
                 <option *ngFor="let filesystem of allFsNames"
                         [value]="filesystem.name">{{ filesystem.name }}</option>
               </select>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="nfsForm.showError('fs_name', formDir, 'required')"
                     i18n>Required field</span>
             </div>
 
         <!-- Secutiry Label -->
         <div class="form-group row"
-             [ngClass]="{':invalid': nfsForm.showError('security_label', formDir)}"
              *ngIf="nfsForm.getValue('name') === 'CEPH'">
           <label class="col-sm-3 col-form-label"
                  for="security_label">
                    id="sec_label_xattr"
                    formControlName="sec_label_xattr">
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('sec_label_xattr', formDir, 'required')"
                   i18n>Required field</span>
           </div>
 
         <!-- Path -->
         <div class="form-group row"
-             [ngClass]="{':invalid': nfsForm.showError('path', formDir)}"
              *ngIf="nfsForm.getValue('name') === 'CEPH'">
           <label class="col-sm-3 col-form-label"
                  for="path">
                    [typeahead]="pathDataSource"
                    (typeaheadOnSelect)="pathChangeHandler()"
                    (blur)="pathChangeHandler()">
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('path', formDir, 'required')"
                   i18n>Required field</span>
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('path', formDir, 'pattern')"
                   i18n>Path need to start with a '/' and can be followed by a word</span>
             <span class="form-text text-muted"
 
         <!-- Bucket -->
         <div class="form-group row"
-             [ngClass]="{':invalid': nfsForm.showError('path', formDir)}"
              *ngIf="nfsForm.getValue('name') === 'RGW'">
           <label class="col-sm-3 col-form-label"
                  for="path">
                    (typeaheadOnSelect)="bucketChangeHandler()"
                    (blur)="bucketChangeHandler()">
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('path', formDir, 'required')"
                   i18n>Required field</span>
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('path', formDir, 'pattern')"
                   i18n>Path can only be a single '/' or a word</span>
 
         </div>
 
         <!-- NFS Protocol -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': nfsForm.showError('protocolNfsv3', formDir) || nfsForm.showError('protocolNfsv4', formDir)}">
+        <div class="form-group row">
           <label class="col-sm-3 col-form-label"
                  for="protocols">
             <ng-container i18n>NFS Protocol</ng-container>
                      class="custom-control-label"
                      for="protocolNfsv4">NFSv4</label>
             </div>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('protocolNfsv3', formDir, 'required') ||
                   nfsForm.showError('protocolNfsv4', formDir, 'required')"
                   i18n>Required field</span>
 
         <!-- Pseudo -->
         <div class="form-group row"
-             [ngClass]="{':invalid': nfsForm.showError('pseudo', formDir)}"
              *ngIf="nfsForm.getValue('protocolNfsv4')">
           <label class="col-sm-3 col-form-label"
                  for="pseudo">
                    name="pseudo"
                    id="pseudo"
                    formControlName="pseudo">
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('pseudo', formDir, 'required')"
                   i18n>Required field</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('pseudo', formDir, 'pattern')"
                   i18n>Pseudo needs to start with a '/' and can't contain any of the following: >, <, |, &, ( or ).</span>
           </div>
         </div>
 
         <!-- Access Type -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': nfsForm.showError('access_type', formDir)}">
+        <div class="form-group row">
           <label class="col-sm-3 col-form-label"
                  for="access_type">
             <ng-container i18n>Access Type</ng-container>
                   *ngIf="nfsForm.getValue('access_type')">
               {{ getAccessTypeHelp(nfsForm.getValue('access_type')) }}
             </span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('access_type', formDir, 'required')"
                   i18n>Required field</span>
           </div>
         </div>
 
         <!-- Squash -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': nfsForm.showError('squash', formDir)}">
+        <div class="form-group row">
           <label class="col-sm-3 col-form-label"
                  for="squash">
             <ng-container i18n>Squash</ng-container>
                       [value]="squash">{{ squash }}</option>
 
             </select>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('squash', formDir,'required')"
                   i18n>Required field</span>
           </div>
         </div>
 
         <!-- Transport Protocol -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': nfsForm.showError('transportUDP', formDir) || nfsForm.showError('transportTCP', formDir)}">
+        <div class="form-group row">
           <label class="col-sm-3 col-form-label"
                  for="transports">
             <ng-container i18n>Transport Protocol</ng-container>
                      class="custom-control-label"
                      i18n>TCP</label>
             </div>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('transportUDP', formDir, 'required') ||
                   nfsForm.showError('transportTCP', formDir, 'required')"
                   i18n>Required field</span>
index e61227ac7bffb3b7929062391c19cd4e4d7a2c13..79d6562641a6ccea50645113e9ead56b265a6732 100644 (file)
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
 import { ReactiveFormsModule } from '@angular/forms';
 import { RouterModule } from '@angular/router';
 
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { TabsModule } from 'ngx-bootstrap/tabs';
 import { TypeaheadModule } from 'ngx-bootstrap/typeahead';
 
@@ -20,7 +21,8 @@ import { NfsListComponent } from './nfs-list/nfs-list.component';
     SharedModule,
     TabsModule.forRoot(),
     CommonModule,
-    TypeaheadModule.forRoot()
+    TypeaheadModule.forRoot(),
+    NgBootstrapFormValidationModule
   ],
   declarations: [
     NfsListComponent,
index d32333dca03bc7965cb36fc44b1900bc2386e93d..326989814ebe757866941158afa184d2707ee00f 100644 (file)
@@ -14,8 +14,7 @@
       [formGroup]="form"
       novalidate>
   <div class="modal-body">
-    <div class="form-group row"
-         [ngClass]="{':invalid': form.showError('name', frm)}">
+    <div class="form-group row">
       <label for="name"
              class="col-form-label col-sm-3">
         <ng-container i18n>Name</ng-container>
                placeholder="Name..."
                formControlName="name"
                autofocus>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('name', frm, 'required')"
               i18n>This field is required!</span>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('name', frm, 'pattern')"
               i18n>The name can only consist of alphanumeric characters, dashes and underscores.</span>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('name', frm, 'uniqueName')"
               i18n>The chosen erasure code profile name is already in use.</span>
       </div>
             {{ plugin }}
           </option>
         </select>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('name', frm, 'required')"
               i18n>This field is required!</span>
       </div>
     </div>
 
-    <div class="form-group row"
-         [ngClass]="{':invalid': form.showError('k', frm)}">
+    <div class="form-group row">
       <label for="k"
              class="col-form-label col-sm-3">
         <ng-container i18n>Data chunks (k)</ng-container>
                ng-model="$ctrl.erasureCodeProfile.k"
                placeholder="Data chunks..."
                formControlName="k">
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('k', frm, 'required')"
               i18n>This field is required!</span>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('k', frm, 'min')"
               i18n>Must be equal to or greater than 2.</span>
       </div>
     </div>
 
-    <div class="form-group row"
-         [ngClass]="{':invalid': form.showError('m', frm)}">
+    <div class="form-group row">
       <label for="m"
              class="col-form-label col-sm-3">
         <ng-container i18n>Coding chunks (m)</ng-container>
                class="form-control"
                placeholder="Coding chunks..."
                formControlName="m">
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('m', frm, 'required')"
               i18n>This field is required!</span>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('m', frm, 'min')"
               i18n>Must be equal to or greater than 1.</span>
       </div>
     </div>
 
     <div class="form-group row"
-         *ngIf="plugin === 'shec'"
-         [ngClass]="{':invalid': form.showError('c', frm)}">
+         *ngIf="plugin === 'shec'">
       <label for="c"
              class="col-form-label col-sm-3">
         <ng-container i18n>Durability estimator (c)</ng-container>
                class="form-control"
                placeholder="Coding chunks..."
                formControlName="c">
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('c', frm, 'min')"
               i18n>Must be equal to or greater than 1.</span>
       </div>
     </div>
 
     <div class="form-group row"
-         *ngIf="plugin === PLUGIN.LRC"
-         [ngClass]="{':invalid': form.showError('l', frm)}">
+         *ngIf="plugin === PLUGIN.LRC">
       <label for="l"
              class="col-form-label col-sm-3">
         <ng-container i18n>Locality (l)</ng-container>
                class="form-control"
                placeholder="Coding chunks..."
                formControlName="l">
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('l', frm, 'required')"
               i18n>This field is required!</span>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('l', frm, 'min')"
               i18n>Must be equal to or greater than 1.</span>
       </div>
     </div>
 
     <div class="form-group row"
-         *ngIf="plugin === PLUGIN.JERASURE"
-         [ngClass]="{':invalid': form.showError('packetSize', frm)}">
+         *ngIf="plugin === PLUGIN.JERASURE">
       <label for="packetSize"
              class="col-form-label col-sm-3">
         <ng-container i18n>Packetsize</ng-container>
                class="form-control"
                placeholder="Packetsize..."
                formControlName="packetSize">
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="form.showError('packetSize', frm, 'min')"
               i18n>Must be equal to or greater than 1.</span>
       </div>
     </div>
 
-    <div class="form-group row"
-         [ngClass]="{':invalid': form.showError('crushRoot', frm)}">
+    <div class="form-group row">
       <label for="crushRoot"
              class="col-form-label col-sm-3">
         <ng-container i18n>Crush root</ng-container>
index 9db51d4d5849bbfb4160e3aa4086dcfe09e7c096..1bfcd20f624a474a8ccbb3a0ab5ef44ec5842b8a 100644 (file)
@@ -3,6 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { By } from '@angular/platform-browser';
 import { RouterTestingModule } from '@angular/router/testing';
 
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { BsModalRef } from 'ngx-bootstrap/modal';
 import { ToastrModule } from 'ngx-toastr';
 import { of } from 'rxjs';
@@ -28,7 +29,13 @@ describe('ErasureCodeProfileFormComponent', () => {
   let data: {};
 
   configureTestBed({
-    imports: [HttpClientTestingModule, RouterTestingModule, ToastrModule.forRoot(), PoolModule],
+    imports: [
+      HttpClientTestingModule,
+      RouterTestingModule,
+      ToastrModule.forRoot(),
+      PoolModule,
+      NgBootstrapFormValidationModule.forRoot()
+    ],
     providers: [ErasureCodeProfileService, BsModalRef, i18nProviders]
   });
 
index 5e433619cc2e09dbdacbb5151c61c494b82126ee..33e35eb9d5d1871aa6b1f89b2308f3a359e37d77 100644 (file)
@@ -16,8 +16,7 @@
 
       <div class="card-body">
         <!-- Name -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': form.showError('name', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="name">
             <ng-container i18n>Name</ng-container>
                    i18n-placeholder
                    formControlName="name"
                    autofocus>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="form.showError('name', formDir, 'required')"
                   i18n>This field is required!</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="form.showError('name', formDir, 'uniqueName')"
                   i18n>The chosen Ceph pool name is already in use.</span>
           </div>
         </div>
 
         <!-- Pool type selection -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': form.showError('poolType', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="poolType">
             <ng-container i18n>Pool type</ng-container>
@@ -61,7 +59,7 @@
                 {{ poolType }}
               </option>
             </select>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="form.showError('poolType', formDir, 'required')"
                   i18n>This field is required!</span>
           </div>
@@ -69,8 +67,7 @@
 
         <div *ngIf="form.getValue('poolType')">
           <!-- Pg number -->
-          <div class="form-group row"
-               [ngClass]="{':invalid': form.showError('pgNum', formDir)}">
+          <div class="form-group row">
             <label class="col-form-label col-sm-3"
                    for="pgNum">
               <ng-container i18n>Placement groups</ng-container>
                      (focus)="externalPgChange = false"
                      (blur)="alignPgs()"
                      required>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="form.showError('pgNum', formDir, 'required')"
                     i18n>This field is required!</span>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="form.showError('pgNum', formDir, 'min')"
                     i18n>At least one placement group is needed!</span>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="form.showError('pgNum', formDir, '34')"
                     i18n>Your cluster can't handle this many PGs. Please recalculate the PG amount needed.</span>
               <span class="form-text text-muted">
 
           <!-- Crush ruleset selection -->
           <div class="form-group row"
-               [ngClass]="{':invalid': form.showError('crushRule', formDir)}"
                *ngIf="form.getValue('poolType') && current.rules.length > 0">
             <label class="col-form-label col-sm-3"
                    for="crushRule"
                   </tab>
                 </tabset>
               </span>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="form.showError('crushRule', formDir, 'tooFewOsds')"
                     i18n>The rule can't be used in the current cluster as it has
                 to few OSDs to meet the minimum required OSD by this rule.</span>
 
           <!-- Replica Size -->
           <div class="form-group row"
-               [ngClass]="{':invalid': form.showError('size', formDir)}"
                *ngIf="form.getValue('poolType') === 'replicated'">
             <label class="col-form-label col-sm-3"
                    for="size">
                      name="size"
                      type="number"
                      formControlName="size">
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="form.showError('size', formDir)">
                 <ul class="list-inline">
                   <li i18n>Minimum: {{ getMinSize() }}</li>
                   <li i18n>Maximum: {{ getMaxSize() }}</li>
                 </ul>
               </span>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="form.showError('size', formDir)"
                     i18n>The size specified is out of range. A value from
                 {{ getMinSize() }} to {{ getMaxSize() }} is valid.</span>
           </div>
           <div *ngIf="hasCompressionEnabled()">
             <!-- Compression algorithm selection -->
-            <div class="form-group row"
-                 [ngClass]="{':invalid': form.showError('algorithm', formDir)}">
+            <div class="form-group row">
               <label i18n
                      class="col-form-label col-sm-3"
                      for="algorithm">Algorithm</label>
               </div>
             </div>
 
-              <!-- Compression min blob size -->
-              <div class="form-group row"
-                   [ngClass]="{':invalid': form.showError('minBlobSize', formDir)}">
-                <label i18n
-                       class="col-form-label col-sm-3"
-                       for="minBlobSize">Minimum blob size</label>
-                <div class="col-sm-9">
-                  <input id="minBlobSize"
-                         name="minBlobSize"
-                         formControlName="minBlobSize"
-                         type="text"
-                         min="0"
-                         class="form-control"
-                         i18n-placeholder
-                         placeholder="e.g., 128KiB"
-                         defaultUnit="KiB"
-                         cdDimlessBinary>
-                  <span class="form-text text-muted"
-                        *ngIf="form.showError('minBlobSize', formDir, 'min')"
-                        i18n>Value should be greater than 0</span>
-                  <span class="form-text text-muted"
-                        *ngIf="form.showError('minBlobSize', formDir, 'maximum')"
-                        i18n>Value should be less than the maximum blob size</span>
+            <!-- Compression min blob size -->
+            <div class="form-group row">
+              <label i18n
+                     class="col-form-label col-sm-3"
+                     for="minBlobSize">Minimum blob size</label>
+              <div class="col-sm-9">
+                <input id="minBlobSize"
+                       name="minBlobSize"
+                       formControlName="minBlobSize"
+                       type="text"
+                       min="0"
+                       class="form-control"
+                       i18n-placeholder
+                       placeholder="e.g., 128KiB"
+                       defaultUnit="KiB"
+                       cdDimlessBinary>
+                <span class="invalid-feedback"
+                      *ngIf="form.showError('minBlobSize', formDir, 'min')"
+                      i18n>Value should be greater than 0</span>
+                <span class="invalid-feedback"
+                      *ngIf="form.showError('minBlobSize', formDir, 'maximum')"
+                      i18n>Value should be less than the maximum blob size</span>
               </div>
             </div>
 
             <!-- Compression max blob size -->
-            <div class="form-group row"
-                 [ngClass]="{':invalid': form.showError('maxBlobSize', formDir)}">
+            <div class="form-group row">
               <label i18n
                      class="col-form-label col-sm-3"
                      for="maxBlobSize">Maximum blob size</label>
                        placeholder="e.g., 512KiB"
                        defaultUnit="KiB"
                        cdDimlessBinary>
-                <span class="form-text text-muted"
+                <span class="invalid-feedback"
                       *ngIf="form.showError('maxBlobSize', formDir, 'min')"
                       i18n>Value should be greater than 0</span>
-                <span class="form-text text-muted"
+                <span class="invalid-feedback"
                       *ngIf="form.showError('maxBlobSize', formDir, 'minimum')"
                       i18n>Value should be greater than the minimum blob size</span>
               </div>
             </div>
 
             <!-- Compression ratio -->
-            <div class="form-group row"
-                 [ngClass]="{':invalid': form.showError('ratio', formDir)}">
+            <div class="form-group row">
               <label i18n
                      class="col-form-label col-sm-3"
                      for="ratio">Ratio</label>
                        class="form-control"
                        i18n-placeholder
                        placeholder="Compression ratio">
-                <span class="form-text text-muted"
+                <span class="invalid-feedback"
                       *ngIf="form.showError('ratio', formDir, 'min') || form.showError('ratio', formDir, 'max')"
                       i18n>Value should be between 0.0 and 1.0</span>
               </div>
           </cd-rbd-configuration-form>
         </div>
 
-        <div class="form-group :invalid">
+        <div class="form-group">
           <div class="offset-sm-3 col-sm-9"
                *ngIf="form.hasError('rbdPool')">
             <br>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   i18n>It's not possible to create an RBD pool with '/' in the name.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   i18n>Please change the name or remove 'rbd' from the applications list.</span>
           </div>
         </div>
index a8d6eb5578637daec944fe8f2bd15a50e2e3de58..9032766d1458086a6b54fff68ea72d358e536677 100644 (file)
@@ -5,6 +5,7 @@ import { By } from '@angular/platform-browser';
 import { ActivatedRoute, Router, Routes } from '@angular/router';
 import { RouterTestingModule } from '@angular/router/testing';
 
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { BsModalService } from 'ngx-bootstrap/modal';
 import { TabsModule } from 'ngx-bootstrap/tabs';
 import { ToastrModule } from 'ngx-toastr';
@@ -148,7 +149,8 @@ describe('PoolFormComponent', () => {
       RouterTestingModule.withRoutes(routes),
       ToastrModule.forRoot(),
       TabsModule.forRoot(),
-      PoolModule
+      PoolModule,
+      NgBootstrapFormValidationModule.forRoot()
     ],
     providers: [
       ErasureCodeProfileService,
index 0d3f6c56e9ff6e5cdd749444ba74db6384532a8b..dc47e3c646654370f0b23535a87c52de8e2f0dd1 100644 (file)
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
 import { ReactiveFormsModule } from '@angular/forms';
 import { RouterModule, Routes } from '@angular/router';
 
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
 import { PopoverModule } from 'ngx-bootstrap/popover';
 import { TabsModule } from 'ngx-bootstrap/tabs';
@@ -28,7 +29,8 @@ import { PoolListComponent } from './pool-list/pool-list.component';
     ReactiveFormsModule,
     BsDropdownModule,
     TooltipModule.forRoot(),
-    BlockModule
+    BlockModule,
+    NgBootstrapFormValidationModule
   ],
   exports: [PoolListComponent, PoolFormComponent],
   declarations: [
index f4d209d6f670bfee109aed0c26beb9193614f5c4..4ddf6db0cd9d0ac5dd2e0df72a8343c4b3ae7718 100644 (file)
@@ -32,8 +32,7 @@
         </div>
 
         <!-- Name -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': bucketForm.showError('bid', frm)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="bid">
             <ng-container i18n>Name</ng-container>
                    formControlName="bid"
                    [readonly]="editing"
                    autofocus>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="bucketForm.showError('bid', frm, 'required')"
                   i18n>This field is required.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="bucketForm.showError('bid', frm, 'bucketNameInvalid')"
                   i18n>The value is not valid.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="bucketForm.showError('bid', frm, 'bucketNameExists')"
                   i18n>The chosen name is already in use.</span>
           </div>
         </div>
 
         <!-- Owner -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': bucketForm.showError('owner', frm)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="owner">
             <ng-container i18n>Owner</ng-container>
@@ -84,7 +82,7 @@
               <option *ngFor="let owner of owners"
                       [value]="owner">{{ owner }}</option>
             </select>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="bucketForm.showError('owner', frm, 'required')"
                   i18n>This field is required.</span>
           </div>
index aa88e5be78c2696440458eb243285c468290c723..7a8d982918fc712b57903e5ad7395498bda2e23e 100644 (file)
@@ -15,8 +15,7 @@
   <div class="modal-body">
 
     <!-- Type -->
-    <div class="form-group row"
-         [ngClass]="{':invalid': formGroup.showError('type', frm)}">
+    <div class="form-group row">
       <label class="col-form-label col-sm-3"
              for="type">
         <ng-container i18n>Type</ng-container>
           <option *ngFor="let type of types"
                   [value]="type">{{ type }}</option>
         </select>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="formGroup.showError('type', frm, 'required')"
               i18n>This field is required.</span>
       </div>
     </div>
 
     <!-- Permission -->
-    <div class="form-group row"
-         [ngClass]="{':invalid': formGroup.showError('perm', frm)}">
+    <div class="form-group row">
       <label class="col-form-label col-sm-3"
              for="perm">
         <ng-container i18n>Permission</ng-container>
@@ -67,7 +65,7 @@
             {{ perm }}
           </option>
         </select>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="formGroup.showError('perm', frm, 'required')"
               i18n>This field is required.</span>
       </div>
index 70479815cc53da4215115f12c468f08ff87d8382..517c990ef8f005d729c292ca83147bd231f22668 100644 (file)
@@ -15,8 +15,7 @@
 
       <div class="card-body">
         <!-- Username -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': userForm.showError('uid', frm)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="uid">
             <ng-container i18n>Username</ng-container>
                    formControlName="uid"
                    [readonly]="editing"
                    autofocus>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('uid', frm, 'required')"
                   i18n>This field is required.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('uid', frm, 'notUnique')"
                   i18n>The chosen user ID is already in use.</span>
           </div>
         </div>
 
         <!-- Full name -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': userForm.showError('display_name', frm)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="display_name">
             <ng-container i18n>Full name</ng-container>
                    class="form-control"
                    type="text"
                    formControlName="display_name">
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('display_name', frm, 'required')"
                   i18n>This field is required.</span>
           </div>
         </div>
 
         <!-- Email address -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': userForm.showError('email', frm)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="email"
                  i18n>Email address</label>
                    class="form-control"
                    type="text"
                    formControlName="email">
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('email', frm, 'email')"
                   i18n>This is not a valid email address.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('email', frm, 'notUnique')"
                   i18n>The chosen email address is already in use.</span>
           </div>
         </div>
 
         <!-- Max. buckets -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': userForm.showError('max_buckets', frm)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="max_buckets">
             <ng-container i18n>Max. buckets</ng-container>
                    class="form-control"
                    type="number"
                    formControlName="max_buckets">
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('max_buckets', frm, 'required')"
                   i18n>This field is required.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('max_buckets', frm, 'min')"
                   i18n>The entered value must be >= 0.</span>
           </div>
 
           <!-- Access key -->
           <div class="form-group row"
-               [ngClass]="{':invalid': userForm.showError('access_key', frm)}"
                *ngIf="!editing && !userForm.getValue('generate_key')">
             <label class="col-form-label col-sm-3"
                    for="access_key">
                   </button>
                 </span>
               </div>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="userForm.showError('access_key', frm, 'required')"
                     i18n>This field is required.</span>
             </div>
 
           <!-- Secret key -->
           <div class="form-group row"
-               [ngClass]="{':invalid': userForm.showError('secret_key', frm)}"
                *ngIf="!editing && !userForm.getValue('generate_key')">
             <label class="col-form-label col-sm-3"
                    for="secret_key">
                   </button>
                 </span>
               </div>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="userForm.showError('secret_key', frm, 'required')"
                     i18n>This field is required.</span>
             </div>
 
           <!-- Maximum size -->
           <div class="form-group row"
-               [ngClass]="{':invalid': userForm.showError('user_quota_max_size', frm)}"
                *ngIf="userForm.controls.user_quota_enabled.value && !userForm.getValue('user_quota_max_size_unlimited')">
             <label class="col-form-label col-sm-3"
                    for="user_quota_max_size">
                      type="text"
                      formControlName="user_quota_max_size"
                      cdDimlessBinary>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="userForm.showError('user_quota_max_size', frm, 'required')"
                     i18n>This field is required.</span>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="userForm.showError('user_quota_max_size', frm, 'quotaMaxSize')"
                     i18n>The value is not valid.</span>
             </div>
 
           <!-- Maximum objects -->
           <div class="form-group row"
-               [ngClass]="{':invalid': userForm.showError('user_quota_max_objects', frm)}"
                *ngIf="userForm.controls.user_quota_enabled.value && !userForm.getValue('user_quota_max_objects_unlimited')">
             <label class="col-form-label col-sm-3"
                    for="user_quota_max_objects">
                      class="form-control"
                      type="number"
                      formControlName="user_quota_max_objects">
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="userForm.showError('user_quota_max_objects', frm, 'required')"
                     i18n>This field is required.</span>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="userForm.showError('user_quota_max_objects', frm, 'min')"
                     i18n>The entered value must be >= 0.</span>
             </div>
 
           <!-- Maximum size -->
           <div class="form-group row"
-               [ngClass]="{':invalid': userForm.showError('bucket_quota_max_size', frm)}"
                *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.getValue('bucket_quota_max_size_unlimited')">
             <label class="col-form-label col-sm-3"
                    for="bucket_quota_max_size">
                      type="text"
                      formControlName="bucket_quota_max_size"
                      cdDimlessBinary>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="userForm.showError('bucket_quota_max_size', frm, 'required')"
                     i18n>This field is required.</span>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="userForm.showError('bucket_quota_max_size', frm, 'quotaMaxSize')"
                     i18n>The value is not valid.</span>
             </div>
 
           <!-- Maximum objects -->
           <div class="form-group row"
-               [ngClass]="{':invalid': userForm.showError('bucket_quota_max_objects', frm)}"
                *ngIf="userForm.controls.bucket_quota_enabled.value && !userForm.getValue('bucket_quota_max_objects_unlimited')">
             <label class="col-form-label col-sm-3"
                    for="bucket_quota_max_objects">
                      class="form-control"
                      type="number"
                      formControlName="bucket_quota_max_objects">
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="userForm.showError('bucket_quota_max_objects', frm, 'required')"
                     i18n>This field is required.</span>
-              <span class="form-text text-muted"
+              <span class="invalid-feedback"
                     *ngIf="userForm.showError('bucket_quota_max_objects', frm, 'min')"
                     i18n>The entered value must be >= 0.</span>
             </div>
index 5ea36e7bcb21732e9d006e3f3eb0310747d8f663..3de20c4896c60ac5622b9314a7c2c6a36c7685d2 100644 (file)
@@ -15,8 +15,7 @@
   <div class="modal-body">
 
     <!-- Username -->
-    <div class="form-group row"
-         [ngClass]="{':invalid': formGroup.showError('user', frm)}">
+    <div class="form-group row">
       <label class="col-form-label col-sm-3"
              for="user">
         <ng-container i18n>Username</ng-container>
@@ -42,7 +41,7 @@
           <option *ngFor="let userCandidate of userCandidates"
                   [value]="userCandidate">{{ userCandidate }}</option>
         </select>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="formGroup.showError('user', frm, 'required')"
               i18n>This field is required.</span>
       </div>
@@ -66,7 +65,6 @@
 
     <!-- Access key -->
     <div class="form-group row"
-         [ngClass]="{':invalid': formGroup.showError('access_key', frm)}"
          *ngIf="!formGroup.getValue('generate_key')">
       <label class="col-form-label col-sm-3"
              for="access_key">
@@ -93,7 +91,7 @@
             </button>
           </span>
         </div>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="formGroup.showError('access_key', frm, 'required')"
               i18n>This field is required.</span>
       </div>
 
     <!-- Secret key -->
     <div class="form-group row"
-         [ngClass]="{':invalid': formGroup.showError('secret_key', frm)}"
          *ngIf="!formGroup.getValue('generate_key')">
       <label class="col-form-label col-sm-3"
              for="secret_key">
             </button>
           </span>
         </div>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="formGroup.showError('secret_key', frm, 'required')"
               i18n>This field is required.</span>
       </div>
index 1885c1091b1f78387a9425e8eccc5ece103abf85..dd0b157c5b1ab78d1640acd04e909ce8f9fece7a 100644 (file)
@@ -14,8 +14,7 @@
   <div class="modal-body">
 
     <!-- Username -->
-    <div class="form-group row"
-         [ngClass]="{':invalid': formGroup.showError('uid', frm)}">
+    <div class="form-group row">
       <label class="col-form-label col-sm-3"
              for="uid"
              i18n>Username</label>
@@ -29,8 +28,7 @@
     </div>
 
     <!-- Subuser -->
-    <div class="form-group row"
-         [ngClass]="{':invalid': formGroup.showError('subuid', frm)}">
+    <div class="form-group row">
       <label class="col-form-label col-sm-3"
              for="subuid">
         <ng-container i18n>Subuser</ng-container>
                formControlName="subuid"
                [readonly]="editing"
                autofocus>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="formGroup.showError('subuid', frm, 'required')"
               i18n>This field is required.</span>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="formGroup.showError('subuid', frm, 'subuserIdExists')"
               i18n>The chosen subuser ID is already in use.</span>
       </div>
     </div>
 
     <!-- Permission -->
-    <div class="form-group row"
-         [ngClass]="{':invalid': formGroup.showError('perm', frm)}">
+    <div class="form-group row">
       <label class="col-form-label col-sm-3"
              for="perm">
         <ng-container i18n>Permission</ng-container>
@@ -77,7 +74,7 @@
           <option i18n
                   value="full-control">full</option>
         </select>
-        <span class="form-text text-muted"
+        <span class="invalid-feedback"
               *ngIf="formGroup.showError('perm', frm, 'required')"
               i18n>This field is required.</span>
       </div>
 
       <!-- Secret key -->
       <div class="form-group row"
-           [ngClass]="{':invalid': formGroup.showError('secret_key', frm)}"
            *ngIf="!editing && !formGroup.getValue('generate_secret')">
         <label class="col-form-label col-sm-3"
                for="secret_key">
               </button>
             </span>
           </div>
-          <span class="form-text text-muted"
+          <span class="invalid-feedback"
                 *ngIf="formGroup.showError('secret_key', frm, 'required')"
                 i18n>This field is required.</span>
         </div>
index 25ce79a373dfc195aba91044d7a3766e5660c03d..62f849d7bc2d0ec5383bfa4f5746e49440c1709e 100644 (file)
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { RouterModule, Routes } from '@angular/router';
 
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { AlertModule } from 'ngx-bootstrap/alert';
 import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
 import { ModalModule } from 'ngx-bootstrap/modal';
@@ -48,7 +49,8 @@ import { RgwUserSwiftKeyModalComponent } from './rgw-user-swift-key-modal/rgw-us
     TabsModule.forRoot(),
     TooltipModule.forRoot(),
     ModalModule.forRoot(),
-    RouterModule
+    RouterModule,
+    NgBootstrapFormValidationModule
   ],
   exports: [
     Rgw501Component,
index cdba46c2e2c8377bf836fab2d71d462a8b102115..f9ecf551cbbd704e8403bd297e4bfdb9403bee64 100644 (file)
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { RouterModule, Routes } from '@angular/router';
 
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
 import { PopoverModule } from 'ngx-bootstrap/popover';
 import { TabsModule } from 'ngx-bootstrap/tabs';
@@ -27,7 +28,8 @@ import { UserTabsComponent } from './user-tabs/user-tabs.component';
     ReactiveFormsModule,
     SharedModule,
     TabsModule.forRoot(),
-    RouterModule
+    RouterModule,
+    NgBootstrapFormValidationModule
   ],
   declarations: [
     LoginComponent,
index 863ff2d78c9fd3858e7b27453ffef17af2a345f2..2fa0114a383b16ded761129374210901f4f3c09a 100644 (file)
@@ -19,8 +19,7 @@
         </div>
 
         <!-- Username -->
-        <div class="form-group has-feedback"
-             [ngClass]="{':invalid': (loginForm.submitted || username.dirty) && username.invalid}">
+        <div class="form-group has-feedback">
           <input name="username"
                  [(ngModel)]="model.username"
                  #username="ngModel"
                  class="form-control"
                  required
                  autofocus>
-          <div class="form-text text-muted"
+          <div class="invalid-feedback"
                *ngIf="(loginForm.submitted || username.dirty) && username.invalid"
                i18n>Username is required</div>
         </div>
 
         <!-- Password -->
-        <div class="form-group has-feedback"
-             [ngClass]="{':invalid': (loginForm.submitted || password.dirty) && password.invalid}">
+        <div class="form-group has-feedback">
           <div class="input-group">
             <input id="password"
                    name="password"
@@ -53,7 +51,7 @@
               </button>
             </span>
           </div>
-          <div class="form-text text-muted"
+          <div class="invalid-feedback"
                *ngIf="(loginForm.submitted || password.dirty) && password.invalid"
                i18n>Password is required</div>
         </div>
index c857f027a1865ec59b129dc65c964d2377201bce..fd6ff143e141ace965dc1555318e95c3f9edf654 100644 (file)
@@ -9,8 +9,7 @@
       <div class="card-body">
 
         <!-- Name -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': roleForm.showError('name', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="name">
             <ng-container i18n>Name</ng-container>
                    name="name"
                    formControlName="name"
                    autofocus>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="roleForm.showError('name', formDir, 'required')"
                   i18n>This field is required.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="roleForm.showError('name', formDir, 'notUnique')"
                   i18n>The chosen name is already in use.</span>
           </div>
         </div>
 
         <!-- Description -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': roleForm.showError('description', formDir)}">
+        <div class="form-group row">
           <label i18n
                  class="col-form-label col-sm-3"
                  for="description">Description</label>
index 990aaeed904a8ae8a893091a1b484f3e5ac0c3d9..c1ecff9c7e881d9fc2868f9340ed69abb8aa9ec2 100644 (file)
@@ -9,8 +9,7 @@
       <div class="card-body">
 
         <!-- Username -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': userForm.showError('username', formDir)}">
+        <div class="form-group row">
           <label class="col-form-label col-sm-3"
                  for="username">
             <ng-container i18n>Username</ng-container>
                    name="username"
                    formControlName="username"
                    autofocus>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('username', formDir, 'required')"
                   i18n>This field is required.</span>
           </div>
         </div>
 
         <!-- Password -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': userForm.showError('password', formDir)}">
+        <div class="form-group row">
           <label i18n
                  class="col-form-label col-sm-3"
                  for="password">Password</label>
                 </button>
               </span>
             </div>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('password', formDir, 'required')"
                   i18n>This field is required.</span>
           </div>
         </div>
 
         <!-- Confirm password -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': userForm.showError('confirmpassword', formDir)}">
+        <div class="form-group row">
           <label i18n
                  class="col-form-label col-sm-3"
                  for="confirmpassword">Confirm password</label>
                 </button>
               </span>
             </div>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('confirmpassword', formDir, 'required')"
                   i18n>This field is required.</span>
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('confirmpassword', formDir, 'match')"
                   i18n>Password confirmation doesn't match the password.</span>
           </div>
         </div>
 
         <!-- Email -->
-        <div class="form-group row"
-             [ngClass]="{':invalid': userForm.showError('email', formDir)}">
+        <div class="form-group row">
           <label i18n
                  class="col-form-label col-sm-3"
                  for="email">Email</label>
                    name="email"
                    formControlName="email">
 
-            <span class="form-text text-muted"
+            <span class="invalid-feedback"
                   *ngIf="userForm.showError('email', formDir, 'email')"
                   i18n>Invalid email.</span>
           </div>
index e2ad026e218d03db363ce300d8c99ff545acc111..625ceb2d209f0c11b1dcdad25a9e4beb4389f6c2 100644 (file)
@@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
 import { NgModule } from '@angular/core';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 
+import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
 import { ChartsModule } from 'ng2-charts/ng2-charts';
 import { AlertModule } from 'ngx-bootstrap/alert';
 import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
@@ -44,7 +45,8 @@ import { WarningPanelComponent } from './warning-panel/warning-panel.component';
     PipesModule,
     ModalModule.forRoot(),
     DirectivesModule,
-    BsDropdownModule
+    BsDropdownModule,
+    NgBootstrapFormValidationModule
   ],
   declarations: [
     ViewCacheComponent,
index ba73fe1492fac99d610badf17d9cbbc2c20a7618..de8b23ce9b9a0adf95e98278f561855596b0de8c 100644 (file)
@@ -14,8 +14,7 @@
         <div class="question">
           <p i18n>Are you sure that you want to
             {{ actionDescription | lowercase }} the selected {{ itemDescription }}?</p>
-          <div class="form-group"
-               [ngClass]="{':invalid': deletionForm.showError('confirmation', formDir)}">
+          <div class="form-group">
             <div class="custom-control custom-checkbox">
               <input type="checkbox"
                      class="custom-control-input"
index a6e7102db786e22ebe4652344808a00c9abad82e..7da0e9c4ca7b072f2574014003a2ff4416f98dae 100644 (file)
@@ -3,7 +3,7 @@
         #formDir="ngForm"
         [formGroup]="form"
         novalidate>
-    <div [ngClass]="{':invalid': form.showError('filter', formDir)}">
+    <div>
       <input type="text"
              formControlName="filter"
              i18n-placeholder
@@ -11,7 +11,7 @@
              (keyup)="$event.keyCode == 13 ? selectOption() : updateFilter()"
              class="form-control text-center" />
       <ng-container *ngFor="let error of Object.keys(messages.customValidations)">
-        <span class="form-text text-muted text-center"
+        <span class="invalid-feedback text-center"
               *ngIf="form.showError('filter', formDir) && filter.hasError(error)">
           {{ messages.customValidations[error] }}
         </span>
@@ -50,7 +50,7 @@
       {{ messages.add }} '{{ filter.value }}'
     </div>
   </div>
-  <div class=":invalid"
+  <div class="is-invalid"
        *ngIf="data.length === selectionLimit">
     <span class="form-text text-muted text-center text-warning"
           [tooltip]="messages.selectionLimit.tooltip"
index 544e6e5b9fb6cd44a0bc04fdb3b105012e6d26b0..4554edfc3a031bee1892768119bc71b5f3c605b6 100644 (file)
@@ -16,11 +16,37 @@ $font-family-sans-serif: 'Helvetica Neue', Helvetica, Arial, 'Noto Sans', sans-s
   'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
 
 $badge-font-size: 1rem;
+$form-feedback-font-size: 100%;
 
 @import '~bootstrap/scss/bootstrap';
 @import '~fork-awesome/scss/fork-awesome';
 @import 'app/ceph/dashboard/info-card/info-card-popover.scss';
 
+/* Reset checkbox success color */
+.was-validated .custom-control-input:valid,
+.custom-control-input.is-valid {
+  ~ .custom-control-label {
+    color: initial;
+  }
+
+  &:checked ~ .custom-control-label::before {
+    border-color: $custom-control-indicator-checked-border-color;
+    background-color: $component-active-bg;
+  }
+
+  ~ .custom-control-label::before {
+    border-color: $custom-control-indicator-border-color;
+  }
+
+  &:focus ~ .custom-control-label::before {
+    box-shadow: $custom-control-indicator-focus-box-shadow;
+  }
+
+  &:focus:not(:checked) ~ .custom-control-label::before {
+    border-color: $custom-control-indicator-focus-border-color;
+  }
+}
+
 /* Basics */
 html {
   background-color: $color-app-bg;
@@ -369,3 +395,7 @@ pre {
   @extend .p-2;
   // @extend my-2;
 }
+
+bfv-messages {
+  display: none;
+}