@@ -44,7 +44,7 @@ angular.module('schemaForm').provider('sfPath',
44
44
this . parse = ObjectPath . parse ;
45
45
this . stringify = ObjectPath . stringify ;
46
46
this . normalize = ObjectPath . normalize ;
47
- this . $get = function ( ) {
47
+ this . $get = function ( ) {
48
48
return ObjectPath ;
49
49
} ;
50
50
} ] ) ;
@@ -55,7 +55,7 @@ angular.module('schemaForm').provider('sfPath',
55
55
* @kind function
56
56
*
57
57
*/
58
- angular . module ( 'schemaForm' ) . factory ( 'sfSelect' , [ 'sfPath' , function ( sfPath ) {
58
+ angular . module ( 'schemaForm' ) . factory ( 'sfSelect' , [ 'sfPath' , function ( sfPath ) {
59
59
var numRe = / ^ \d + $ / ;
60
60
61
61
/**
@@ -271,7 +271,7 @@ angular.module('schemaForm').provider('schemaFormDecorators',
271
271
return scope . form . validationMessage [ schemaError . code ] ||
272
272
scope . form . validationMessage [ 'default' ] ;
273
273
} else {
274
- return scope . form . validationMessage . required ||
274
+ return scope . form . validationMessage . number ||
275
275
scope . form . validationMessage [ 'default' ] ||
276
276
scope . form . validationMessage ;
277
277
}
@@ -282,8 +282,8 @@ angular.module('schemaForm').provider('schemaFormDecorators',
282
282
return schemaError . message ; //use tv4.js validation message
283
283
}
284
284
285
- //Otherwise we only use required so it must be it.
286
- return 'Required ' ;
285
+ //Otherwise we only have input number not being a number
286
+ return 'Not a number ' ;
287
287
288
288
} ;
289
289
}
@@ -586,7 +586,6 @@ angular.module('schemaForm').provider('schemaForm',
586
586
} ;
587
587
588
588
var fieldset = function ( name , schema , options ) {
589
-
590
589
if ( schema . type === 'object' ) {
591
590
var f = stdFormObj ( name , schema , options ) ;
592
591
f . type = 'fieldset' ;
@@ -640,7 +639,8 @@ angular.module('schemaForm').provider('schemaForm',
640
639
path : arrPath ,
641
640
required : required || false ,
642
641
lookup : options . lookup ,
643
- ignore : options . ignore
642
+ ignore : options . ignore ,
643
+ global : options . global
644
644
} ) ] ;
645
645
646
646
return f ;
@@ -720,23 +720,27 @@ angular.module('schemaForm').provider('schemaForm',
720
720
721
721
var service = { } ;
722
722
723
- service . merge = function ( schema , form , ignore , options ) {
723
+ service . merge = function ( schema , form , ignore , options , readonly ) {
724
724
form = form || [ '*' ] ;
725
725
options = options || { } ;
726
726
727
+ // Get readonly from root object
728
+ readonly = readonly || schema . readonly || schema . readOnly ;
729
+
727
730
var stdForm = service . defaults ( schema , ignore , options ) ;
731
+
728
732
//simple case, we have a "*", just put the stdForm there
729
733
var idx = form . indexOf ( '*' ) ;
730
734
if ( idx !== - 1 ) {
731
735
form = form . slice ( 0 , idx )
732
736
. concat ( stdForm . form )
733
737
. concat ( form . slice ( idx + 1 ) ) ;
734
- return form ;
735
738
}
736
739
737
740
//ok let's merge!
738
741
//We look at the supplied form and extend it with schema standards
739
742
var lookup = stdForm . lookup ;
743
+
740
744
return postProcessFn ( form . map ( function ( obj ) {
741
745
742
746
//handle the shortcut with just a name
@@ -767,26 +771,32 @@ angular.module('schemaForm').provider('schemaForm',
767
771
} ) ;
768
772
}
769
773
774
+ //extend with std form from schema.
775
+
776
+ if ( obj . key ) {
777
+ var strid = sfPathProvider . stringify ( obj . key ) ;
778
+ if ( lookup [ strid ] ) {
779
+ obj = angular . extend ( lookup [ strid ] , obj ) ;
780
+ }
781
+ }
782
+
783
+ // Are we inheriting readonly?
784
+ if ( readonly === true ) { // Inheriting false is not cool.
785
+ obj . readonly = true ;
786
+ }
787
+
770
788
//if it's a type with items, merge 'em!
771
789
if ( obj . items ) {
772
- obj . items = service . merge ( schema , obj . items , ignore ) ;
790
+ obj . items = service . merge ( schema , obj . items , ignore , options , obj . readonly ) ;
773
791
}
774
792
775
793
//if its has tabs, merge them also!
776
794
if ( obj . tabs ) {
777
795
angular . forEach ( obj . tabs , function ( tab ) {
778
- tab . items = service . merge ( schema , tab . items , ignore ) ;
796
+ tab . items = service . merge ( schema , tab . items , ignore , options , obj . readonly ) ;
779
797
} ) ;
780
798
}
781
799
782
- //extend with std form from schema.
783
- if ( obj . key ) {
784
- var str = sfPathProvider . stringify ( obj . key ) ;
785
- if ( lookup [ str ] ) {
786
- obj = angular . extend ( lookup [ str ] , obj ) ;
787
- }
788
- }
789
-
790
800
// Special case: checkbox
791
801
// Since have to ternary state we need a default
792
802
if ( obj . type === 'checkbox' && angular . isUndefined ( obj . schema [ 'default' ] ) ) {
@@ -893,26 +903,26 @@ angular.module('schemaForm').factory('sfValidator', [function() {
893
903
* @return a tv4js result object.
894
904
*/
895
905
validator . validate = function ( form , value ) {
896
-
906
+ if ( ! form ) {
907
+ return { valid : true } ;
908
+ }
897
909
var schema = form . schema ;
898
910
899
911
if ( ! schema ) {
900
- //Nothings to Validate
901
- return value ;
912
+ return { valid : true } ;
902
913
}
903
914
904
- //Type cast and validate against schema.
905
- //Basic types of json schema sans array and object
906
- if ( schema . type === 'integer' ) {
907
- value = parseInt ( value , 10 ) ;
908
- } else if ( schema . type === 'number' ) {
909
- value = parseFloat ( value , 10 ) ;
910
- } else if ( schema . type === 'boolean' && typeof value === 'string' ) {
911
- if ( value === 'true' ) {
912
- value = true ;
913
- } else if ( value === 'false' ) {
914
- value = false ;
915
- }
915
+ // Input of type text and textareas will give us a viewValue of ''
916
+ // when empty, this is a valid value in a schema and does not count as something
917
+ // that breaks validation of 'required'. But for our own sanity an empty field should
918
+ // not validate if it's required.
919
+ if ( value === '' ) {
920
+ value = undefined ;
921
+ }
922
+
923
+ // Numbers fields will give a null value, which also means empty field
924
+ if ( form . type === 'number' && value === null ) {
925
+ value = undefined ;
916
926
}
917
927
918
928
// Version 4 of JSON Schema has the required property not on the
@@ -929,7 +939,6 @@ angular.module('schemaForm').factory('sfValidator', [function() {
929
939
if ( angular . isDefined ( value ) ) {
930
940
valueWrap [ propName ] = value ;
931
941
}
932
-
933
942
return tv4 . validateResult ( valueWrap , wrap ) ;
934
943
935
944
} ;
@@ -985,8 +994,16 @@ angular.module('schemaForm').directive('sfArray', ['sfSelect', 'schemaForm', 'sf
985
994
// section. Unless there is just one.
986
995
var subForm = form . items [ 0 ] ;
987
996
if ( form . items . length > 1 ) {
988
- subForm = { type : 'section' , items : form . items } ;
997
+ subForm = {
998
+ type : 'section' ,
999
+ items : form . items . map ( function ( item ) {
1000
+ item . ngModelOptions = form . ngModelOptions ;
1001
+ item . readonly = form . readonly ;
1002
+ return item ;
1003
+ } )
1004
+ } ;
989
1005
}
1006
+
990
1007
}
991
1008
992
1009
// We ceate copies of the form on demand, caching them for
@@ -1259,28 +1276,17 @@ angular.module('schemaForm')
1259
1276
//make the form available to decorators
1260
1277
scope . schemaForm = { form : merged , schema : schema } ;
1261
1278
1262
- //clean all but pre existing html.
1263
- element . children ( ':not(.schema-form-ignore)' ) . remove ( ) ;
1264
-
1265
1279
//Create directives from the form definition
1266
- angular . forEach ( merged , function ( obj , i ) {
1267
- var n = document . createElement ( attrs . sfDecorator || snakeCase ( schemaFormDecorators . defaultDecorator , '-' ) ) ;
1268
- n . setAttribute ( 'form' , 'schemaForm.form[' + i + ']' ) ;
1269
- var slot ;
1270
- try {
1271
- slot = element [ 0 ] . querySelector ( '*[sf-insert-field="' + obj . key + '"]' ) ;
1272
- } catch ( err ) {
1273
- // field insertion not supported for complex keys
1274
- slot = null ;
1275
- }
1276
- if ( slot ) {
1277
- slot . innerHTML = "" ;
1278
- slot . appendChild ( n ) ;
1279
- } else {
1280
- frag . appendChild ( n ) ;
1281
- }
1280
+ angular . forEach ( merged , function ( obj , i ) {
1281
+ var n = document . createElement ( attrs . sfDecoratorName ||
1282
+ snakeCase ( schemaFormDecorators . defaultDecorator , '-' ) ) ;
1283
+ n . setAttribute ( 'form' , 'schemaForm.form[' + i + ']' ) ;
1284
+ frag . appendChild ( n ) ;
1282
1285
} ) ;
1283
1286
1287
+ //clean all but pre existing html.
1288
+ element . children ( ':not(.schema-form-ignore)' ) . remove ( ) ;
1289
+
1284
1290
element [ 0 ] . appendChild ( frag ) ;
1285
1291
1286
1292
//compile only children
@@ -1306,59 +1312,69 @@ angular.module('schemaForm').directive('schemaValidate', ['sfValidator', functio
1306
1312
return {
1307
1313
restrict : 'A' ,
1308
1314
scope : false ,
1315
+ // We want the link function to be *after* the input directives link function so we get access
1316
+ // the parsed value, ex. a number instead of a string
1317
+ priority : 1000 ,
1309
1318
require : 'ngModel' ,
1310
1319
link : function ( scope , element , attrs , ngModel ) {
1311
1320
//Since we have scope false this is the same scope
1312
1321
//as the decorator
1313
1322
scope . ngModel = ngModel ;
1314
1323
1315
1324
var error = null ;
1316
- var form = scope . $eval ( attrs . schemaValidate ) ;
1317
- // Validate against the schema.
1318
- var validate = function ( viewValue ) {
1325
+
1326
+ var getForm = function ( ) {
1319
1327
if ( ! form ) {
1320
1328
form = scope . $eval ( attrs . schemaValidate ) ;
1321
1329
}
1330
+ return form ;
1331
+ } ;
1332
+ var form = getForm ( ) ;
1322
1333
1323
- //Still might be undefined
1324
- if ( ! form ) {
1325
- return viewValue ;
1326
- }
1334
+ // Validate against the schema.
1327
1335
1328
- // Is required is handled by ng-required?
1329
- if ( angular . isDefined ( attrs . ngRequired ) && angular . isUndefined ( viewValue ) ) {
1330
- return undefined ;
1331
- }
1336
+ // Get in last of the parses so the parsed value has the correct type.
1337
+ if ( ngModel . $validators ) { // Angular 1.3
1338
+ ngModel . $validators . schema = function ( value ) {
1339
+ var result = sfValidator . validate ( getForm ( ) , value ) ;
1340
+ error = result . error ;
1341
+ return result . valid ;
1342
+ } ;
1343
+ } else {
1332
1344
1333
- // An empty field gives us the an empty string, which JSON schema
1334
- // happily accepts as a proper defined string, but an empty field
1335
- // for the user should trigger "required". So we set it to undefined.
1336
- if ( viewValue === '' ) {
1337
- viewValue = undefined ;
1338
- }
1345
+ // Angular 1.2
1346
+ ngModel . $parsers . push ( function ( viewValue ) {
1347
+ form = getForm ( ) ;
1348
+ //Still might be undefined
1349
+ if ( ! form ) {
1350
+ return viewValue ;
1351
+ }
1339
1352
1340
- var result = sfValidator . validate ( form , viewValue ) ;
1353
+ var result = sfValidator . validate ( form , viewValue ) ;
1341
1354
1342
- if ( result . valid ) {
1343
- // it is valid
1344
- ngModel . $setValidity ( 'schema' , true ) ;
1345
- return viewValue ;
1346
- } else {
1347
- // it is invalid, return undefined (no model update)
1348
- ngModel . $setValidity ( 'schema' , false ) ;
1349
- error = result . error ;
1350
- return undefined ;
1351
- }
1352
- } ;
1355
+ if ( result . valid ) {
1356
+ // it is valid
1357
+ ngModel . $setValidity ( 'schema' , true ) ;
1358
+ return viewValue ;
1359
+ } else {
1360
+ // it is invalid, return undefined (no model update)
1361
+ ngModel . $setValidity ( 'schema' , false ) ;
1362
+ error = result . error ;
1363
+ return undefined ;
1364
+ }
1365
+ } ) ;
1366
+ }
1353
1367
1354
- // Unshift onto parsers of the ng-model.
1355
- ngModel . $parsers . unshift ( validate ) ;
1356
1368
1357
1369
// Listen to an event so we can validate the input on request
1358
1370
scope . $on ( 'schemaFormValidate' , function ( ) {
1359
1371
1360
- if ( ngModel . $commitViewValue ) {
1361
- ngModel . $commitViewValue ( true ) ;
1372
+ if ( ngModel . $validate ) {
1373
+ ngModel . $validate ( ) ;
1374
+ if ( ngModel . $invalid ) { // The field must be made dirty so the error message is displayed
1375
+ ngModel . $dirty = true ;
1376
+ ngModel . $pristine = false ;
1377
+ }
1362
1378
} else {
1363
1379
ngModel . $setViewValue ( ngModel . $viewValue ) ;
1364
1380
}
@@ -1380,4 +1396,4 @@ angular.module('schemaForm').directive('schemaValidate', ['sfValidator', functio
1380
1396
1381
1397
}
1382
1398
} ;
1383
- } ] ) ;
1399
+ } ] ) ;
0 commit comments