@@ -44,7 +44,7 @@ angular.module('schemaForm').provider('sfPath',
4444 this . parse = ObjectPath . parse ;
4545 this . stringify = ObjectPath . stringify ;
4646 this . normalize = ObjectPath . normalize ;
47- this . $get = function ( ) {
47+ this . $get = function ( ) {
4848 return ObjectPath ;
4949 } ;
5050} ] ) ;
@@ -55,7 +55,7 @@ angular.module('schemaForm').provider('sfPath',
5555 * @kind function
5656 *
5757 */
58- angular . module ( 'schemaForm' ) . factory ( 'sfSelect' , [ 'sfPath' , function ( sfPath ) {
58+ angular . module ( 'schemaForm' ) . factory ( 'sfSelect' , [ 'sfPath' , function ( sfPath ) {
5959 var numRe = / ^ \d + $ / ;
6060
6161 /**
@@ -271,7 +271,7 @@ angular.module('schemaForm').provider('schemaFormDecorators',
271271 return scope . form . validationMessage [ schemaError . code ] ||
272272 scope . form . validationMessage [ 'default' ] ;
273273 } else {
274- return scope . form . validationMessage . required ||
274+ return scope . form . validationMessage . number ||
275275 scope . form . validationMessage [ 'default' ] ||
276276 scope . form . validationMessage ;
277277 }
@@ -282,8 +282,8 @@ angular.module('schemaForm').provider('schemaFormDecorators',
282282 return schemaError . message ; //use tv4.js validation message
283283 }
284284
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 ' ;
287287
288288 } ;
289289 }
@@ -586,7 +586,6 @@ angular.module('schemaForm').provider('schemaForm',
586586 } ;
587587
588588 var fieldset = function ( name , schema , options ) {
589-
590589 if ( schema . type === 'object' ) {
591590 var f = stdFormObj ( name , schema , options ) ;
592591 f . type = 'fieldset' ;
@@ -640,7 +639,8 @@ angular.module('schemaForm').provider('schemaForm',
640639 path : arrPath ,
641640 required : required || false ,
642641 lookup : options . lookup ,
643- ignore : options . ignore
642+ ignore : options . ignore ,
643+ global : options . global
644644 } ) ] ;
645645
646646 return f ;
@@ -720,23 +720,27 @@ angular.module('schemaForm').provider('schemaForm',
720720
721721 var service = { } ;
722722
723- service . merge = function ( schema , form , ignore , options ) {
723+ service . merge = function ( schema , form , ignore , options , readonly ) {
724724 form = form || [ '*' ] ;
725725 options = options || { } ;
726726
727+ // Get readonly from root object
728+ readonly = readonly || schema . readonly || schema . readOnly ;
729+
727730 var stdForm = service . defaults ( schema , ignore , options ) ;
731+
728732 //simple case, we have a "*", just put the stdForm there
729733 var idx = form . indexOf ( '*' ) ;
730734 if ( idx !== - 1 ) {
731735 form = form . slice ( 0 , idx )
732736 . concat ( stdForm . form )
733737 . concat ( form . slice ( idx + 1 ) ) ;
734- return form ;
735738 }
736739
737740 //ok let's merge!
738741 //We look at the supplied form and extend it with schema standards
739742 var lookup = stdForm . lookup ;
743+
740744 return postProcessFn ( form . map ( function ( obj ) {
741745
742746 //handle the shortcut with just a name
@@ -767,26 +771,32 @@ angular.module('schemaForm').provider('schemaForm',
767771 } ) ;
768772 }
769773
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+
770788 //if it's a type with items, merge 'em!
771789 if ( obj . items ) {
772- obj . items = service . merge ( schema , obj . items , ignore ) ;
790+ obj . items = service . merge ( schema , obj . items , ignore , options , obj . readonly ) ;
773791 }
774792
775793 //if its has tabs, merge them also!
776794 if ( obj . tabs ) {
777795 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 ) ;
779797 } ) ;
780798 }
781799
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-
790800 // Special case: checkbox
791801 // Since have to ternary state we need a default
792802 if ( obj . type === 'checkbox' && angular . isUndefined ( obj . schema [ 'default' ] ) ) {
@@ -893,26 +903,26 @@ angular.module('schemaForm').factory('sfValidator', [function() {
893903 * @return a tv4js result object.
894904 */
895905 validator . validate = function ( form , value ) {
896-
906+ if ( ! form ) {
907+ return { valid : true } ;
908+ }
897909 var schema = form . schema ;
898910
899911 if ( ! schema ) {
900- //Nothings to Validate
901- return value ;
912+ return { valid : true } ;
902913 }
903914
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 ;
916926 }
917927
918928 // Version 4 of JSON Schema has the required property not on the
@@ -929,7 +939,6 @@ angular.module('schemaForm').factory('sfValidator', [function() {
929939 if ( angular . isDefined ( value ) ) {
930940 valueWrap [ propName ] = value ;
931941 }
932-
933942 return tv4 . validateResult ( valueWrap , wrap ) ;
934943
935944 } ;
@@ -985,8 +994,16 @@ angular.module('schemaForm').directive('sfArray', ['sfSelect', 'schemaForm', 'sf
985994 // section. Unless there is just one.
986995 var subForm = form . items [ 0 ] ;
987996 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+ } ;
9891005 }
1006+
9901007 }
9911008
9921009 // We ceate copies of the form on demand, caching them for
@@ -1259,28 +1276,17 @@ angular.module('schemaForm')
12591276 //make the form available to decorators
12601277 scope . schemaForm = { form : merged , schema : schema } ;
12611278
1262- //clean all but pre existing html.
1263- element . children ( ':not(.schema-form-ignore)' ) . remove ( ) ;
1264-
12651279 //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 ) ;
12821285 } ) ;
12831286
1287+ //clean all but pre existing html.
1288+ element . children ( ':not(.schema-form-ignore)' ) . remove ( ) ;
1289+
12841290 element [ 0 ] . appendChild ( frag ) ;
12851291
12861292 //compile only children
@@ -1306,59 +1312,69 @@ angular.module('schemaForm').directive('schemaValidate', ['sfValidator', functio
13061312 return {
13071313 restrict : 'A' ,
13081314 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 ,
13091318 require : 'ngModel' ,
13101319 link : function ( scope , element , attrs , ngModel ) {
13111320 //Since we have scope false this is the same scope
13121321 //as the decorator
13131322 scope . ngModel = ngModel ;
13141323
13151324 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 ( ) {
13191327 if ( ! form ) {
13201328 form = scope . $eval ( attrs . schemaValidate ) ;
13211329 }
1330+ return form ;
1331+ } ;
1332+ var form = getForm ( ) ;
13221333
1323- //Still might be undefined
1324- if ( ! form ) {
1325- return viewValue ;
1326- }
1334+ // Validate against the schema.
13271335
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 {
13321344
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+ }
13391352
1340- var result = sfValidator . validate ( form , viewValue ) ;
1353+ var result = sfValidator . validate ( form , viewValue ) ;
13411354
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+ }
13531367
1354- // Unshift onto parsers of the ng-model.
1355- ngModel . $parsers . unshift ( validate ) ;
13561368
13571369 // Listen to an event so we can validate the input on request
13581370 scope . $on ( 'schemaFormValidate' , function ( ) {
13591371
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+ }
13621378 } else {
13631379 ngModel . $setViewValue ( ngModel . $viewValue ) ;
13641380 }
@@ -1380,4 +1396,4 @@ angular.module('schemaForm').directive('schemaValidate', ['sfValidator', functio
13801396
13811397 }
13821398 } ;
1383- } ] ) ;
1399+ } ] ) ;
0 commit comments