diff --git a/grails-app/assets/javascripts/forms-knockout-bindings.js b/grails-app/assets/javascripts/forms-knockout-bindings.js index 027d107..b27ffa2 100644 --- a/grails-app/assets/javascripts/forms-knockout-bindings.js +++ b/grails-app/assets/javascripts/forms-knockout-bindings.js @@ -1146,6 +1146,21 @@ ecodata.forms.OutputListSupport.apply(target, [options.metadata, options.constructorFunction, options.context, options.userAddedRows, options.config]); }; + /** + * The role of this extender is to provide a function the view model can use to render a list of + * values selected using a multi select component (select2Many / selectMany) that have also used a + * label/value configuration for the options. + * @param target the observable. + * @param options unused + */ + ko.extenders.toReadOnlyString = function(target, options) { + target.toReadOnlyString = function() { + var values = ko.utils.unwrapObservable(target); + var labels = target.constraints && _.isFunction(target.constraints.label) ? _.map(values, target.constraints.label) : values; + return labels.join(', '); + } + } + /** * This is kind of a hack to make the closure config object available to the any components that use the model. */ diff --git a/grails-app/taglib/au/org/ala/ecodata/forms/ModelJSTagLib.groovy b/grails-app/taglib/au/org/ala/ecodata/forms/ModelJSTagLib.groovy index 52ea12a..bc31395 100644 --- a/grails-app/taglib/au/org/ala/ecodata/forms/ModelJSTagLib.groovy +++ b/grails-app/taglib/au/org/ala/ecodata/forms/ModelJSTagLib.groovy @@ -732,7 +732,8 @@ class ModelJSTagLib { } def stringListViewModel(JSModelRenderContext ctx) { - observableArray(ctx) + String extender = '{toReadOnlyString:true}' + observableArray(ctx, [extender]) } def setViewModel(JSModelRenderContext ctx) { diff --git a/src/main/groovy/au/org/ala/ecodata/forms/ViewModelWidgetRenderer.groovy b/src/main/groovy/au/org/ala/ecodata/forms/ViewModelWidgetRenderer.groovy index f8cfd00..2f7ac82 100644 --- a/src/main/groovy/au/org/ala/ecodata/forms/ViewModelWidgetRenderer.groovy +++ b/src/main/groovy/au/org/ala/ecodata/forms/ViewModelWidgetRenderer.groovy @@ -72,9 +72,23 @@ class ViewModelWidgetRenderer implements ModelWidgetRenderer { context.writer << "" } + /** + * The binding looks for the toReadOnlyString function because there exist some misconfigurations that + * use a "text" data model item with a selectMany/select2Many view. This is a workaround to prevent exceptions. + */ + private static String selectManyBindingString(WidgetRenderContext context) { + '_.isFunction(('+context.source+' || []).toReadOnlyString) ? '+ context.source+'.toReadOnlyString() : ('+context.source+'() || []).join(", ")' + } + @Override void renderSelectMany(WidgetRenderContext context) { - context.databindAttrs.add 'text', '('+context.source+'() || []).join(", ")' + context.databindAttrs.add 'text',selectManyBindingString(context) + context.writer << "" + } + + @Override + void renderSelect2Many(WidgetRenderContext context) { + context.databindAttrs.add 'text', selectManyBindingString(context) context.writer << "" } @@ -195,12 +209,6 @@ class ViewModelWidgetRenderer implements ModelWidgetRenderer { context.writer << """\$.00""" } - @Override - void renderSelect2Many(WidgetRenderContext context) { - context.databindAttrs.add 'text', '('+context.source+'() || []).join(", ")' - context.writer << "" - } - @Override void renderMultiInput(WidgetRenderContext context) { context.databindAttrs.add 'text', '('+context.source+'() || []).join(", ")'