diff --git a/README.md b/README.md index fdcc023e6..d0a9dfd43 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ Angular Schema Form Generate forms from JSON schemas using AngularJS! -Web Page / Twitter +The Web Site / The Twitter / The Movie -------- -[schemaform.io](http://schemaform.io) / [@SchemaFormIO](http://twitter.com/SchemaFormIO) +[schemaform.io](http://schemaform.io) / [@SchemaFormIO](http://twitter.com/SchemaFormIO) / [Movie](https://www.youtube.com/watch?v=duBFMipRq2o) Demo Time! ---------- diff --git a/src/directives/schema-form.js b/src/directives/schema-form.js index e77bfee4f..aef650607 100644 --- a/src/directives/schema-form.js +++ b/src/directives/schema-form.js @@ -5,8 +5,8 @@ FIXME: real documentation angular.module('schemaForm') .directive('sfSchema', -['$compile', 'schemaForm', 'schemaFormDecorators', 'sfSelect', 'sfPath', - function($compile, schemaForm, schemaFormDecorators, sfSelect, sfPath) { +['$compile', 'schemaForm', 'schemaFormDecorators', 'sfSelect', 'sfPath', 'sfRetainModel', + function($compile, schemaForm, schemaFormDecorators, sfSelect, sfPath, sfRetainModel) { var SNAKE_CASE_REGEXP = /[A-Z]/g; var snakeCase = function(name, separator) { @@ -119,14 +119,16 @@ angular.module('schemaForm') $compile(element.children())(childScope); //ok, now that that is done let's set any defaults - schemaForm.traverseSchema(schema, function(prop, path) { - if (angular.isDefined(prop['default'])) { - var val = sfSelect(path, scope.model); - if (angular.isUndefined(val)) { - sfSelect(path, scope.model, prop['default']); + if (!scope.options || scope.options.setSchemaDefaults !== false) { + schemaForm.traverseSchema(schema, function(prop, path) { + if (angular.isDefined(prop['default'])) { + var val = sfSelect(path, scope.model); + if (angular.isUndefined(val)) { + sfSelect(path, scope.model, prop['default']); + } } - } - }); + }); + } scope.$emit('sf-render-finished', element); }; @@ -159,6 +161,9 @@ angular.module('schemaForm') } }); + scope.$on('$destroy', function() { + sfRetainModel.setFlag(true); + }); } }; } diff --git a/src/directives/schema-validate.js b/src/directives/schema-validate.js index 2a9c0f761..50ee72a30 100644 --- a/src/directives/schema-validate.js +++ b/src/directives/schema-validate.js @@ -1,5 +1,5 @@ -angular.module('schemaForm').directive('schemaValidate', ['sfValidator', 'sfSelect', 'sfUnselect', - function(sfValidator, sfSelect, sfUnselect) { +angular.module('schemaForm').directive('schemaValidate', ['sfValidator', 'sfSelect', 'sfUnselect', '$parse', 'sfRetainModel', + function(sfValidator, sfSelect, sfUnselect, $parse, sfRetainModel) { return { restrict: 'A', @@ -127,54 +127,60 @@ angular.module('schemaForm').directive('schemaValidate', ['sfValidator', 'sfSele // Clean up the model when the corresponding form field is $destroy-ed. // Default behavior can be supplied as a globalOption, and behavior can be overridden in the form definition. scope.$on('$destroy', function() { - var form = getForm(); - - // Either set in form definition, or as part of globalOptions. - var destroyStrategy = - !form.hasOwnProperty('destroyStrategy') ? DEFAULT_DESTROY_STRATEGY : form.destroyStrategy; - var schemaType = getSchemaType(); - if (destroyStrategy && destroyStrategy !== 'retain' ) { - // Don't recognize the strategy, so give a warning. - console.warn('%s has defined unrecognized destroyStrategy: \'%s\'. Used default instead.', - attrs.name, destroyStrategy); - destroyStrategy = DEFAULT_DESTROY_STRATEGY; - } - else if (schemaType !== 'string' && destroyStrategy === '') { - // Only 'string' type fields can have an empty string value as a valid option. - console.warn('%s attempted to use empty string destroyStrategy on non-string form type. ' + - 'Used default instead.', attrs.name); - destroyStrategy = DEFAULT_DESTROY_STRATEGY; - } + var form = getForm(); + var conditionResult = $parse(form.condition); + var formModelNotRetained = !sfRetainModel.getFlag(); + + // If condition is defined and not satisfied and the sfSchema model should not be retained. + if (form.hasOwnProperty('condition') && !conditionResult(scope) && formModelNotRetained) { + + // Either set in form definition, or as part of globalOptions. + var destroyStrategy = + !form.hasOwnProperty('destroyStrategy') ? DEFAULT_DESTROY_STRATEGY : form.destroyStrategy; + var schemaType = getSchemaType(); + + if (destroyStrategy && destroyStrategy !== 'retain') { + // Don't recognize the strategy, so give a warning. + console.warn('%s has defined unrecognized destroyStrategy: \'%s\'. Used default instead.', + attrs.name, destroyStrategy); + destroyStrategy = DEFAULT_DESTROY_STRATEGY; + } + else if (schemaType !== 'string' && destroyStrategy === '') { + // Only 'string' type fields can have an empty string value as a valid option. + console.warn('%s attempted to use empty string destroyStrategy on non-string form type. ' + + 'Used default instead.', attrs.name); + destroyStrategy = DEFAULT_DESTROY_STRATEGY; + } - if (destroyStrategy === 'retain') { - return; // Valid option to avoid destroying data in the model. - } + if (destroyStrategy === 'retain') { + return; // Valid option to avoid destroying data in the model. + } - destroyUsingStrategy(destroyStrategy); + destroyUsingStrategy(destroyStrategy); - function destroyUsingStrategy(strategy) { - var strategyIsDefined = (strategy === null || strategy === '' || strategy === undefined); - if (!strategyIsDefined){ - strategy = DEFAULT_DESTROY_STRATEGY; + function destroyUsingStrategy(strategy) { + var strategyIsDefined = (strategy === null || strategy === '' || strategy === undefined); + if (!strategyIsDefined) { + strategy = DEFAULT_DESTROY_STRATEGY; + } + sfUnselect(scope.form.key, scope.model, strategy); } - sfUnselect(scope.form.key, scope.model, strategy); - } - function getSchemaType() { - var sType; - if (form.schema) { - sType = form.schema.type; + function getSchemaType() { + var sType; + if (form.schema) { + sType = form.schema.type; + } + else { + sType = null; + } + return sType; } - else { - sType = null; - } - return sType; } }); - scope.schemaError = function() { return error; }; diff --git a/src/services/retainModel.js b/src/services/retainModel.js new file mode 100644 index 000000000..e500d3d3d --- /dev/null +++ b/src/services/retainModel.js @@ -0,0 +1,37 @@ +angular.module('schemaForm').factory('sfRetainModel', function() { + + var data = {retainModelFlag: false }; + + return { + /** + * @description + * Utility service to indicate if the sfSchema model should be retained. + * Set to true to prevent an operation that would have destroyed the model + * from doing so (such as wrapping the form in an ng-if). + * + * ex. + * var foo = sfRetainModel.getFlag(); + * + * @returns {boolean} returns the current value of the retainModelFlag. + */ + getFlag: function () { + return data.retainModelFlag; + }, + + /** + * @description + * Set the value of the retainModelFlag. + * True prevents cleaning the data in the model, while false follows the configured destroyStrategy. + * + * ex. + * var bar = sfRetainModel.setFlag(true); + * + * @param {boolean} value The boolean value to set as the retainModelFlag + * @returns {boolean} returns the value of the retainModelFlag after toggling. + */ + setFlag: function(value) { + data.retainModelFlag = value; + return data.retainModelFlag; + } + } +}); \ No newline at end of file diff --git a/test/directives/schema-form-test.js b/test/directives/schema-form-test.js index ff764ae3b..fd12b9ba4 100644 --- a/test/directives/schema-form-test.js +++ b/test/directives/schema-form-test.js @@ -494,6 +494,48 @@ describe('directive',function(){ }); }); + it('should honor defaults in schema unless told not to',function(){ + + inject(function($compile,$rootScope){ + var scope = $rootScope.$new(); + scope.person = { + name: 'Foobar' + }; + + scope.schema = { + "type": "object", + "properties": { + "name": { + "type": "string", + "default": "Bar" + }, + "nick": { + "type": "string", + "default": "Zeb" + }, + "alias": { + "type": "string" + }, + } + }; + + scope.form = ["*"]; + + scope.options = {setSchemaDefaults: false}; + + var tmpl = angular.element('
'); + + $compile(tmpl)(scope); + $rootScope.$apply(); + + scope.person.name.should.be.equal('Foobar'); + expect(scope.person.nick).to.be.undefined; + expect(scope.person.alias).to.be.undefined; + + }); + }); + + it('should handle schema form default in deep structure',function(){ inject(function($compile,$rootScope){ @@ -1950,7 +1992,7 @@ describe('directive',function(){ } }; scope.form = [field.form]; - + var tmpl = angular.element(''); $compile(tmpl)(scope); $rootScope.$apply(); @@ -1978,7 +2020,7 @@ describe('directive',function(){ } }; scope.form = [field.form]; - + var tmpl = angular.element(''); $compile(tmpl)(scope); $rootScope.$apply(); @@ -1993,4 +2035,4 @@ describe('directive',function(){ }); }); }); -}); \ No newline at end of file +});