@@ -299,6 +299,13 @@ angular.module('schemaForm').provider('sfBuilder', ['sfPathProvider', function(s
299299 }
300300 } ;
301301 this . builders = builders ;
302+ var stdBuilders = [
303+ builders . sfField ,
304+ builders . ngModel ,
305+ builders . ngModelOptions ,
306+ builders . condition
307+ ] ;
308+ this . stdBuilders = stdBuilders ;
302309
303310 this . $get = [ '$templateCache' , 'schemaFormDecorators' , 'sfPath' , function ( $templateCache , schemaFormDecorators , sfPath ) {
304311
@@ -326,7 +333,7 @@ angular.module('schemaForm').provider('sfBuilder', ['sfPathProvider', function(s
326333
327334 // Sanity check.
328335 if ( ! f . type ) {
329- return ;
336+ return frag ;
330337 }
331338
332339 var field = decorator [ f . type ] || decorator [ 'default' ] ;
@@ -408,6 +415,7 @@ angular.module('schemaForm').provider('sfBuilder', ['sfPathProvider', function(s
408415
409416 } ,
410417 builder : builders ,
418+ stdBuilders : stdBuilders ,
411419 internalBuild : build
412420 } ;
413421 } ] ;
@@ -812,17 +820,22 @@ angular.module('schemaForm').provider('schemaFormDecorators',
812820
813821
814822 /**
815- * Create a decorator directive and its sibling "manual" use decorators.
816- * The directive can be used to create form fields or other form entities.
817- * It can be used in conjunction with <schema-form> directive in which case the decorator is
818- * given it's configuration via a the "form" attribute.
823+ * Define a decorator. A decorator is a set of form types with templates and builder functions
824+ * that help set up the form.
819825 *
820- * ex. Basic usage
821- * <sf-decorator form="myform"></sf-decorator>
822- **
823826 * @param {string } name directive name (CamelCased)
824827 * @param {Object } fields, an object that maps "type" => `{ template, builder, replace}`.
825828 attributes `builder` and `replace` are optional, and replace defaults to true.
829+
830+ `template` should be the key of the template to load and it should be pre-loaded
831+ in `$templateCache`.
832+
833+ `builder` can be a function or an array of functions. They will be called in
834+ the order they are supplied.
835+
836+ `replace` (DEPRECATED) is for backwards compatability. If false the builder
837+ will use the "old" way of building that form field using a <sf-decorator>
838+ directive.
826839 */
827840 this . defineDecorator = function ( name , fields ) {
828841 decorators [ name ] = { '__name' : name } ; // TODO: this feels like a hack, come up with a better way.
@@ -840,6 +853,7 @@ angular.module('schemaForm').provider('schemaFormDecorators',
840853 } ;
841854
842855 /**
856+ * DEPRECATED
843857 * Creates a directive of a decorator
844858 * Usable when you want to use the decorators without using <schema-form> directive.
845859 * Specifically when you need to reuse styling.
@@ -854,6 +868,7 @@ angular.module('schemaForm').provider('schemaFormDecorators',
854868 this . createDirective = createManualDirective ;
855869
856870 /**
871+ * DEPRECATED
857872 * Same as createDirective, but takes an object where key is 'type' and value is 'templateUrl'
858873 * Useful for batching.
859874 * @param {Object } templates
@@ -876,6 +891,7 @@ angular.module('schemaForm').provider('schemaFormDecorators',
876891
877892
878893 /**
894+ * DEPRECATED use defineAddOn() instead.
879895 * Adds a mapping to an existing decorator.
880896 * @param {String } name Decorator name
881897 * @param {String } type Form type for the mapping
@@ -893,6 +909,25 @@ angular.module('schemaForm').provider('schemaFormDecorators',
893909 }
894910 } ;
895911
912+ /**
913+ * Adds an add-on to an existing decorator.
914+ * @param {String } name Decorator name
915+ * @param {String } type Form type for the mapping
916+ * @param {String } url The template url
917+ * @param {Function|Array } builder (optional) builder function(s),
918+ */
919+ this . defineAddOn = function ( name , type , url , builder ) {
920+ if ( decorators [ name ] ) {
921+ decorators [ name ] [ type ] = {
922+ template : url ,
923+ builder : builder ,
924+ replace : true
925+ } ;
926+ }
927+ } ;
928+
929+
930+
896931 //Service is just a getter for directive templates and rules
897932 this . $get = function ( ) {
898933 return {
@@ -1817,11 +1852,27 @@ angular.module('schemaForm').directive('sfArray', ['sfSelect', 'schemaForm', 'sf
18171852 scope . $on ( 'schemaFormValidate' , scope . validateArray ) ;
18181853
18191854 scope . hasSuccess = function ( ) {
1820- return ngModel . $valid && ! ngModel . $pristine ;
1855+ if ( scope . options && scope . options . pristine &&
1856+ scope . options . pristine . success === false ) {
1857+ return ngModel . $valid &&
1858+ ! ngModel . $pristine && ! ngModel . $isEmpty ( ngModel . $modelValue ) ;
1859+ } else {
1860+ return ngModel . $valid &&
1861+ ( ! ngModel . $pristine || ! ngModel . $isEmpty ( ngModel . $modelValue ) ) ;
1862+ }
18211863 } ;
18221864
18231865 scope . hasError = function ( ) {
1824- return ngModel . $invalid ;
1866+ if ( ! scope . options || ! scope . options . pristine || scope . options . pristine . errors !== false ) {
1867+ // Show errors in pristine forms. The default.
1868+ // Note that "validateOnRender" option defaults to *not* validate initial form.
1869+ // so as a default there won't be any error anyway, but if the model is modified
1870+ // from the outside the error will show even if the field is pristine.
1871+ return ngModel . $invalid ;
1872+ } else {
1873+ // Don't show errors in pristine forms.
1874+ return ngModel . $invalid && ! ngModel . $pristine ;
1875+ }
18251876 } ;
18261877
18271878 scope . schemaError = function ( ) {
@@ -1975,20 +2026,35 @@ angular.module('schemaForm').directive('sfField',
19752026 return ( expression && $interpolate ( expression ) ( locals ) ) ;
19762027 } ;
19772028
1978- //This works since we ot the ngModel from the array or the schema-validate directive.
2029+ //This works since we get the ngModel from the array or the schema-validate directive.
19792030 scope . hasSuccess = function ( ) {
19802031 if ( ! scope . ngModel ) {
19812032 return false ;
19822033 }
1983- return scope . ngModel . $valid &&
2034+ if ( scope . options && scope . options . pristine &&
2035+ scope . options . pristine . success === false ) {
2036+ return scope . ngModel . $valid &&
2037+ ! scope . ngModel . $pristine && ! scope . ngModel . $isEmpty ( scope . ngModel . $modelValue ) ;
2038+ } else {
2039+ return scope . ngModel . $valid &&
19842040 ( ! scope . ngModel . $pristine || ! scope . ngModel . $isEmpty ( scope . ngModel . $modelValue ) ) ;
2041+ }
19852042 } ;
19862043
19872044 scope . hasError = function ( ) {
19882045 if ( ! scope . ngModel ) {
19892046 return false ;
19902047 }
1991- return scope . ngModel . $invalid && ! scope . ngModel . $pristine ;
2048+ if ( ! scope . options || ! scope . options . pristine || scope . options . pristine . errors !== false ) {
2049+ // Show errors in pristine forms. The default.
2050+ // Note that "validateOnRender" option defaults to *not* validate initial form.
2051+ // so as a default there won't be any error anyway, but if the model is modified
2052+ // from the outside the error will show even if the field is pristine.
2053+ return scope . ngModel . $invalid ;
2054+ } else {
2055+ // Don't show errors in pristine forms.
2056+ return scope . ngModel . $invalid && ! scope . ngModel . $pristine ;
2057+ }
19922058 } ;
19932059
19942060 /**
@@ -2046,7 +2112,8 @@ angular.module('schemaForm').directive('sfField',
20462112 scope . $broadcast ( 'schemaFormValidate' ) ;
20472113 }
20482114 }
2049- } ) ;
2115+ }
2116+ ) ;
20502117
20512118 // Clean up the model when the corresponding form field is $destroy-ed.
20522119 // Default behavior can be supplied as a globalOption, and behavior can be overridden
@@ -2113,60 +2180,80 @@ angular.module('schemaForm').directive('sfMessage',
21132180 scope . $watch ( attrs . sfMessage , function ( msg ) {
21142181 if ( msg ) {
21152182 message = $sanitize ( msg ) ;
2116- if ( scope . ngModel ) {
2117- update ( scope . ngModel . $valid ) ;
2118- } else {
2119- update ( ) ;
2120- }
2183+ update ( ! ! scope . ngModel ) ;
21212184 }
21222185 } ) ;
21232186 }
21242187
2125- var update = function ( valid ) {
2126- if ( valid && ! scope . hasError ( ) ) {
2127- element . html ( message ) ;
2128- } else {
2129- var errors = [ ] ;
2130- angular . forEach ( ( ( scope . ngModel && scope . ngModel . $error ) || { } ) , function ( status , code ) {
2131- if ( status ) {
2132- // if true then there is an error
2133- // Angular 1.3 removes properties, so we will always just have errors.
2134- // Angular 1.2 sets them to false.
2135- errors . push ( code ) ;
2136- }
2137- } ) ;
2188+ var currentMessage ;
2189+ // Only call html() if needed.
2190+ var setMessage = function ( msg ) {
2191+ if ( msg !== currentMessage ) {
2192+ element . html ( msg ) ;
2193+ currentMessage = msg ;
2194+ }
2195+ } ;
21382196
2139- // In Angular 1.3 we use one $validator to stop the model value from getting updated.
2140- // this means that we always end up with a 'schemaForm' error.
2141- errors = errors . filter ( function ( e ) { return e !== 'schemaForm' ; } ) ;
2142-
2143- // We only show one error.
2144- // TODO: Make that optional
2145- var error = errors [ 0 ] ;
2146-
2147- if ( error ) {
2148- element . html ( sfErrorMessage . interpolate (
2149- error ,
2150- scope . ngModel . $modelValue ,
2151- scope . ngModel . $viewValue ,
2152- scope . form ,
2153- scope . options && scope . options . validationMessage
2154- ) ) ;
2197+ var update = function ( checkForErrors ) {
2198+ if ( checkForErrors ) {
2199+ if ( ! scope . hasError ( ) ) {
2200+ setMessage ( message ) ;
21552201 } else {
2156- element . html ( message ) ;
2202+ var errors = [ ] ;
2203+ angular . forEach ( scope . ngModel && scope . ngModel . $error , function ( status , code ) {
2204+ if ( status ) {
2205+ // if true then there is an error
2206+ // Angular 1.3 removes properties, so we will always just have errors.
2207+ // Angular 1.2 sets them to false.
2208+ errors . push ( code ) ;
2209+ }
2210+ } ) ;
2211+
2212+ // In Angular 1.3 we use one $validator to stop the model value from getting updated.
2213+ // this means that we always end up with a 'schemaForm' error.
2214+ errors = errors . filter ( function ( e ) { return e !== 'schemaForm' ; } ) ;
2215+
2216+ // We only show one error.
2217+ // TODO: Make that optional
2218+ var error = errors [ 0 ] ;
2219+
2220+ if ( error ) {
2221+ setMessage ( sfErrorMessage . interpolate (
2222+ error ,
2223+ scope . ngModel . $modelValue ,
2224+ scope . ngModel . $viewValue ,
2225+ scope . form ,
2226+ scope . options && scope . options . validationMessage
2227+ ) ) ;
2228+ } else {
2229+ setMessage ( message ) ;
2230+ }
21572231 }
2232+ } else {
2233+ setMessage ( message ) ;
21582234 }
21592235 } ;
21602236
21612237 // Update once.
21622238 update ( ) ;
21632239
2164- scope . $watchCollection ( 'ngModel.$error' , function ( ) {
2165- if ( scope . ngModel ) {
2166- update ( scope . ngModel . $valid ) ;
2240+ var once = scope . $watch ( 'ngModel' , function ( ngModel ) {
2241+ if ( ngModel ) {
2242+ // We also listen to changes of the model via parsers and formatters.
2243+ // This is since both the error message can change and given a pristine
2244+ // option to not show errors the ngModel.$error might not have changed
2245+ // but we're not pristine any more so we should change!
2246+ ngModel . $parsers . push ( function ( val ) { update ( true ) ; return val ; } ) ;
2247+ ngModel . $formatters . push ( function ( val ) { update ( true ) ; return val ; } ) ;
2248+ once ( ) ;
21672249 }
21682250 } ) ;
21692251
2252+ // We watch for changes in $error
2253+ scope . $watchCollection ( 'ngModel.$error' , function ( ) {
2254+ update ( ! ! scope . ngModel ) ;
2255+ } ) ;
2256+
21702257 }
21712258 } ;
21722259} ] ) ;
@@ -2627,11 +2714,13 @@ angular.module('schemaForm').directive('schemaValidate', ['sfValidator', '$parse
26272714 sfSelect ( path , scope . model , ngModel . $modelValue ) ;
26282715 } ) ;
26292716 } ) ;
2630- }
2717+ } ;
2718+
26312719
26322720 // Validate against the schema.
26332721
26342722 var validate = function ( viewValue ) {
2723+ //console.log('validate called', viewValue)
26352724 //Still might be undefined
26362725 if ( ! form ) {
26372726 return viewValue ;
@@ -2643,7 +2732,7 @@ angular.module('schemaForm').directive('schemaValidate', ['sfValidator', '$parse
26432732 }
26442733
26452734 var result = sfValidator . validate ( form , viewValue ) ;
2646-
2735+ //console.log('result is', result)
26472736 // Since we might have different tv4 errors we must clear all
26482737 // errors that start with tv4-
26492738 Object . keys ( ngModel . $error )
@@ -2698,6 +2787,7 @@ angular.module('schemaForm').directive('schemaValidate', ['sfValidator', '$parse
26982787 // updating if we've found an error.
26992788 if ( ngModel . $validators ) {
27002789 ngModel . $validators . schemaForm = function ( ) {
2790+ //console.log('validators called.')
27012791 // Any error and we're out of here!
27022792 return ! Object . keys ( ngModel . $error ) . some ( function ( e ) { return e !== 'schemaForm' ; } ) ;
27032793 } ;
@@ -2741,6 +2831,20 @@ angular.module('schemaForm').directive('schemaValidate', ['sfValidator', '$parse
27412831 }
27422832 } ;
27432833
2834+ var first = true ;
2835+ ngModel . $formatters . push ( function ( val ) {
2836+
2837+ // When a form first loads this will be called for each field.
2838+ // we usually don't want that.
2839+ if ( ngModel . $pristine && first &&
2840+ ( ! scope . options || scope . options . validateOnRender !== true ) ) {
2841+ first = false ;
2842+ return val ;
2843+ }
2844+ validate ( ngModel . $modelValue ) ;
2845+ return val ;
2846+ } ) ;
2847+
27442848 // Listen to an event so we can validate the input on request
27452849 scope . $on ( 'schemaFormValidate' , scope . validateField ) ;
27462850
0 commit comments