diff --git a/README.md b/README.md index c83eee72d..42c9d304a 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,15 @@ function FormController($scope) { } ``` + +Contributing +------------ + +All contributions are welcome! We're trying to use [git flow](http://danielkummer.github.io/git-flow-cheatsheet/) +so please base any merge request on the **development** branch instead of **master**. + + + Form types ---------- Schema Form currently supports the following form field types: @@ -178,15 +187,40 @@ General options most field types can handle: title: "Street", //Title of field, taken from schema if available notitle: false, //Set to true to hide title description: "Street name", //A description, taken from schema if available - validationMessage: "Oh noes, please write a proper address" //A custom validation error message + validationMessage: "Oh noes, please write a proper address", //A custom validation error message + onChange: "valueChanged(form.key,modelValue)", //onChange event handler, expression or function + feedback: false //inline feedback icons } ``` -Validation Messages -------------------- +### onChange +The ```onChange``` option can be used with most fields and its value should be +either an angular expression, as a string, or a function. If its an expression +it will be evaluated in the parent scope of the ```sf-schema``` directive with +the special locals ```modelValue``` and ```form```. If its a function that will +be called with ```modelValue``` and ```form``` as first and second arguments. + +ex. +```javascript +$scope.form = [ + { + key: "name", + onChange: "updated(modelValue,form)" + }, + { + key: "password", + onChange: function(modelValue,form) { + console.log("Password is",modelValue); + } + } +]; +``` + +### Validation Messages + Per default all error messages but "Required" comes from the schema validator [tv4](https://github.com/geraintluff/tv4), this might or might not work for you. -If you supply a ´´´validationMessage´´´ proṕerty in the form definition, and if its value is a +If you supply a ```validationMessage``` property in the form definition, and if its value is a string that will be used instead on any validation error. If you need more fine grained control you can supply an object instead with keys matching the error @@ -204,6 +238,34 @@ Ex. } ``` +### Inline feedback icons +*input* and *textarea* based fields get inline status icons by default. A check +when everything is valid and a cross when there are validation errors. + +This can be turned off or configured to other icons. To turn off just +set ```feedback``` to false. If set to a string that string is evaluated by +a ```ngClass``` in the decorators scope. If not set att all the default value +is ```{ 'glyphicon': true, 'glyphicon-ok': hasSuccess(), 'glyphicon-remove': hasError() }``` + +ex. displaying an asterisk on required fields +```javascript + $sope.form = [ + { + key: "name", + feedback: "{ 'glyphicon': true, 'glyphicon-asterisk': form.requires && !hasSuccess && !hassError() ,'glyphicon-ok': hasSuccess(), 'glyphicon-remove': hasError() }" + } +``` + +Useful things in the decorators scope are + +| Name | Description| +|:---------------|:----------:| +| hasSuccess() | *true* if field is valid and not pristine | +| hasError() | *true* if field is invalid and not pristine | +| ngModel | The controller of the ngModel directive, ex. ngModel.$valid | +| form | The form definition for this field | + + Specific options per type ------------------------- @@ -346,8 +408,23 @@ function FormCtrl($scope) { ``` -Contributing ------------- +Post process function +--------------------- -All contributions are welcome! We're trying to use [git flow](http://danielkummer.github.io/git-flow-cheatsheet/) -so please base any merge request on the **development** branch instead of **master**. +If you like to use ```["*"]``` as a form, or aren't in control of the form definitions +but really need to change or add something you can register a *post process* +function with the ```schemaForm``` service provider. The post process function +gets one argument, the final form merged with the defaults from the schema just +before it's rendered, and should return a form. + +Ex. Reverse all forms +```javascript +angular.module('myModule').config(function(schemaFormProvider){ + + schemaForm.postProcess(function(form){ + form.reverse(); + return form; + }) + +}); +``` diff --git a/dist/bootstrap-decorator.min.js b/dist/bootstrap-decorator.min.js index 0f0d3ed34..5b7eb686e 100644 --- a/dist/bootstrap-decorator.min.js +++ b/dist/bootstrap-decorator.min.js @@ -1 +1 @@ -angular.module("schemaForm").run(["$templateCache",function(e){e.put("directives/decorators/bootstrap/actions-trcl.html",'
'),e.put("directives/decorators/bootstrap/actions.html",'
'),e.put("directives/decorators/bootstrap/checkbox.html",'
{{form.description}}
'),e.put("directives/decorators/bootstrap/checkboxes.html",'
{{form.description}}
'),e.put("directives/decorators/bootstrap/default.html",'
{{ (hasError() && errorMessage(schemaError())) || form.description}}
'),e.put("directives/decorators/bootstrap/fieldset-trcl.html",'
{{ form.title }}
'),e.put("directives/decorators/bootstrap/fieldset.html",'
{{ form.title }}
'),e.put("directives/decorators/bootstrap/radio-buttons.html",'
{{form.description}}
'),e.put("directives/decorators/bootstrap/radios.html",'
{{form.description}}
'),e.put("directives/decorators/bootstrap/readonly.html",'
{{form.description}}
'),e.put("directives/decorators/bootstrap/section.html",'
'),e.put("directives/decorators/bootstrap/select.html",'
{{ (hasError() && errorMessage(schemaError())) || form.description}}
'),e.put("directives/decorators/bootstrap/submit.html",'
'),e.put("directives/decorators/bootstrap/textarea.html",'
{{ (hasError() && errorMessage(schemaError())) || form.description}}
')}]),angular.module("schemaForm").config(["schemaFormDecoratorsProvider",function(e){var t="directives/decorators/bootstrap/";e.createDecorator("bootstrapDecorator",{textarea:t+"textarea.html",fieldset:t+"fieldset.html",section:t+"section.html",conditional:t+"section.html",actions:t+"actions.html",select:t+"select.html",checkbox:t+"checkbox.html",checkboxes:t+"checkboxes.html",number:t+"default.html",passord:t+"default.html",submit:t+"submit.html",button:t+"submit.html",radios:t+"radios.html",radiobuttons:t+"radio-buttons.html","default":t+"default.html"},[function(e){return e.readonly&&e.key&&"fieldset"!==e.type?t+"readonly.html":void 0}]),e.createDirectives({textarea:t+"textarea.html",select:t+"select.html",checkbox:t+"checkbox.html",checkboxes:t+"checkboxes.html",number:t+"default.html",submit:t+"submit.html",button:t+"submit.html",text:t+"default.html",date:t+"default.html",password:t+"default.html",input:t+"default.html",radios:t+"radios.html",radiobuttons:t+"radio-buttons.html"})}]).directive("sfFieldset",function(){return{transclude:!0,scope:!0,templateUrl:"directives/decorators/bootstrap/fieldset-trcl.html",link:function(e,t,r){e.title=e.$eval(r.title)}}}); \ No newline at end of file +angular.module("schemaForm").run(["$templateCache",function(e){e.put("directives/decorators/bootstrap/actions-trcl.html",'
'),e.put("directives/decorators/bootstrap/actions.html",'
'),e.put("directives/decorators/bootstrap/checkbox.html",'
{{form.description}}
'),e.put("directives/decorators/bootstrap/checkboxes.html",'
{{form.description}}
'),e.put("directives/decorators/bootstrap/default.html",'
{{ (hasError() && errorMessage(schemaError())) || form.description}}
'),e.put("directives/decorators/bootstrap/fieldset-trcl.html",'
{{ form.title }}
'),e.put("directives/decorators/bootstrap/fieldset.html",'
{{ form.title }}
'),e.put("directives/decorators/bootstrap/radio-buttons.html",'
{{form.description}}
'),e.put("directives/decorators/bootstrap/radios.html",'
{{form.description}}
'),e.put("directives/decorators/bootstrap/readonly.html",'
{{form.description}}
'),e.put("directives/decorators/bootstrap/section.html",'
'),e.put("directives/decorators/bootstrap/select.html",'
{{ (hasError() && errorMessage(schemaError())) || form.description}}
'),e.put("directives/decorators/bootstrap/submit.html",'
'),e.put("directives/decorators/bootstrap/textarea.html",'
{{ (hasError() && errorMessage(schemaError())) || form.description}}
')}]),angular.module("schemaForm").config(["schemaFormDecoratorsProvider",function(e){var t="directives/decorators/bootstrap/";e.createDecorator("bootstrapDecorator",{textarea:t+"textarea.html",fieldset:t+"fieldset.html",section:t+"section.html",conditional:t+"section.html",actions:t+"actions.html",select:t+"select.html",checkbox:t+"checkbox.html",checkboxes:t+"checkboxes.html",number:t+"default.html",password:t+"default.html",submit:t+"submit.html",button:t+"submit.html",radios:t+"radios.html",radiobuttons:t+"radio-buttons.html","default":t+"default.html"},[function(e){return e.readonly&&e.key&&"fieldset"!==e.type?t+"readonly.html":void 0}]),e.createDirectives({textarea:t+"textarea.html",select:t+"select.html",checkbox:t+"checkbox.html",checkboxes:t+"checkboxes.html",number:t+"default.html",submit:t+"submit.html",button:t+"submit.html",text:t+"default.html",date:t+"default.html",password:t+"default.html",input:t+"default.html",radios:t+"radios.html",radiobuttons:t+"radio-buttons.html"})}]).directive("sfFieldset",function(){return{transclude:!0,scope:!0,templateUrl:"directives/decorators/bootstrap/fieldset-trcl.html",link:function(e,t,s){e.title=e.$eval(s.title)}}}); \ No newline at end of file diff --git a/dist/schema-form.min.js b/dist/schema-form.min.js index 9c43a9d54..189d326ed 100644 --- a/dist/schema-form.min.js +++ b/dist/schema-form.min.js @@ -1 +1 @@ -angular.module("schemaForm",[]),angular.module("schemaForm").provider("schemaFormDecorators",["$compileProvider",function(e){var r="",t={},n=function(e,n){"sfDecorator"===e&&(e=r);for(var a=t[e],i=a.rules,o=0;o',link:function(e,t,n){var a={items:"c",titleMap:"c",schema:"c"},i={type:r},o=!0;angular.forEach(n,function(r,t){if("$"!==t[0]&&0!==t.indexOf("ng")&&"sfField"!==t){var u=function(r){angular.isDefined(r)&&r!==i[t]&&(i[t]=r,o&&i.type&&(i.key||angular.isUndefined(n.key))&&(e.form=i,o=!1))};"model"===t?e.$watch(r,function(r){r&&e.model!==r&&(e.model=r)}):"c"===a[t]?e.$watchCollection(r,u):n.$observe(t,u)}})}}})};this.createDecorator=function(e,n,i){t[e]={mappings:n||{},rules:i||[]},t[r]||(r=e),a(e)},this.createDirective=i,this.createDirectives=function(e){angular.forEach(e,function(e,r){i(r,e)})},this.directive=function(e){return e=e||r,t[e]},this.$get=function(){return{directive:function(e){return t[e]},defaultDecorator:r}},a("sfDecorator")}]),angular.module("schemaForm").provider("schemaForm",[function(){var e=function(e,r,t){var n=l[r.type];if(n)for(var a,i=0;i',link:function(e,t,n){var a={items:"c",titleMap:"c",schema:"c"},i={type:r},o=!0;angular.forEach(n,function(r,t){if("$"!==t[0]&&0!==t.indexOf("ng")&&"sfField"!==t){var u=function(r){angular.isDefined(r)&&r!==i[t]&&(i[t]=r,o&&i.type&&(i.key||angular.isUndefined(n.key))&&(e.form=i,o=!1))};"model"===t?e.$watch(r,function(r){r&&e.model!==r&&(e.model=r)}):"c"===a[t]?e.$watchCollection(r,u):n.$observe(t,u)}})}}})};this.createDecorator=function(e,n,i){t[e]={mappings:n||{},rules:i||[]},t[r]||(r=e),a(e)},this.createDirective=i,this.createDirectives=function(e){angular.forEach(e,function(e,r){i(r,e)})},this.directive=function(e){return e=e||r,t[e]},this.$get=function(){return{directive:function(e){return t[e]},defaultDecorator:r}},a("sfDecorator")}]),angular.module("schemaForm").provider("schemaForm",[function(){var e=function(e,r,t){var n=l[r.type];if(n)for(var a,i=0;i0){f.schema=c,f.form=l;var m=r.merge(c,l,s),p=document.createDocumentFragment();i.schemaForm={form:m,schema:c},angular.forEach(m,function(e,r){var a=document.createElement(u.sfDecorator||n(t.defaultDecorator,"-"));a.setAttribute("form","schemaForm.form["+r+"]"),p.appendChild(a)}),o.children(":not(.schema-form-ignore)").remove(),o[0].appendChild(p),e(o.children())(i),a(c,function(e,r){angular.isDefined(e["default"])&&i.$eval("model."+r+" = model."+r+" || defaltValue",{defaltValue:e["default"]})})}})}}}]),angular.module("schemaForm").directive("schemaValidate",function(){return{restrict:"A",scope:!1,require:"ngModel",link:function(e,r,t,n){e.ngModel=n;var a=null,i=e.$eval(t.schemaValidate);n.$parsers.unshift(function(r){if(i||(i=e.$eval(t.schemaValidate)),!i)return r;if(angular.isUndefined(r))return void 0;var o=r;"integer"===i.type?o=parseInt(o,10):"number"===i.type?o=parseFloat(o,10):"boolean"===i.type&&"string"==typeof r&&("true"===r?o=!0:"false"===r&&(o=!1));var u=tv4.validateResult(o,i);return u.valid?(n.$setValidity("schema",!0),a=null,r):(n.$setValidity("schema",!1),void(a=u.error))}),e.hasSuccess=function(){return n.$valid&&(!n.$pristine||!n.$isEmpty(n.$modelValue))},e.hasError=function(){return n.$invalid&&!n.$pristine},e.schemaError=function(){return a}}}}); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index e031ab09b..aaad68251 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -45,4 +45,9 @@ gulp.task('minify',function(){ }); -gulp.task('default',['minify','bootstrap']); \ No newline at end of file +gulp.task('default',['minify','bootstrap']); + + +gulp.task('watch', function() { + gulp.watch('./src/**/*', ['default']); +}); diff --git a/src/bootstrap-example.html b/src/bootstrap-example.html index 60ec39a32..7b014c5c5 100644 --- a/src/bootstrap-example.html +++ b/src/bootstrap-example.html @@ -30,7 +30,7 @@

The Generated Form

Model

-
{{pretty()}}
+
{{pretty()}}

Form

@@ -42,7 +42,6 @@

Schema

- @@ -53,6 +52,7 @@

Schema

+