@@ -168,8 +168,10 @@ angular.module('schemaForm').provider('schemaFormDecorators',
168168
169169 var createDirective = function ( name ) {
170170 $compileProvider . directive ( name ,
171- [ '$parse' , '$compile' , '$http' , '$templateCache' , '$interpolate' , '$q' , 'sfErrorMessage' , 'sfPath' ,
172- function ( $parse , $compile , $http , $templateCache , $interpolate , $q , sfErrorMessage , sfPath ) {
171+ [ '$parse' , '$compile' , '$http' , '$templateCache' , '$interpolate' , '$q' , 'sfErrorMessage' ,
172+ 'sfPath' , 'sfSelect' ,
173+ function ( $parse , $compile , $http , $templateCache , $interpolate , $q , sfErrorMessage ,
174+ sfPath , sfSelect ) {
173175
174176 return {
175177 restrict : 'AE' ,
@@ -398,7 +400,48 @@ angular.module('schemaForm').provider('schemaFormDecorators',
398400 scope . $broadcast ( 'schemaFormValidate' ) ;
399401 }
400402 }
401- } )
403+ } ) ;
404+
405+ // Clean up the model when the corresponding form field is $destroy-ed.
406+ // Default behavior can be supplied as a globalOption, and behavior can be overridden in the form definition.
407+ scope . $on ( '$destroy' , function ( ) {
408+ // If the entire schema form is destroyed we don't touch the model
409+ if ( ! scope . externalDestructionInProgress ) {
410+ var destroyStrategy = form . destroyStrategy ||
411+ ( scope . options && scope . options . destroyStrategy ) || 'remove' ;
412+ // No key no model, and we might have strategy 'retain'
413+ if ( form . key && destroyStrategy !== 'retain' ) {
414+
415+ // Get the object that has the property we wan't to clear.
416+ var obj = scope . model ;
417+ if ( form . key . length > 1 ) {
418+ obj = sfSelect ( form . key . slice ( 0 , form . key . length - 1 ) , obj ) ;
419+ }
420+
421+ // We can get undefined here if the form hasn't been filled out entirely
422+ if ( obj === undefined ) {
423+ return ;
424+ }
425+
426+ // Type can also be a list in JSON Schema
427+ var type = ( form . schema && form . schema . type ) || '' ;
428+
429+ // Empty means '',{} and [] for appropriate types and undefined for the rest
430+ //console.log('destroy', destroyStrategy, form.key, type, obj);
431+ if ( destroyStrategy === 'empty' && type . indexOf ( 'string' ) !== - 1 ) {
432+ obj [ form . key . slice ( - 1 ) ] = '' ;
433+ } else if ( destroyStrategy === 'empty' && type . indexOf ( 'object' ) !== - 1 ) {
434+ obj [ form . key . slice ( - 1 ) ] = { } ;
435+ } else if ( destroyStrategy === 'empty' && type . indexOf ( 'array' ) !== - 1 ) {
436+ obj [ form . key . slice ( - 1 ) ] = [ ] ;
437+ } else if ( destroyStrategy === 'null' ) {
438+ obj [ form . key . slice ( - 1 ) ] = null ;
439+ } else {
440+ delete obj [ form . key . slice ( - 1 ) ] ;
441+ }
442+ }
443+ }
444+ } ) ;
402445 }
403446
404447 once ( ) ;
@@ -1633,7 +1676,10 @@ angular.module('schemaForm')
16331676 // they have been removed from the DOM
16341677 // https://github.com/Textalk/angular-schema-form/issues/200
16351678 if ( childScope ) {
1679+ // Destroy strategy should not be acted upon
1680+ scope . externalDestructionInProgress = true ;
16361681 childScope . $destroy ( ) ;
1682+ scope . externalDestructionInProgress = false ;
16371683 }
16381684 childScope = scope . $new ( ) ;
16391685
@@ -1680,14 +1726,16 @@ angular.module('schemaForm')
16801726 $compile ( element . children ( ) ) ( childScope ) ;
16811727
16821728 //ok, now that that is done let's set any defaults
1683- schemaForm . traverseSchema ( schema , function ( prop , path ) {
1684- if ( angular . isDefined ( prop [ 'default' ] ) ) {
1685- var val = sfSelect ( path , scope . model ) ;
1686- if ( angular . isUndefined ( val ) ) {
1687- sfSelect ( path , scope . model , prop [ 'default' ] ) ;
1729+ if ( ! scope . options || scope . options . setSchemaDefaults !== false ) {
1730+ schemaForm . traverseSchema ( schema , function ( prop , path ) {
1731+ if ( angular . isDefined ( prop [ 'default' ] ) ) {
1732+ var val = sfSelect ( path , scope . model ) ;
1733+ if ( angular . isUndefined ( val ) ) {
1734+ sfSelect ( path , scope . model , prop [ 'default' ] ) ;
1735+ }
16881736 }
1689- }
1690- } ) ;
1737+ } ) ;
1738+ }
16911739
16921740 scope . $emit ( 'sf-render-finished' , element ) ;
16931741 } ;
@@ -1720,121 +1768,131 @@ angular.module('schemaForm')
17201768 }
17211769 } ) ;
17221770
1771+ scope . $on ( '$destroy' , function ( ) {
1772+ // Each field listens to the $destroy event so that it can remove any value
1773+ // from the model if that field is removed from the form. This is the default
1774+ // destroy strategy. But if the entire form (or at least the part we're on)
1775+ // gets removed, like when routing away to another page, then we definetly want to
1776+ // keep the model intact. So therefore we set a flag to tell the others it's time to just
1777+ // let it be.
1778+ scope . externalDestructionInProgress = true ;
1779+ } ) ;
17231780 }
17241781 } ;
17251782 }
17261783] ) ;
17271784
1728- angular . module ( 'schemaForm' ) . directive ( 'schemaValidate' , [ 'sfValidator' , 'sfSelect' , function ( sfValidator , sfSelect ) {
1729- return {
1730- restrict : 'A' ,
1731- scope : false ,
1732- // We want the link function to be *after* the input directives link function so we get access
1733- // the parsed value, ex. a number instead of a string
1734- priority : 500 ,
1735- require : 'ngModel' ,
1736- link : function ( scope , element , attrs , ngModel ) {
1785+ angular . module ( 'schemaForm' ) . directive ( 'schemaValidate' , [ 'sfValidator' , '$parse' ,
1786+ function ( sfValidator , $parse ) {
17371787
1788+ return {
1789+ restrict : 'A' ,
1790+ scope : false ,
1791+ // We want the link function to be *after* the input directives link function so we get access
1792+ // the parsed value, ex. a number instead of a string
1793+ priority : 500 ,
1794+ require : 'ngModel' ,
1795+ link : function ( scope , element , attrs , ngModel ) {
17381796
1739- // We need the ngModelController on several places,
1740- // most notably for errors.
1741- // So we emit it up to the decorator directive so it can put it on scope.
1742- scope . $emit ( 'schemaFormPropagateNgModelController' , ngModel ) ;
1797+ // We need the ngModelController on several places,
1798+ // most notably for errors.
1799+ // So we emit it up to the decorator directive so it can put it on scope.
1800+ scope . $emit ( 'schemaFormPropagateNgModelController' , ngModel ) ;
17431801
1744- var error = null ;
1802+ var error = null ;
17451803
1746- var getForm = function ( ) {
1747- if ( ! form ) {
1748- form = scope . $eval ( attrs . schemaValidate ) ;
1749- }
1750- return form ;
1751- } ;
1752- var form = getForm ( ) ;
1753- if ( form . copyValueTo ) {
1754- ngModel . $viewChangeListeners . push ( function ( ) {
1755- var paths = form . copyValueTo ;
1756- angular . forEach ( paths , function ( path ) {
1757- sfSelect ( path , scope . model , ngModel . $modelValue ) ;
1804+ var getForm = function ( ) {
1805+ if ( ! form ) {
1806+ form = scope . $eval ( attrs . schemaValidate ) ;
1807+ }
1808+ return form ;
1809+ } ;
1810+ var form = getForm ( ) ;
1811+ if ( form . copyValueTo ) {
1812+ ngModel . $viewChangeListeners . push ( function ( ) {
1813+ var paths = form . copyValueTo ;
1814+ angular . forEach ( paths , function ( path ) {
1815+ sfSelect ( path , scope . model , ngModel . $modelValue ) ;
1816+ } ) ;
17581817 } ) ;
1759- } ) ;
1760- }
1818+ }
17611819
1762- // Validate against the schema.
1820+ // Validate against the schema.
17631821
1764- var validate = function ( viewValue ) {
1765- form = getForm ( ) ;
1766- //Still might be undefined
1767- if ( ! form ) {
1768- return viewValue ;
1769- }
1822+ var validate = function ( viewValue ) {
1823+ form = getForm ( ) ;
1824+ //Still might be undefined
1825+ if ( ! form ) {
1826+ return viewValue ;
1827+ }
17701828
1771- // Omit TV4 validation
1772- if ( scope . options && scope . options . tv4Validation === false ) {
1773- return viewValue ;
1774- }
1829+ // Omit TV4 validation
1830+ if ( scope . options && scope . options . tv4Validation === false ) {
1831+ return viewValue ;
1832+ }
17751833
1776- var result = sfValidator . validate ( form , viewValue ) ;
1777- // Since we might have different tv4 errors we must clear all
1778- // errors that start with tv4-
1779- Object . keys ( ngModel . $error )
1834+ var result = sfValidator . validate ( form , viewValue ) ;
1835+ // Since we might have different tv4 errors we must clear all
1836+ // errors that start with tv4-
1837+ Object . keys ( ngModel . $error )
17801838 . filter ( function ( k ) { return k . indexOf ( 'tv4-' ) === 0 ; } )
17811839 . forEach ( function ( k ) { ngModel . $setValidity ( k , true ) ; } ) ;
17821840
1783- if ( ! result . valid ) {
1784- // it is invalid, return undefined (no model update)
1785- ngModel . $setValidity ( 'tv4-' + result . error . code , false ) ;
1786- error = result . error ;
1787- return undefined ;
1788- }
1789- return viewValue ;
1790- } ;
1791-
1792- // Custom validators, parsers, formatters etc
1793- if ( typeof form . ngModel === 'function' ) {
1794- form . ngModel ( ngModel ) ;
1795- }
1841+ if ( ! result . valid ) {
1842+ // it is invalid, return undefined (no model update)
1843+ ngModel . $setValidity ( 'tv4-' + result . error . code , false ) ;
1844+ error = result . error ;
1845+ return undefined ;
1846+ }
1847+ return viewValue ;
1848+ } ;
17961849
1797- [ '$parsers' , '$viewChangeListeners' , '$formatters' ] . forEach ( function ( attr ) {
1798- if ( form [ attr ] && ngModel [ attr ] ) {
1799- form [ attr ] . forEach ( function ( fn ) {
1800- ngModel [ attr ] . push ( fn ) ;
1801- } ) ;
1850+ // Custom validators, parsers, formatters etc
1851+ if ( typeof form . ngModel === 'function' ) {
1852+ form . ngModel ( ngModel ) ;
18021853 }
1803- } ) ;
18041854
1805- [ '$validators' , '$asyncValidators' ] . forEach ( function ( attr ) {
1806- // Check if our version of angular has i, i.e. 1.3+
1807- if ( form [ attr ] && ngModel [ attr ] ) {
1808- angular . forEach ( form [ attr ] , function ( fn , name ) {
1809- ngModel [ attr ] [ name ] = fn ;
1810- } ) ;
1811- }
1812- } ) ;
1855+ [ '$parsers' , '$viewChangeListeners' , '$formatters' ] . forEach ( function ( attr ) {
1856+ if ( form [ attr ] && ngModel [ attr ] ) {
1857+ form [ attr ] . forEach ( function ( fn ) {
1858+ ngModel [ attr ] . push ( fn ) ;
1859+ } ) ;
1860+ }
1861+ } ) ;
18131862
1814- // Get in last of the parses so the parsed value has the correct type.
1815- // We don't use $validators since we like to set different errors depeding tv4 error codes
1816- ngModel . $parsers . push ( validate ) ;
1863+ [ '$validators' , '$asyncValidators' ] . forEach ( function ( attr ) {
1864+ // Check if our version of angular has i, i.e. 1.3+
1865+ if ( form [ attr ] && ngModel [ attr ] ) {
1866+ angular . forEach ( form [ attr ] , function ( fn , name ) {
1867+ ngModel [ attr ] [ name ] = fn ;
1868+ } ) ;
1869+ }
1870+ } ) ;
18171871
1818- // Listen to an event so we can validate the input on request
1819- scope . $on ( 'schemaFormValidate' , function ( ) {
1820- if ( ngModel . $setDirty ) {
1821- // Angular 1.3+
1822- ngModel . $setDirty ( ) ;
1823- validate ( ngModel . $modelValue ) ;
1824- } else {
1825- // Angular 1.2
1826- ngModel . $setViewValue ( ngModel . $viewValue ) ;
1827- }
1872+ // Get in last of the parses so the parsed value has the correct type.
1873+ // We don't use $validators since we like to set different errors depeding tv4 error codes
1874+ ngModel . $parsers . push ( validate ) ;
18281875
1829- } ) ;
1876+ // Listen to an event so we can validate the input on request
1877+ scope . $on ( 'schemaFormValidate' , function ( ) {
1878+ if ( ngModel . $setDirty ) {
1879+ // Angular 1.3+
1880+ ngModel . $setDirty ( ) ;
1881+ validate ( ngModel . $modelValue ) ;
1882+ } else {
1883+ // Angular 1.2
1884+ ngModel . $setViewValue ( ngModel . $viewValue ) ;
1885+ }
18301886
1831- scope . schemaError = function ( ) {
1832- return error ;
1833- } ;
1887+ } ) ;
18341888
1835- }
1836- } ;
1837- } ] ) ;
1889+ scope . schemaError = function ( ) {
1890+ return error ;
1891+ } ;
1892+
1893+ }
1894+ } ;
1895+ } ] ) ;
18381896
18391897return schemaForm ;
18401898} ) ) ;
0 commit comments