Skip to content

Commit a60fc50

Browse files
committed
Support for required
Both v3 and v4 variant of json schema or as override on the form. Also reduced template duplication.
1 parent b963c9c commit a60fc50

File tree

8 files changed

+134
-22
lines changed

8 files changed

+134
-22
lines changed

src/bootstrap-example.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ <h1>Schema Form Example</h1>
1818
<div class="row">
1919
<div class="col-sm-6">
2020
<h3>The Generated Form</h3>
21-
<form sf-model="person" sf-form="form" sf-schema="schema" sf-decorator="{{decorator}}">
22-
21+
<form name="ngform" sf-model="person" sf-form="form" sf-schema="schema" sf-decorator="{{decorator}}">
2322
</form>
2423
</div>
2524
<div class="col-sm-6">
@@ -51,6 +50,7 @@ <h3>Model</h3>
5150

5251
$scope.schema = {
5352
"type": "object",
53+
"required": ['name','shoesize'],
5454
"properties": {
5555
"name": {
5656
"title": "Name",

src/directives/decorators/bootstrap/bootstrap-decorator.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function($parse, $compile, $http, $templateCache){
1313
return 'directives/decorators/bootstrap/checkbox.html';
1414
}
1515
if (type === 'number') {
16-
return 'directives/decorators/bootstrap/number.html';
16+
return 'directives/decorators/bootstrap/default.html';
1717
}
1818
if (type === 'submit') {
1919
return 'directives/decorators/bootstrap/submit.html';
@@ -28,7 +28,6 @@ function($parse, $compile, $http, $templateCache){
2828
replace: true,
2929
transclude: false,
3030
scope: true,
31-
3231
link: function(scope,element,attrs) {
3332

3433
//rebind our part of the form to the scope.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
<div class="checkbox">
1+
<div class="checkbox" ng-class="{'has-error': hasError()}">
22
<label ng-show="form.title">
3-
<input type="checkbox" ng-model="$$value$$" schema-validate="form.schema">
3+
<input type="checkbox" ng-model="$$value$$" schema-validate="form.schema" ng-required="form.required">
44
{{form.title}}
55
</label>
66

7-
<span class="help-block" ng-show="form.description" >{{form.description}}</span>
7+
<span class="help-block" ng-show="form.description">{{form.description}}</span>
88
</div>
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
<div class="form-group" ng-class="{'has-error':ngModel.$invalid}">
1+
<div class="form-group" ng-class="{'has-error': hasError()}">
22
<label ng-show="form.title">{{form.title}}</label>
3-
<input type="text" class="form-control" name="" ng-model="$$value$$" schema-validate="form.schema">
4-
<span class="help-block" ng-show="form.description && !ngModel.$invalid">{{form.description}} </span>
5-
<span class="help-block" ng-show="ngModel.$invalid">{{schemaError}}</span>
3+
<input type="{{form.type}}" class="form-control" ng-required="form.required" ng-model="$$value$$" schema-validate="form.schema">
4+
5+
<span class="help-block" ng-show="form.description && !hasError()">{{form.description}} </span>
6+
<span class="help-block" ng-show="hasError()">{{schemaError}} {{ngModel.$error}}</span>
67
</div>

src/directives/decorators/bootstrap/number.html

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
<div class="form-group">
1+
<div class="form-group" ng-class="{'has-error': hasError()}">
22
<label ng-show="form.title">
33
{{form.title}}
44
</label>
5-
<select ng-model="$$value$$" class="form-control" schema-validate="form.schema">
6-
<option ng-repeat="(val,name) in form.titleMap" value="{{val}}">{{name}}</option>
5+
<select ng-model="$$value$$"
6+
class="form-control"
7+
schema-validate="form.schema"
8+
ng-required="form.required"
9+
ng-options="val as name for (val,name) in form.titleMap">
710
</select>
8-
<span class="help-block" ng-show="form.description && !ngModel.$invalid">{{form.description}}</span>
9-
<span class="help-block" ng-show="ngModel.$invalid">{{schemaError}}</span>
11+
<span class="help-block" ng-show="form.description && !hasError()">{{form.description}}</span>
12+
<span class="help-block" ng-show="hasError()">{{schemaError}}</span>
1013
</div>

src/directives/schema-validate.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ angular.module('schemaForm').directive('schemaValidate',function(){
1313
schema = scope.$eval(attrs.schemaValidate);
1414
}
1515

16+
//required is handled by ng-required
17+
if (angular.isUndefined(viewValue)) {
18+
return undefined;
19+
}
20+
1621
//Type cast and validate against schema.
1722
//Basic types of json schema sans array and object
1823
var value = viewValue;
@@ -41,6 +46,12 @@ angular.module('schemaForm').directive('schemaValidate',function(){
4146
return undefined;
4247
}
4348
});
49+
50+
//This works since we now we're inside a decorator and that this is the decorators scope.
51+
scope.hasError = function(){
52+
return scope.ngModel.$invalid && !scope.ngModel.$pristine;
53+
};
54+
4455
}
4556
};
4657
});

test/schema-form-test.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,112 @@ describe('Schema form',function(){
205205

206206
});
207207

208+
209+
it('should use ng-required on required fields',function(){
210+
211+
inject(function($compile,$rootScope){
212+
var scope = $rootScope.$new();
213+
scope.person = {};
214+
215+
scope.schema = {
216+
"type": "object",
217+
"required": ["name"],
218+
"properties": {
219+
"name": { "type": "string" },
220+
"nick": { "type": "string" }
221+
}
222+
};
223+
224+
scope.form = ["*"];
225+
226+
var tmpl = angular.element('<form sf-schema="schema" sf-form="form" sf-model="person"></form>');
227+
228+
$compile(tmpl)(scope);
229+
$rootScope.$apply();
230+
231+
tmpl.children().length.should.be.equal(2);
232+
tmpl.children().eq(0).is('div.form-group').should.be.true;
233+
tmpl.children().eq(0).find('input').is('input[type="text"]').should.be.true;
234+
tmpl.children().eq(0).find('input').attr('required').should.be.equal('required');
235+
tmpl.children().eq(0).find('input').attr('ng-model').should.be.equal('model.name');
236+
tmpl.children().eq(1).is('div.form-group').should.be.true;
237+
tmpl.children().eq(1).children('input').length.should.equal(1);
238+
expect(tmpl.children().eq(1).children('input').attr('required')).to.be.undefined;
239+
});
240+
});
241+
242+
it('should use ng-required on required fields, json schema v3',function(){
243+
244+
inject(function($compile,$rootScope){
245+
var scope = $rootScope.$new();
246+
scope.person = {};
247+
248+
scope.schema = {
249+
"type": "object",
250+
"properties": {
251+
"name": { "type": "string", "required": true },
252+
"nick": { "type": "string" }
253+
}
254+
};
255+
256+
scope.form = ["*"];
257+
258+
var tmpl = angular.element('<form sf-schema="schema" sf-form="form" sf-model="person"></form>');
259+
260+
$compile(tmpl)(scope);
261+
$rootScope.$apply();
262+
263+
tmpl.children().length.should.be.equal(2);
264+
tmpl.children().eq(0).is('div.form-group').should.be.true;
265+
tmpl.children().eq(0).find('input').is('input[type="text"]').should.be.true;
266+
tmpl.children().eq(0).find('input').attr('required').should.be.equal('required');
267+
tmpl.children().eq(0).find('input').attr('ng-model').should.be.equal('model.name');
268+
tmpl.children().eq(1).is('div.form-group').should.be.true;
269+
tmpl.children().eq(1).children('input').length.should.equal(1);
270+
expect(tmpl.children().eq(1).children('input').attr('required')).to.be.undefined;
271+
});
272+
});
273+
274+
it('should use ng-required on required fields, form override',function(){
275+
276+
inject(function($compile,$rootScope){
277+
var scope = $rootScope.$new();
278+
scope.person = {};
279+
280+
scope.schema = {
281+
"type": "object",
282+
"properties": {
283+
"name": { "type": "string" },
284+
"nick": { "type": "string" }
285+
}
286+
};
287+
288+
scope.form = [
289+
{ key: 'name', required: true },
290+
'nick'
291+
];
292+
293+
var tmpl = angular.element('<form sf-schema="schema" sf-form="form" sf-model="person"></form>');
294+
295+
$compile(tmpl)(scope);
296+
$rootScope.$apply();
297+
298+
tmpl.children().length.should.be.equal(2);
299+
tmpl.children().eq(0).is('div.form-group').should.be.true;
300+
tmpl.children().eq(0).find('input').is('input[type="text"]').should.be.true;
301+
tmpl.children().eq(0).find('input').attr('required').should.be.equal('required');
302+
tmpl.children().eq(0).find('input').attr('ng-model').should.be.equal('model.name');
303+
tmpl.children().eq(1).is('div.form-group').should.be.true;
304+
tmpl.children().eq(1).children('input').length.should.equal(1);
305+
expect(tmpl.children().eq(1).children('input').attr('required')).to.be.undefined;
306+
});
307+
});
308+
309+
310+
208311
});
209312

313+
210314
describe('decorator directive',function(){
211315
it('should decorate',function(){
212316

0 commit comments

Comments
 (0)