Skip to content

Commit 04b0441

Browse files
committed
Apply include/exclude support to other constraint types #177
1 parent 36326b4 commit 04b0441

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.
@@ -743,6 +776,7 @@ function orEmptyArray(v) {
743776
}
744777

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

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

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)