@@ -243,8 +243,6 @@ function orEmptyArray(v) {
243
243
} ;
244
244
} ( ) ;
245
245
246
-
247
-
248
246
function arrayFunction ( array , expression , reducer , memo ) {
249
247
var parsedExpression = exprEval . Parser . parse ( expression ) ;
250
248
var variables = parsedExpression . variables ( ) ;
@@ -751,12 +749,54 @@ function orEmptyArray(v) {
751
749
}
752
750
else if ( _ . isObject ( metadata . constraints ) ) {
753
751
if ( metadata . constraints . type == 'computed' ) {
754
- self . constraints = ko . computed ( function ( ) {
755
- var rule = _ . find ( metadata . constraints . options , function ( option ) {
756
- return ecodata . forms . expressionEvaluator . evaluateBoolean ( option . condition , context ) ;
752
+ if ( _ . isArray ( metadata . constraints . options ) ) {
753
+ self . constraints = ko . computed ( function ( ) {
754
+ var rule = _ . find ( metadata . constraints . options , function ( option ) {
755
+ return ecodata . forms . expressionEvaluator . evaluateBoolean ( option . condition , context ) ;
756
+ } ) ;
757
+ return rule ? rule . value : metadata . constraints . default ;
757
758
} ) ;
758
- return rule ? rule . value : metadata . constraints . default ;
759
- } ) ;
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 {
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
+ }
798
+ } ) ;
799
+ }
760
800
}
761
801
else if ( metadata . constraints . type == 'pre-populated' ) {
762
802
var defaultConstraints = metadata . constraints . defaults || [ ] ;
@@ -769,7 +809,7 @@ function orEmptyArray(v) {
769
809
constraintsInititaliser . resolve ( ) ;
770
810
} ) ;
771
811
}
772
- else if ( metadata . constraints . type == 'literal' || metadata . contraints . literal ) {
812
+ else if ( metadata . constraints . type == 'literal' || metadata . constraints . literal ) {
773
813
self . constraints = [ ] . concat ( metadata . constraints . literal ) ;
774
814
}
775
815
}
@@ -1197,6 +1237,50 @@ function orEmptyArray(v) {
1197
1237
return deferred ;
1198
1238
} ;
1199
1239
1240
+ /**
1241
+ * Invokes the callback function for each value of the specified path in this model.
1242
+ * The path must treat array/list valued items as a simple property name (without
1243
+ * an index) - each array in the path will be iterated over so the callback may be
1244
+ * invoked more than once.
1245
+ * @param path a string specifying the path to the item of interest e.g. 'list1.list2.property'
1246
+ * @param callback a function taking a single argument which will be the value of the property
1247
+ * specified by path.
1248
+ */
1249
+ self . eachValueForPath = function ( path , callback ) {
1250
+ var pathAsArray = path . split ( '.' ) ;
1251
+ self . iterateOverPath ( pathAsArray , callback , self . data ) ;
1252
+ }
1253
+
1254
+ /**
1255
+ * Recursively iterates over each element in the supplied pathAsArray, taking into
1256
+ * account when values are lists.
1257
+ * @param pathAsArray
1258
+ * @param callback
1259
+ * @param dataModel
1260
+ */
1261
+ self . iterateOverPath = function ( pathAsArray , callback , dataModel ) {
1262
+ var modelItem = ko . utils . unwrapObservable ( dataModel [ pathAsArray [ 0 ] ] ) ;
1263
+
1264
+ function nextPath ( model ) {
1265
+ var nextPath = pathAsArray . slice ( 1 ) ;
1266
+ self . iterateOverPath ( nextPath , callback , model ) ;
1267
+ }
1268
+ // If the current model is an array, iterate over all elements in the array.
1269
+ if ( _ . isArray ( modelItem ) ) {
1270
+ for ( var i = 0 ; i < modelItem . length ; i ++ ) {
1271
+ nextPath ( modelItem [ i ] ) ;
1272
+ }
1273
+ }
1274
+ // Otherwise, just move deeper into the object hierarchy
1275
+ else if ( pathAsArray . length > 1 ) {
1276
+ nextPath ( modelItem ) ;
1277
+ }
1278
+ // If we are at the final element of the path, we have reached the value we are after, so
1279
+ // invoke the callback with this value.
1280
+ else if ( pathAsArray . length == 1 ) {
1281
+ callback ( modelItem ) ;
1282
+ }
1283
+ }
1200
1284
1201
1285
self . attachDocument = function ( target ) {
1202
1286
var url = config . documentUpdateUrl || fcConfig . documentUpdateUrl ;
0 commit comments