Skip to content

Commit 66837f3

Browse files
authored
Merge pull request #183 from AtlasOfLivingAustralia/feature/issue177
Apply include/exclude support to other constraint types #177
2 parents d31b206 + 04b0441 commit 66837f3

File tree

3 files changed

+77
-49
lines changed

3 files changed

+77
-49
lines changed

grails-app/assets/javascripts/forms.js

+58-43
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,39 @@ function orEmptyArray(v) {
667667

668668
};
669669

670+
function applyIncludeExclude(metadata, outputModel, observable, initialConstraints) {
671+
var path = metadata.constraints.excludePath || metadata.constraints.includePath;
672+
673+
var currentValue = observable();
674+
var selectedSoFar = [];
675+
outputModel.eachValueForPath(path, function (val) {
676+
if (_.isArray(val)) {
677+
selectedSoFar = selectedSoFar.concat(val);
678+
} else if (val != null) {
679+
selectedSoFar.push(val);
680+
}
681+
});
682+
683+
if (metadata.constraints.excludePath) {
684+
return _.filter(initialConstraints, function (value) {
685+
var isCurrentSelection = _.isArray(currentValue) ? currentValue.indexOf(value) >= 0 : value == currentValue;
686+
return (isCurrentSelection || selectedSoFar.indexOf(value) < 0);
687+
});
688+
} else {
689+
var constraints = initialConstraints.concat(selectedSoFar);
690+
var currentSelection = _.isArray(currentValue) ? currentValue : [currentValue];
691+
for (var i = 0; i < currentSelection.length; i++) {
692+
693+
if (currentSelection[i] != null && constraints.indexOf(currentSelection[i]) < 0) {
694+
constraints.push(currentSelection[i]);
695+
}
696+
}
697+
698+
return constraints;
699+
}
700+
701+
}
702+
670703
/**
671704
* Implements the constraints specified on a single data model item using the "constraints" attribute in the metadata.
672705
* Also provides access to global configuration and context for components that need it.
@@ -744,6 +777,7 @@ function orEmptyArray(v) {
744777
}
745778

746779
self.constraints = [];
780+
var includeExcludeDefined = metadata.constraints.excludePath || metadata.constraints.includePath;
747781
// Support existing configuration style.
748782
if (_.isArray(metadata.constraints)) {
749783
self.constraints = [].concat(metadata.constraints);
@@ -755,63 +789,44 @@ function orEmptyArray(v) {
755789
var rule = _.find(metadata.constraints.options, function (option) {
756790
return ecodata.forms.expressionEvaluator.evaluateBoolean(option.condition, context);
757791
});
758-
return rule ? rule.value : metadata.constraints.default;
792+
var evaluatedConstraints = rule ? rule.value : metadata.constraints.default;
793+
return !includeExcludeDefined ? evaluatedConstraints : applyIncludeExclude(metadata, context.outputModel, self, metadata.constraints.default || []);
759794
});
760-
}
761-
// This configuration takes a set of default constraints then either
762-
// adds values based on selections from other model items in the form or
763-
// removes selections based on other model items in the form. The main use
764-
// case this was developed for was to only allow each contraint to be selected once
765-
// in a form.
766-
else if (metadata.constraints.excludePath || metadata.constraints.includePath) {
767-
var defaultConstraints = metadata.constraints.default || [];
768-
var path = metadata.constraints.excludePath || metadata.constraints.includePath;
769-
self.constraints = ko.computed(function() {
770-
var currentValue = self();
771-
var selectedSoFar = [];
772-
context.outputModel.eachValueForPath(path, function(val) {
773-
if (_.isArray(val)) {
774-
selectedSoFar = selectedSoFar.concat(val);
775-
}
776-
else if (val != null) {
777-
selectedSoFar.push(val);
778-
}
779-
});
780-
781-
if (metadata.constraints.excludePath) {
782-
return _.filter(defaultConstraints, function (value) {
783-
var isCurrentSelection = _.isArray(currentValue) ? currentValue.indexOf(value) >= 0 : value == currentValue;
784-
return (isCurrentSelection || selectedSoFar.indexOf(value) < 0);
785-
});
786-
}
787-
else {
788-
var constraints = defaultConstraints.concat(selectedSoFar);
789-
var currentSelection = _.isArray(currentValue) ? currentValue : [currentValue];
790-
for (var i=0; i<currentSelection.length; i++) {
791-
792-
if (currentSelection[i] != null && constraints.indexOf(currentSelection[i]) < 0) {
793-
constraints.push(currentSelection[i]);
794-
}
795-
}
796-
797-
return constraints;
798-
}
795+
} else if (includeExcludeDefined) {
796+
self.constraints = ko.computed(function () {
797+
return applyIncludeExclude(metadata, context.outputModel, self, metadata.constraints.default || []);
799798
});
800799
}
801800
}
802801
else if (metadata.constraints.type == 'pre-populated') {
803802
var defaultConstraints = metadata.constraints.defaults || [];
804-
self.constraints = ko.observableArray(defaultConstraints);
803+
var constraintsObservable = ko.observableArray(defaultConstraints);
804+
if (!includeExcludeDefined) {
805+
self.constraints = constraintsObservable;
806+
}
807+
else {
808+
self.constraints = ko.computed(function () {
809+
return applyIncludeExclude(metadata, context.outputModel, self, constraintsObservable());
810+
});
811+
}
805812

806813
constraintsInititaliser = $.Deferred();
807814
var dataLoader = ecodata.forms.dataLoader(context, config);
808815
dataLoader.prepop(metadata.constraints.config).done(function (data) {
809-
self.constraints(data);
816+
constraintsObservable(data);
810817
constraintsInititaliser.resolve();
811818
});
812819
}
813820
else if (metadata.constraints.type == 'literal' || metadata.constraints.literal) {
814-
self.constraints = [].concat(metadata.constraints.literal);
821+
822+
if (includeExcludeDefined) {
823+
self.constraints = ko.computed(function() {
824+
return applyIncludeExclude(metadata, context.outputModel, self, metadata.constraints.literal || []);
825+
});
826+
}
827+
else {
828+
self.constraints = [].concat(metadata.constraints.literal);
829+
}
815830
}
816831
}
817832

grails-app/conf/example_models/constraintsExample.json

+14-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,16 @@
1515
"columns": [
1616
{
1717
"name": "value1",
18-
"dataType": "text"
18+
"dataType": "text",
19+
"constraints": {
20+
"type": "pre-populated",
21+
"config": {
22+
"source": {
23+
"url": "/preview/prepopulateConstraints"
24+
}
25+
},
26+
"excludePath": "list.value1"
27+
}
1928
},
2029
{
2130
"name": "nestedList",
@@ -39,9 +48,8 @@
3948
"name": "value3",
4049
"dataType": "stringList",
4150
"constraints": {
42-
"type": "computed",
4351
"includePath": "list.nestedList.value2",
44-
"default": [
52+
"literal": [
4553
"Default value"
4654
]
4755
}
@@ -90,7 +98,7 @@
9098
"type": "row",
9199
"items": [
92100
{
93-
"preLabel": "Value1",
101+
"preLabel": "Value1 - pre-populated constraints with one constraint use per form",
94102
"type": "selectOne",
95103
"source": "value1"
96104
}
@@ -103,12 +111,12 @@
103111
"columns": [
104112
{
105113
"type": "selectOne",
106-
"title": "Value 2",
114+
"title": "Value 2 - constraints with one constraint use per form",
107115
"source": "value2"
108116
},
109117
{
110118
"type": "selectMany",
111-
"title": "Value 3",
119+
"title": "Value 3 - Constraints allowed from selections made from value2",
112120
"source": "value3"
113121
}
114122
]

grails-app/controllers/au/org/ala/ecodata/forms/PreviewController.groovy

+5
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ class PreviewController {
104104
respond params
105105
}
106106

107+
def prepopulateConstraints() {
108+
List constraints = ['pre-pop c1', 'pre-pop c2', 'pre-pop c3']
109+
respond constraints
110+
}
111+
107112
/**
108113
* Stub function for testing geoMap dataType.
109114
*/

0 commit comments

Comments
 (0)