Skip to content

Commit 171e67c

Browse files
committed
Merge branch 'feature/required-is-required-even-for-you' into development
2 parents 64f0415 + 2d7fb3a commit 171e67c

File tree

11 files changed

+168
-52
lines changed

11 files changed

+168
-52
lines changed

examples/data/array.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
"schema": {
33
"type": "object",
44
"title": "Comment",
5+
"required": ["comments"],
56
"properties": {
67
"comments": {
78
"type": "array",
9+
"maxItems": 2,
810
"items": {
911
"type": "object",
1012
"properties": {
@@ -33,7 +35,7 @@
3335
"form": [
3436
{
3537
"type": "help",
36-
"helpvalue": "<h4>Array Example</h4><p>Try adding a couple of forms, reorder by drag'n'drop.</p>"
38+
"helpvalue": "<h4>Array Example</h4><p>Try adding a couple of forms, reorder by drag'n'drop.</p>"
3739
},
3840
{
3941
"key": "comments",

src/directives/array.js

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/**
22
* Directive that handles the model arrays
33
*/
4-
angular.module('schemaForm').directive('sfArray', ['sfSelect','schemaForm',
5-
function(sfSelect, schemaForm) {
4+
angular.module('schemaForm').directive('sfArray', ['sfSelect','schemaForm','sfValidator',
5+
function(sfSelect, schemaForm, sfValidator) {
66

77
var setIndex = function(index) {
88
return function(form) {
@@ -15,7 +15,8 @@ function(sfSelect, schemaForm) {
1515
return {
1616
restrict: 'A',
1717
scope: true,
18-
link: function(scope, element, attrs) {
18+
require: '?ngModel',
19+
link: function(scope, element, attrs, ngModel) {
1920
var formDefCache = {};
2021

2122
// Watch for the form definition and then rewrite it.
@@ -74,21 +75,31 @@ function(sfSelect, schemaForm) {
7475
});
7576

7677
// If there are no defaults nothing is added so we need to initialize
77-
// the array. null for basic values, {} or [] for the others.
78+
// the array. undefined for basic values, {} or [] for the others.
7879
if (len === list.length) {
7980
var type = sfSelect('schema.items.type',form);
80-
var dflt = null;
81+
var dflt;
8182
if (type === 'object') {
8283
dflt = {};
8384
} else if (type === 'array') {
8485
dflt = [];
8586
}
8687
list.push(dflt);
8788
}
89+
90+
// Trigger validation.
91+
if (scope.validateArray) {
92+
scope.validateArray();
93+
}
8894
};
8995

9096
scope.deleteFromArray = function(index) {
9197
list.splice(index,1);
98+
99+
// Trigger validation.
100+
if (scope.validateArray) {
101+
scope.validateArray();
102+
}
92103
};
93104

94105
// Always start with one empty form unless configured otherwise.
@@ -107,9 +118,6 @@ function(sfSelect, schemaForm) {
107118
if (form.titleMap && form.titleMap.length > 0) {
108119
scope.titleMapValues = [];
109120

110-
111-
112-
113121
// We watch the model for changes and the titleMapValues to reflect
114122
// the modelArray
115123
var updateTitleMapValues = function(arr) {
@@ -146,6 +154,55 @@ function(sfSelect, schemaForm) {
146154
});
147155
}
148156

157+
158+
// If there is a ngModel present we need to validate when asked.
159+
if (ngModel) {
160+
var error;
161+
162+
scope.validateArray = function() {
163+
// The actual content of the array is validated by each field
164+
// so we settle for checking validations specific to arrays
165+
166+
// Since we prefill with empty arrays we can get the funny situation
167+
// where the array is required but empty in the gui but still validates.
168+
// Thats why we check the length.
169+
var result = sfValidator.validate(
170+
form,
171+
scope.modelArray.length > 0 ? scope.modelArray : undefined
172+
);
173+
if (result.valid === false &&
174+
result.error &&
175+
(result.error.dataPath === '' ||
176+
result.error.dataPath === '/'+form.key[form.key.length - 1])) {
177+
178+
// Set viewValue to trigger $dirty on field. If someone knows a
179+
// a better way to do it please tell.
180+
ngModel.$setViewValue(scope.modelArray);
181+
error = result.error;
182+
ngModel.$setValidity('schema', false);
183+
184+
} else {
185+
ngModel.$setValidity('schema', true);
186+
}
187+
};
188+
189+
scope.$on('schemaFormValidate',scope.validateArray);
190+
191+
192+
scope.hasSuccess = function(){
193+
return ngModel.$valid && !ngModel.$pristine;
194+
};
195+
196+
scope.hasError = function(){
197+
return ngModel.$invalid;
198+
};
199+
200+
scope.schemaError = function() {
201+
return error;
202+
};
203+
204+
}
205+
149206
once();
150207
});
151208
}

src/directives/decorators/bootstrap/array.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<div sf-array="form">
1+
<div sf-array="form" ng-model="$$value$$">
22
<h3 ng-show="form.title && form.notitle !== true">{{ form.title }}</h3>
33
<ul class="list-group" ng-model="modelArray" ui-sortable>
44
<li class="list-group-item" ng-repeat="item in modelArray track by $index">
@@ -18,4 +18,7 @@ <h3 ng-show="form.title && form.notitle !== true">{{ form.title }}</h3>
1818
{{ form.add || 'Add'}}
1919
</button>
2020
</div>
21+
<div class="help-block"
22+
ng-show="(hasError() && errorMessage(schemaError())) || form.description"
23+
ng-bind-html="(hasError() && errorMessage(schemaError())) || form.description"></div>
2124
</div>

src/directives/decorators/bootstrap/checkbox.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
<input type="checkbox"
44
sf-changed="form"
55
ng-model="$$value$$"
6-
schema-validate="form.schema"
7-
ng-required="form.required">
6+
schema-validate="form">
87
<span ng-bind-html="form.title"></span>
98
</label>
109

src/directives/decorators/bootstrap/datepicker/datepicker.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
style="background-color: white"
66
type="date"
77
class="form-control"
8-
ng-required="form.required"
9-
schema-validate="form.schema"
8+
schema-validate="form"
109
ng-model="$$value$$"
1110
pick-a-date
1211
min-date="form.minDate"

src/directives/decorators/bootstrap/default.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@
66
sf-changed="form"
77
placeholder="{{form.placeholder}}"
88
class="form-control"
9-
ng-required="form.required"
109
ng-model="$$value$$"
11-
schema-validate="form.schema">
10+
schema-validate="form">
1211
<span ng-if="form.feedback !== false"
1312
class="form-control-feedback"
1413
ng-class="evalInScope(form.feedback) || {'glyphicon': true, 'glyphicon-ok': hasSuccess(), 'glyphicon-remove': hasError() }"></span>

src/directives/decorators/bootstrap/select.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
<select ng-model="$$value$$"
66
sf-changed="form"
77
class="form-control"
8-
schema-validate="form.schema"
9-
ng-required="form.required"
8+
schema-validate="form"
109
ng-options="item.value as item.name for item in form.titleMap">
1110
</select>
1211
<div class="help-block"

src/directives/decorators/bootstrap/textarea.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
<label ng-show="showTitle()">{{form.title}}</label>
33
<textarea class="form-control"
44
sf-changed="form"
5-
ng-required="form.required"
65
ng-model="$$value$$"
7-
schema-validate="form.schema"></textarea>
6+
schema-validate="form"></textarea>
87
<!-- disabled until we unbreak the css
98
<span ng-if="form.feedback !== false"
109
class="form-control-feedback"

src/directives/schema-validate.js

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* global tv4 */
2-
angular.module('schemaForm').directive('schemaValidate',function(){
2+
angular.module('schemaForm').directive('schemaValidate',['sfValidator',function(sfValidator){
33
return {
44
restrict: 'A',
55
scope: false,
@@ -10,50 +10,50 @@ angular.module('schemaForm').directive('schemaValidate',function(){
1010
scope.ngModel = ngModel;
1111

1212
var error = null;
13-
var schema = scope.$eval(attrs.schemaValidate);
14-
15-
ngModel.$parsers.unshift(function(viewValue) {
16-
if (!schema) {
17-
schema = scope.$eval(attrs.schemaValidate);
13+
var form = scope.$eval(attrs.schemaValidate);
14+
// Validate against the schema.
15+
var validate = function(viewValue) {
16+
if (!form) {
17+
form = scope.$eval(attrs.schemaValidate);
1818
}
1919

20-
//Still might be undefined, especially if form has no schema...
21-
if (!schema) {
20+
//Still might be undefined
21+
if (!form) {
2222
return viewValue;
2323
}
2424

25-
//required is handled by ng-required
26-
if (angular.isUndefined(viewValue)) {
25+
// Is required is handled by ng-required?
26+
if (angular.isDefined(attrs.ngRequired) && angular.isUndefined(viewValue)) {
2727
return undefined;
2828
}
2929

30-
//Type cast and validate against schema.
31-
//Basic types of json schema sans array and object
32-
var value = viewValue;
33-
if (schema.type === 'integer') {
34-
value = parseInt(value,10);
35-
} else if (schema.type === 'number') {
36-
value = parseFloat(value,10);
37-
} else if (schema.type === 'boolean' && typeof viewValue === 'string') {
38-
if (viewValue === 'true') {
39-
value = true;
40-
} else if (viewValue === 'false') {
41-
value = false;
42-
}
30+
// An empty field gives us the an empty string, which JSON schema
31+
// happily accepts as a proper defined string, but an empty field
32+
// for the user should trigger "required". So we set it to undefined.
33+
if (viewValue === "") {
34+
viewValue = undefined;
4335
}
4436

45-
var result = tv4.validateResult(value,schema);
37+
var result = sfValidator.validate(form, viewValue);
38+
4639
if (result.valid) {
4740
// it is valid
4841
ngModel.$setValidity('schema', true);
49-
error = null;
5042
return viewValue;
5143
} else {
5244
// it is invalid, return undefined (no model update)
5345
ngModel.$setValidity('schema', false);
5446
error = result.error;
5547
return undefined;
5648
}
49+
};
50+
51+
// Unshift onto parsers of the ng-model.
52+
ngModel.$parsers.unshift(validate);
53+
54+
// Listen to an event so we can validate the input on request
55+
scope.$on('schemaFormValidate',function() {
56+
ngModel.$commitViewValue(true);
5757
});
5858

5959
//This works since we now we're inside a decorator and that this is the decorators scope.
@@ -72,4 +72,4 @@ angular.module('schemaForm').directive('schemaValidate',function(){
7272

7373
}
7474
};
75-
});
75+
}]);

src/services/schema-form.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ angular.module('schemaForm').provider('schemaForm',['sfPathProvider', function(s
4747
if (schema.title) f.title = schema.title;
4848
if (schema.description) f.description = schema.description;
4949
if (options.required === true || schema.required === true) f.required = true;
50-
if (schema.default !== undefined) f.default = schema.default;
5150
if (schema.maxLength) f.maxlength = schema.maxLength;
5251
if (schema.minLength) f.minlength = schema.maxLength;
5352
if (schema.readOnly || schema.readonly) f.readonly = schema.readOnly || schema.readonly;
@@ -303,23 +302,29 @@ angular.module('schemaForm').provider('schemaForm',['sfPathProvider', function(s
303302

304303
//if its has tabs, merge them also!
305304
if (obj.tabs) {
306-
angular.forEach(obj.tabs,function(tab){
307-
tab.items = service.merge(schema,tab.items,ignore);
305+
angular.forEach(obj.tabs, function(tab) {
306+
tab.items = service.merge(schema, tab.items, ignore);
308307
});
309308
}
310309

311310
//extend with std form from schema.
312311
if (obj.key) {
313-
if(typeof obj.key == 'string') {
312+
if (typeof obj.key === 'string') {
314313
obj.key = sfPathProvider.parse(obj.key);
315314
}
316315

317316
var str = sfPathProvider.stringify(obj.key);
318-
if(lookup[str]){
319-
return angular.extend(lookup[str],obj);
317+
if (lookup[str]) {
318+
obj = angular.extend(lookup[str], obj);
320319
}
321320
}
322321

322+
// Special case: checkbox
323+
// Since have to ternary state we need a default
324+
if (obj.type === 'checkbox' && angular.isUndefined(obj.schema['default'])) {
325+
obj.schema['default'] = false;
326+
}
327+
323328
return obj;
324329
}));
325330
};

0 commit comments

Comments
 (0)