From b362334e26b8235dddc8a50447e1cf56e36abd7b Mon Sep 17 00:00:00 2001 From: chrisala Date: Fri, 12 Jul 2024 09:03:10 +1000 Subject: [PATCH 1/4] Fix view mode rendering of select2Many/selectMany #256 --- .../javascripts/forms-knockout-bindings.js | 15 +++++++++++++ .../ala/ecodata/forms/ModelJSTagLib.groovy | 3 ++- .../forms/ViewModelWidgetRenderer.groovy | 22 +++++++++++++------ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/grails-app/assets/javascripts/forms-knockout-bindings.js b/grails-app/assets/javascripts/forms-knockout-bindings.js index 027d107a..b27ffa24 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 52ea12a8..bc313952 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 f8cfd001..2f7ac82d 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(", ")' From aa7e77955b0aad7d02d4aaafd1284189ec3d66d5 Mon Sep 17 00:00:00 2001 From: chrisala Date: Fri, 12 Jul 2024 10:24:09 +1000 Subject: [PATCH 2/4] Updated label function to render tags correctly in view mode #256 --- grails-app/assets/javascripts/forms.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grails-app/assets/javascripts/forms.js b/grails-app/assets/javascripts/forms.js index da771c3b..1f9b4df6 100644 --- a/grails-app/assets/javascripts/forms.js +++ b/grails-app/assets/javascripts/forms.js @@ -1050,7 +1050,7 @@ function orEmptyArray(v) { if (match) { return self.constraints.text(match); } - return ''; + return value || ''; } } From db8cf336ebdbe0419bd5bcbb65269333ea512748 Mon Sep 17 00:00:00 2001 From: chrisala Date: Fri, 12 Jul 2024 10:50:11 +1000 Subject: [PATCH 3/4] Updated test #256 --- src/test/js/spec/DataModelItemSpec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/js/spec/DataModelItemSpec.js b/src/test/js/spec/DataModelItemSpec.js index e60f0c77..06e59e1a 100644 --- a/src/test/js/spec/DataModelItemSpec.js +++ b/src/test/js/spec/DataModelItemSpec.js @@ -90,7 +90,7 @@ describe("DataModelItem Spec", function () { dataItem('2'); expect(dataItem.constraints.label()).toEqual('2') expect(dataItem.constraints.label('3')).toEqual('3') - expect(dataItem.constraints.label("does not exist")).toEqual(''); + expect(dataItem.constraints.label("does not exist")).toEqual('does not exist'); // Support for tags and historical constraints that have been removed. var objectConstraints = [{text:'label 1', value:'1'}, {text:'label 2', value:'2'}, {text:'label 3', value:'3'}]; metadata.constraints = { @@ -104,7 +104,7 @@ describe("DataModelItem Spec", function () { dataItem('2'); expect(dataItem.constraints.label()).toEqual('label 2') expect(dataItem.constraints.label('3')).toEqual('label 3') - expect(dataItem.constraints.label("does not exist")).toEqual(''); + expect(dataItem.constraints.label("does not exist")).toEqual('does not exist'); // Support for tags and historical constraints that have been removed. metadata.constraints = { type:"pre-populated", From 7490d6420634d130e80b9a344a6b36957cb145c1 Mon Sep 17 00:00:00 2001 From: chrisala Date: Fri, 12 Jul 2024 10:52:06 +1000 Subject: [PATCH 4/4] Updated test #256 --- src/test/js/spec/DataModelItemSpec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/js/spec/DataModelItemSpec.js b/src/test/js/spec/DataModelItemSpec.js index 06e59e1a..c479824c 100644 --- a/src/test/js/spec/DataModelItemSpec.js +++ b/src/test/js/spec/DataModelItemSpec.js @@ -128,7 +128,6 @@ describe("DataModelItem Spec", function () { deferred.resolve(objectConstraints).then(function() { expect(dataItem.constraints.label()).toEqual('label 2') expect(dataItem.constraints.label('3')).toEqual('label 3') - expect(dataItem.constraints.label("does not exist")).toEqual(''); done(); });