Browse Source

Merge "Provide 'validatable-with' directive"

Jenkins 3 years ago
parent
commit
f0023b100f

+ 6
- 1
extensions/mistral/static/mistral/js/mistral.workbook.models.js View File

@@ -559,7 +559,12 @@
559 559
                 'index': 0,
560 560
                 'panelIndex': 0,
561 561
                 'row': 0
562
-              }
562
+              },
563
+              '@constraints': [
564
+                function(value) {
565
+                  return value !== 'workbook1' ? true : 'The sample validation failure.';
566
+                }
567
+              ]
563 568
             })
564 569
           },
565 570
           'description': {

+ 30
- 0
merlin/static/merlin/js/merlin.directives.js View File

@@ -112,6 +112,36 @@
112 112
         }
113 113
       }
114 114
     })
115
+    .directive('validatableWith', function($parse) {
116
+      return {
117
+        restrict: 'A',
118
+        require: 'ngModel',
119
+        link: function(scope, element, attrs, ctrl) {
120
+          var model;
121
+          if ( attrs.validatableWith ) {
122
+            model = $parse(attrs.validatableWith)(scope);
123
+            scope.error = '';
124
+            model.setValidatable && model.setValidatable(true);
125
+            model.on && model.on('validation', function(result) {
126
+              var isValid = (result == 'succeeded'),
127
+                baseMessage = '';
128
+              // (FIXME): hack until Barricade supports validation of empty required entries
129
+              if ( !model.get() && model.isRequired() ) {
130
+                isValid = false;
131
+                baseMessage = 'This field is required.'
132
+              }
133
+              ctrl.$setValidity('barricade', isValid);
134
+              scope.error = model.hasError() ? model.getError() : baseMessage;
135
+            });
136
+            ctrl.$formatters.push(function(modelValue) {
137
+              return modelValue === undefined ?
138
+                ( ctrl.$isEmpty(ctrl.$viewValue) ? undefined : ctrl.$viewValue ) :
139
+                modelValue;
140
+            });
141
+          }
142
+        }
143
+      }
144
+    })
115 145
     .directive('typedField', ['$compile', 'merlin.templates',
116 146
       function($compile, templates) {
117 147
         function updateAutoCompletionDirective(template) {

+ 12
- 1
merlin/static/merlin/js/merlin.field.models.js View File

@@ -32,11 +32,18 @@
32 32
       });
33 33
 
34 34
       var modelMixin = Barricade.Blueprint.create(function(type) {
35
+        var isValid = true,
36
+          isValidatable = false;
35 37
         this.value = function() {
36 38
           if ( !arguments.length ) {
37
-            return this.get();
39
+            if ( isValidatable ) {
40
+              return isValid ? this.get() : undefined;
41
+            } else {
42
+              return this.get();
43
+            }
38 44
           } else {
39 45
             this.set(arguments[0]);
46
+            isValid = !this.hasError();
40 47
           }
41 48
         };
42 49
         this.id = utils.getNewId();
@@ -45,6 +52,10 @@
45 52
           return type;
46 53
         };
47 54
 
55
+        this.setValidatable = function(validatable) {
56
+          isValidatable = validatable;
57
+        };
58
+
48 59
         this.setType = function(_type) {
49 60
           type = _type;
50 61
         };

+ 7
- 2
merlin/static/merlin/scss/merlin.scss View File

@@ -54,8 +54,13 @@
54 54
   }
55 55
 }
56 56
 
57
-editable .fa-pencil {
58
-  color: black;
57
+editable {
58
+  .fa-pencil {
59
+    color: black;
60
+  }
61
+  button:disabled {
62
+    color: gray;
63
+  }
59 64
 }
60 65
 
61 66
 .section {

+ 1
- 1
merlin/static/merlin/templates/editable.html View File

@@ -5,6 +5,6 @@
5 5
 </span>
6 6
 <span ng-show="isEdited">
7 7
   <input type="text" ng-model="editableValue" show-focus="isEdited">
8
-  <button ng-click="accept()"><i class="fa fa-check"></i></button>
8
+  <button ng-click="accept()" ng-disabled="!editableValue"><i class="fa fa-check"></i></button>
9 9
   <button ng-click="reject()"><i class="fa fa-close"></i></button>
10 10
 </span>

+ 2
- 1
merlin/static/merlin/templates/fields/number.html View File

@@ -3,5 +3,6 @@
3 3
   <label for="elem-{$ $id $}">{$ value.title() $}</label>
4 4
   <input type="number" class="form-control" id="elem-{$ $id $}"
5 5
          ng-model="value.value" ng-model-options="{ getterSetter: true }"
6
-         autocompletable="true">
6
+         autocompletable="true" validatable-with="value">
7
+  <div ng-show="error" class="alert alert-danger">{$ error $}</div>
7 8
 </div>

+ 2
- 1
merlin/static/merlin/templates/fields/string.html View File

@@ -2,5 +2,6 @@
2 2
   <label for="elem-{$ $id $}">{$ value.title() $}</label>
3 3
   <input type="text" class="form-control" id="elem-{$ $id $}"
4 4
          ng-model="value.value" ng-model-options="{ getterSetter: true }"
5
-         autocompletable="true">
5
+         autocompletable="true" validatable-with="value">
6
+  <div ng-show="error" class="alert alert-danger">{$ error $}</div>
6 7
 </div>

+ 2
- 1
merlin/static/merlin/templates/fields/text.html View File

@@ -2,5 +2,6 @@
2 2
   <label for="elem-{$ $id $}">{$ value.title() $}</label>
3 3
   <textarea class="form-control" id="elem-{$ $id $}"
4 4
             ng-model="value.value" ng-model-options="{ getterSetter: true }"
5
-            autocompletable="true"></textarea>
5
+            autocompletable="true" validatable-with="value"></textarea>
6
+  <div ng-show="error" class="alert alert-danger">{$ error $}</div>
6 7
 </div>

+ 79
- 0
merlin/test/js/directivesSpec.js View File

@@ -347,4 +347,83 @@ describe('merlin directives', function() {
347 347
     });
348 348
 
349 349
   });
350
+
351
+  describe("'validatable'", function() {
352
+    var fields;
353
+
354
+    beforeEach(inject(function($injector) {
355
+      fields = $injector.get('merlin.field.models');
356
+    }));
357
+
358
+    describe('working with the @constraints property:', function() {
359
+      var model, elt,
360
+        goodValue = 'allowedValue',
361
+        badValue = 'restrictedValue',
362
+        errorMessage = 'Wrong value provided';
363
+      beforeEach(function() {
364
+        var modelClass = fields.string.extend({}, {
365
+          '@constraints': [
366
+            function(value) {
367
+              return value !== badValue ? true : errorMessage;
368
+            }
369
+          ]
370
+        });
371
+        $scope.model = modelClass.create();
372
+        elt = $compile('<form name="form"><input name="model" type="text" ' +
373
+          'ng-model="model.value" ng-model-options="{ getterSetter: true }" ' +
374
+          'validatable-with="model"></form>')($scope);
375
+      });
376
+
377
+      describe('any valid value', function() {
378
+        beforeEach(function() {
379
+          $scope.form.model.$setViewValue(goodValue);
380
+          $scope.$digest();
381
+        });
382
+
383
+        it('is allowed to be entered', function() {
384
+          expect($scope.form.model.$viewValue).toEqual(goodValue);
385
+        });
386
+
387
+        it('is propagated into the model', function() {
388
+          expect($scope.model.value()).toEqual(goodValue);
389
+        });
390
+
391
+        it('does not cause the input to be marked as erroneous', function() {
392
+          expect(elt.find('input').hasClass('ng-valid')).toBe(true);
393
+        });
394
+
395
+        it('sets error message on scope to an empty string', function() {
396
+          expect($scope.error).toEqual('');
397
+        });
398
+      });
399
+
400
+      describe('any invalid value', function() {
401
+        beforeEach(function() {
402
+          $scope.form.model.$setViewValue(badValue);
403
+          $scope.$digest();
404
+        });
405
+
406
+        it('is allowed to be entered', function() {
407
+          expect($scope.form.model.$viewValue).toEqual(badValue);
408
+        });
409
+
410
+        it('is not propagated into the model', function() {
411
+          expect($scope.model.value()).toBe(undefined);
412
+        });
413
+
414
+        it('causes the input to be marked as erroneous', function() {
415
+          expect(elt.find('input').hasClass('ng-invalid')).toBe(true);
416
+        });
417
+
418
+        it('exposes error message in the parent scope', function() {
419
+          expect($scope.error).toEqual(errorMessage);
420
+        })
421
+      });
422
+    });
423
+
424
+    describe('working with the @required property', function() {
425
+      // TODO: fill in once validation of @required fields changes in Barricade
426
+    });
427
+  });
428
+
350 429
 });

Loading…
Cancel
Save