Skip to content

Commit 2839f68

Browse files
committed
Revert a31cdbe..7b53c63
This rolls back to commit a31cdbe.
1 parent 7b53c63 commit 2839f68

File tree

3 files changed

+157
-2
lines changed

3 files changed

+157
-2
lines changed

docs/index.md

+9
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,7 @@ General options most field types can handle:
634634
labelHtmlClass: "street" // CSS Class(es) to be added to the label of the field (or similar)
635635
copyValueTo: ["address.street"], // Copy values to these schema keys.
636636
condition: "person.age < 18" // Show or hide field depending on an angular expression
637+
destroyStrategy: '' // Update the model when the field is destoyed, e.g. when condition is not longer satisfied.
637638
}
638639
```
639640
@@ -823,6 +824,14 @@ function FormCtrl($scope) {
823824
Note that arrays inside arrays won't work with conditions.
824825
825826
827+
### destroyStrategy
828+
By default, when a field is removed from the DOM and the $destroy event is broadcast, the schema-validate directive
829+
will update the model to set the field value to undefined. This can be overridden by setting the destroyStrategy
830+
on a field to one of null, empty string (""), undefined, or "retain". Any other value will be ignored and the default
831+
behavior will apply. The empty string option only applies to fields that have a type of string; using the empty string
832+
with other field types will just be set to the default destroyStrategy. If you'd like to set the destroyStrategy for
833+
an entire form, add it to the formDefaults in the [globalOptions](#global-options)
834+
826835
827836
828837
Specific options and types

src/directives/schema-validate.js

+65-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
angular.module('schemaForm').directive('schemaValidate', ['sfValidator', 'sfSelect', function(sfValidator, sfSelect) {
1+
angular.module('schemaForm').directive('schemaValidate', ['sfValidator', 'sfSelect', 'sfUnselect',
2+
function(sfValidator, sfSelect, sfUnselect) {
3+
24
return {
35
restrict: 'A',
46
scope: false,
@@ -8,7 +10,6 @@ angular.module('schemaForm').directive('schemaValidate', ['sfValidator', 'sfSele
810
require: 'ngModel',
911
link: function(scope, element, attrs, ngModel) {
1012

11-
1213
// We need the ngModelController on several places,
1314
// most notably for errors.
1415
// So we emit it up to the decorator directive so it can put it on scope.
@@ -101,6 +102,68 @@ angular.module('schemaForm').directive('schemaValidate', ['sfValidator', 'sfSele
101102

102103
});
103104

105+
106+
var DEFAULT_DESTROY_STRATEGY;
107+
if (scope.options && scope.options.formDefaults) {
108+
var formDefaultDestroyStrategy = scope.options.formDefaults.destroyStrategy;
109+
var isValidFormDefaultDestroyStrategy = (formDefaultDestroyStrategy === undefined ||
110+
formDefaultDestroyStrategy === '' ||
111+
formDefaultDestroyStrategy === null ||
112+
formDefaultDestroyStrategy === 'retain');
113+
if (isValidFormDefaultDestroyStrategy) {
114+
DEFAULT_DESTROY_STRATEGY = formDefaultDestroyStrategy;
115+
}
116+
else {
117+
console.warn('Unrecognized formDefaults.destroyStrategy: \'%s\'. Used undefined instead.',
118+
formDefaultDestroyStrategy);
119+
DEFAULT_DESTROY_STRATEGY = undefined;
120+
}
121+
}
122+
123+
// Clean up the model when the corresponding form field is $destroy-ed.
124+
// Default behavior can be supplied as a formDefault, and behavior can be overridden in the form definition.
125+
scope.$on('$destroy', function() {
126+
var form = getForm();
127+
var destroyStrategy = form.destroyStrategy; // Either set in form definition, or as part of formDefaults.
128+
var schemaType = getSchemaType();
129+
130+
if (destroyStrategy && destroyStrategy !== 'retain' ) {
131+
// Don't recognize the strategy, so give a warning.
132+
console.warn('Unrecognized destroyStrategy: \'%s\'. Used default instead.', destroyStrategy);
133+
destroyStrategy = DEFAULT_DESTROY_STRATEGY;
134+
}
135+
else if (schemaType !== 'string' && destroyStrategy === '') {
136+
// Only 'string' type fields can have an empty string value as a valid option.
137+
console.warn('Attempted to use empty string destroyStrategy on non-string form type. Used default instead.');
138+
destroyStrategy = DEFAULT_DESTROY_STRATEGY;
139+
}
140+
141+
if (destroyStrategy === 'retain') {
142+
return; // Valid option to avoid destroying data in the model.
143+
}
144+
145+
destroyUsingStrategy(destroyStrategy);
146+
147+
function destroyUsingStrategy(strategy) {
148+
var strategyIsDefined = (strategy === null || strategy === '' || typeof strategy == undefined);
149+
if (!strategyIsDefined){
150+
strategy = DEFAULT_DESTROY_STRATEGY;
151+
}
152+
sfUnselect(scope.form.key, scope.model, strategy);
153+
}
154+
155+
function getSchemaType() {
156+
if (form.schema) {
157+
schemaType = form.schema.type;
158+
}
159+
else {
160+
schemaType = null;
161+
}
162+
}
163+
});
164+
165+
166+
104167
scope.schemaError = function() {
105168
return error;
106169
};

src/services/unselect.js

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
2+
angular.module('schemaForm').factory('sfUnselect', ['sfPath', function(sfPath) {
3+
var numRe = /^\d+$/;
4+
5+
/**
6+
* @description
7+
* Utility method to clear deep properties without
8+
* throwing errors when things are not defined.
9+
* DOES NOT create objects when they are missing.
10+
*
11+
* Based on sfSelect.
12+
*
13+
* ex.
14+
* var foo = Unselect('address.contact.name',obj, null)
15+
* var bar = Unselect('address.contact.name',obj, undefined)
16+
* Unselect('address.contact.name',obj,'')
17+
*
18+
* @param {string} projection A dot path to the property you want to set
19+
* @param {object} obj (optional) The object to project on, defaults to 'this'
20+
* @param {Any} unselectValue The value to set; if parts of the path of
21+
* the projection is missing empty objects will NOT be created.
22+
* @returns {Any|undefined} returns the value at the end of the projection path
23+
* or undefined if there is none.
24+
*/
25+
return function(projection, obj, unselectValue) {
26+
if (!obj) {
27+
obj = this;
28+
}
29+
//Support [] array syntax
30+
var parts = typeof projection === 'string' ? sfPath.parse(projection) : projection;
31+
//console.log(parts);
32+
33+
if (parts.length === 1) {
34+
//Special case, just setting one variable
35+
36+
//console.log('Only 1 variable in parts');
37+
obj[parts[0]] = unselectValue;
38+
return obj;
39+
}
40+
41+
if (typeof obj[parts[0]] === 'undefined') {
42+
// If top-level part isn't defined.
43+
var isArray = numRe.test(parts[1]);
44+
if (isArray) {
45+
//console.info('Expected array as top-level part, but is already undefined. Returning.');
46+
return undefined;
47+
}
48+
else if (parts.length > 2) {
49+
obj[parts[0]] = {};
50+
}
51+
}
52+
53+
var value = obj[parts[0]];
54+
for (var i = 1; i < parts.length; i++) {
55+
// Special case: We allow JSON Form syntax for arrays using empty brackets
56+
// These will of course not work here so we exit if they are found.
57+
if (parts[i] === '') {
58+
return undefined;
59+
}
60+
61+
var tmp = value[parts[i]];
62+
if (i === parts.length - 1 ) {
63+
//End of projection; setting the value
64+
65+
//console.log('Value set using destroyStrategy.');
66+
value[parts[i]] = unselectValue;
67+
return unselectValue;
68+
} else {
69+
// Make sure to NOT create new objects on the way if they are not there.
70+
// We need to look ahead to check if array is appropriate.
71+
// Believe that if an array/object isn't present/defined, we can return.
72+
73+
//console.log('Processing part %s', parts[i]);
74+
if (typeof tmp === 'undefined' || tmp === null) {
75+
//console.log('Part is undefined; returning.');
76+
return undefined;
77+
}
78+
value = tmp;
79+
}
80+
}
81+
return value;
82+
};
83+
}]);

0 commit comments

Comments
 (0)