From d422000efcf82a7bf7f31182c6e2b30b7e4d89c0 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Sat, 7 Sep 2024 16:23:25 -0700 Subject: [PATCH 1/8] Allow customization of default wrapper. --- .../plugin/formfields/FormFieldsTagLib.groovy | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy index 5dd70440..73773654 100644 --- a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy +++ b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy @@ -190,26 +190,31 @@ class FormFieldsTagLib { String prefixAttribute = formFieldsTemplateService.getWidgetPrefix() ?: 'widget-' attrs.each { k, v -> - if (k?.startsWith(prefixAttribute)) widgetAttrs[k.replace(prefixAttribute, '')] = v else wrapperAttrs[k] = v } + List classes = [widgetAttrs['class']?:''] + if (model.invalid) classes << (widgetAttrs.remove('invalidClass')?:'error') + if (model.required) classes << (widgetAttrs.remove('requiredClass')?:'required') + widgetAttrs['class'] = classes.join(' ').trim() + if (widgetAttrs['class'].isEmpty()) { + widgetAttrs.remove('class') + } if (hasBody(body)) { model.widget = raw(body(model + [attrs: widgetAttrs] + widgetAttrs)) } else { model.widget = renderWidget(propertyAccessor, model, widgetAttrs, widgetFolder ?: templatesFolder, theme) } - String templateName = formFieldsTemplateService.getTemplateFor("wrapper") Map template = formFieldsTemplateService.findTemplate(propertyAccessor, templateName, fieldFolder ?: templatesFolder, theme) if (template) { out << render(template: template.path, plugin: template.plugin, model: model + [attrs: wrapperAttrs] + wrapperAttrs) } else { - out << renderDefaultField(model) + out << renderDefaultField(wrapperAttrs, model) } } } @@ -623,28 +628,39 @@ class FormFieldsTagLib { message ?: defaultMessage } - private CharSequence renderDefaultField(Map model) { - List classes = ['fieldcontain'] - if (model.invalid) classes << 'error' - if (model.required) classes << 'required' - + private CharSequence renderDefaultField(Map attrs, Map model) { + List classes = [attrs['class']?:'fieldcontain'] + if (model.invalid) classes << (attrs.remove('invalidClass')?:'error') + if (model.required) classes << (attrs.remove('requiredClass')?:'required') + attrs['class'] = classes.join(' ').trim() Writer writer = new FastStringWriter() - new MarkupBuilder(writer).div(class: classes.join(' ')) { - label(for: (model.prefix ?: '') + model.property, model.label) { + def mb = new MarkupBuilder(writer) + mb.setDoubleQuotes(true) + mb.div(class: attrs['class']) { + label(class: attrs.labelClass, for: (model.prefix ?: '') + model.property, model.label) { if (model.required) { span(class: 'required-indicator', '*') } } - // TODO: encoding information of widget gets lost - don't use MarkupBuilder - def widget = model.widget - if (widget != null) { - mkp.yieldUnescaped widget + if (attrs.divClass) { + div(class: attrs.divClass) { + renderWidget(mkp, model) + } + } else { + renderWidget(mkp, model) } - } writer.buffer } + private void renderWidget(def mkp, Map model) { + // TODO: encoding information of widget gets lost - don't use MarkupBuilder + def widget = model.widget + if (widget != null) { + mkp.yieldUnescaped widget + } + } + CharSequence renderDefaultInput(Map model, Map attrs = [:]) { renderDefaultInput(null, model, attrs) } From 7c6c04ca801f45a06d220cd8c510b421bf63c178 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Sat, 7 Sep 2024 17:03:56 -0700 Subject: [PATCH 2/8] Fix unit tests --- .../grails/plugin/formfields/FormFieldsTagLib.groovy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy index 73773654..a119ca40 100644 --- a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy +++ b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy @@ -197,8 +197,8 @@ class FormFieldsTagLib { } List classes = [widgetAttrs['class']?:''] - if (model.invalid) classes << (widgetAttrs.remove('invalidClass')?:'error') - if (model.required) classes << (widgetAttrs.remove('requiredClass')?:'required') + if (model.invalid) classes << (widgetAttrs.remove('invalidClass')?:'') + if (model.required) classes << (widgetAttrs.remove('requiredClass')?:'') widgetAttrs['class'] = classes.join(' ').trim() if (widgetAttrs['class'].isEmpty()) { widgetAttrs.remove('class') @@ -214,7 +214,7 @@ class FormFieldsTagLib { if (template) { out << render(template: template.path, plugin: template.plugin, model: model + [attrs: wrapperAttrs] + wrapperAttrs) } else { - out << renderDefaultField(wrapperAttrs, model) + out << renderDefaultField(model, wrapperAttrs) } } } @@ -628,7 +628,7 @@ class FormFieldsTagLib { message ?: defaultMessage } - private CharSequence renderDefaultField(Map attrs, Map model) { + protected CharSequence renderDefaultField(Map model, Map attrs = [:]) { List classes = [attrs['class']?:'fieldcontain'] if (model.invalid) classes << (attrs.remove('invalidClass')?:'error') if (model.required) classes << (attrs.remove('requiredClass')?:'required') From 3418bb8fb9ab9d9fea48dc2ca7844f21dc307d9e Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Sun, 8 Sep 2024 10:01:14 -0700 Subject: [PATCH 3/8] Support class and controller attributes on --- grails-app/views/templates/_fields/_table.gsp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grails-app/views/templates/_fields/_table.gsp b/grails-app/views/templates/_fields/_table.gsp index 47f86e34..2f04df84 100644 --- a/grails-app/views/templates/_fields/_table.gsp +++ b/grails-app/views/templates/_fields/_table.gsp @@ -1,4 +1,4 @@ - + class="${pageScope['class']}"> @@ -11,10 +11,10 @@ - + - + From 762dcb067e2f09b90f4d788f9b4ec0a29f41579e Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Sun, 8 Sep 2024 10:33:38 -0700 Subject: [PATCH 4/8] Support _list class attributes --- .../grails/plugin/formfields/FormFieldsTagLib.groovy | 2 +- grails-app/views/templates/_fields/_list.gsp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy index a119ca40..2672e07b 100644 --- a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy +++ b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy @@ -343,7 +343,7 @@ class FormFieldsTagLib { String template = attrs.remove('template') ?: 'list' List properties = resolvePersistentProperties(domainClass, attrs) - out << render(template: "/templates/_fields/$template", model: [domainClass: domainClass, domainProperties: properties]) { prop -> + out << render(template: "/templates/_fields/$template", model: attrs + [domainClass: domainClass, domainProperties: properties]) { prop -> BeanPropertyAccessor propertyAccessor = resolveProperty(bean, prop.name) Map model = buildModel(propertyAccessor, attrs, 'HTML') out << raw(renderDisplayWidget(propertyAccessor, model, attrs, templatesFolder, theme)) diff --git a/grails-app/views/templates/_fields/_list.gsp b/grails-app/views/templates/_fields/_list.gsp index 0f477ce6..6767249e 100644 --- a/grails-app/views/templates/_fields/_list.gsp +++ b/grails-app/views/templates/_fields/_list.gsp @@ -1,8 +1,8 @@ -
    +
      -
    1. - -
      ${body(p)}
      +
    2. + +
      ${body(p)}
    \ No newline at end of file From 58559cefbf049eac9a5d96587da373cf21b7dfa3 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Thu, 12 Sep 2024 00:01:11 -0700 Subject: [PATCH 5/8] use class instead of listClass for ol --- grails-app/views/templates/_fields/_list.gsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grails-app/views/templates/_fields/_list.gsp b/grails-app/views/templates/_fields/_list.gsp index 6767249e..f096cde1 100644 --- a/grails-app/views/templates/_fields/_list.gsp +++ b/grails-app/views/templates/_fields/_list.gsp @@ -1,4 +1,4 @@ -
      +
      1. From 530a433c3a2b8ef3140f1bf84d72579a5d3c888a Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 13 Sep 2024 00:29:22 -0700 Subject: [PATCH 6/8] =?UTF-8?q?Formatting=20suggested=20by=20S=C3=B8ren=20?= =?UTF-8?q?:)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/formfields/FormFieldsTagLib.groovy | 58 +++++++++++-------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy index 2672e07b..fa30af35 100644 --- a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy +++ b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy @@ -20,6 +20,7 @@ import grails.core.GrailsApplication import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import groovy.xml.MarkupBuilder +import groovy.xml.MarkupBuilderHelper import org.apache.commons.lang.StringUtils import org.grails.buffer.FastStringWriter import org.grails.datastore.mapping.model.MappingContext @@ -190,15 +191,16 @@ class FormFieldsTagLib { String prefixAttribute = formFieldsTemplateService.getWidgetPrefix() ?: 'widget-' attrs.each { k, v -> - if (k?.startsWith(prefixAttribute)) + if (k?.startsWith(prefixAttribute)) { widgetAttrs[k.replace(prefixAttribute, '')] = v - else + } else { wrapperAttrs[k] = v + } } - List classes = [widgetAttrs['class']?:''] - if (model.invalid) classes << (widgetAttrs.remove('invalidClass')?:'') - if (model.required) classes << (widgetAttrs.remove('requiredClass')?:'') + List classes = [widgetAttrs['class'] ?: ''] + if (model.invalid) classes << (widgetAttrs.remove('invalidClass') ?: '') + if (model.required) classes << (widgetAttrs.remove('requiredClass') ?: '') widgetAttrs['class'] = classes.join(' ').trim() if (widgetAttrs['class'].isEmpty()) { widgetAttrs.remove('class') @@ -361,10 +363,11 @@ class FormFieldsTagLib { String prefixAttribute = formFieldsTemplateService.getWidgetPrefix() ?: 'widget-' attrs.each { k, v -> - if (k?.startsWith(prefixAttribute)) + if (k?.startsWith(prefixAttribute)) { widgetAttrs[k.replace(prefixAttribute, '')] = v - else + } else { wrapperAttrs[k] = v + } } @@ -394,8 +397,9 @@ class FormFieldsTagLib { Map widgetAttrs = [:] attrs.each { k, v -> String prefixAttribute = formFieldsTemplateService.getWidgetPrefix() - if (k?.startsWith(prefixAttribute)) + if (k?.startsWith(prefixAttribute)) { widgetAttrs[k.replace(prefixAttribute, '')] = v + } } return widgetAttrs } @@ -430,11 +434,9 @@ class FormFieldsTagLib { private List resolvePropertyNames(PersistentEntity domainClass, Map attrs) { if (attrs.containsKey('order')) { return getList(attrs.order) - } - else if (attrs.containsKey('properties')) { + } else if (attrs.containsKey('properties')) { return getList(attrs.remove('properties')) } else { - List properties = resolvePersistentProperties(domainClass, attrs, true)*.name int maxProperties = attrs.containsKey('maxProperties') ? attrs.remove('maxProperties').toInteger() : 7 if (maxProperties && properties.size() > maxProperties) { @@ -479,8 +481,7 @@ class FormFieldsTagLib { String errorMsg = null try { errorMsg = error instanceof MessageSourceResolvable ? messageSource.getMessage(error, locale) : messageSource.getMessage(error.toString(), null, locale) - } - catch (NoSuchMessageException ignored) { + } catch (NoSuchMessageException ignored) { // no-op } // unresolved message codes fallback to the defaultMessage and this should @@ -546,8 +547,9 @@ class FormFieldsTagLib { private String resolvePrefix(prefixAttribute) { def prefix = resolvePageScopeVariable(prefixAttribute) ?: prefixAttribute ?: beanStack.prefix - if (prefix && !prefix.endsWith('.')) + if (prefix && !prefix.endsWith('.')) { prefix = prefix + '.' + } prefix ?: '' } @@ -622,16 +624,16 @@ class FormFieldsTagLib { def message = keysInPreferenceOrder.findResult { key -> message(code: key, default: null) ?: null } - if(log.traceEnabled && !message) { + if (log.traceEnabled && !message) { log.trace("i18n missing translation for one of ${keysInPreferenceOrder}") } message ?: defaultMessage } protected CharSequence renderDefaultField(Map model, Map attrs = [:]) { - List classes = [attrs['class']?:'fieldcontain'] - if (model.invalid) classes << (attrs.remove('invalidClass')?:'error') - if (model.required) classes << (attrs.remove('requiredClass')?:'required') + List classes = [attrs['class'] ?: 'fieldcontain'] + if (model.invalid) classes << (attrs.remove('invalidClass') ?: 'error') + if (model.required) classes << (attrs.remove('requiredClass') ?: 'required') attrs['class'] = classes.join(' ').trim() Writer writer = new FastStringWriter() def mb = new MarkupBuilder(writer) @@ -653,7 +655,7 @@ class FormFieldsTagLib { writer.buffer } - private void renderWidget(def mkp, Map model) { + private void renderWidget(MarkupBuilderHelper mkp, Map model) { // TODO: encoding information of widget gets lost - don't use MarkupBuilder def widget = model.widget if (widget != null) { @@ -729,18 +731,24 @@ class FormFieldsTagLib { if (!attrs.type) { if (constrained?.inList) { attrs.from = constrained.inList - if (!model.required) attrs.noSelection = ["": ""] + if (!model.required) { + attrs.noSelection = ["": ""] + } return g.select(attrs) } else if (constrained?.password) { attrs.type = "password" attrs.remove('value') - } else if (constrained?.email) attrs.type = "email" - else if (constrained?.url) attrs.type = "url" - else attrs.type = "text" + } else if (constrained?.email) { + attrs.type = "email" + } else if (constrained?.url) { + attrs.type = "url" + } else { + attrs.type = "text" + } } - if (constrained?.matches) attrs.pattern = constrained.matches - if (constrained?.maxSize) attrs.maxlength = constrained.maxSize + if (constrained?.matches) { attrs.pattern = constrained.matches } + if (constrained?.maxSize) { attrs.maxlength = constrained.maxSize } if (constrained?.widget == 'textarea') { attrs.remove('type') From 038e355e259cbb72e123a4c60c6de6e93c0e7ba0 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 13 Sep 2024 00:35:21 -0700 Subject: [PATCH 7/8] Document new classes --- .../taglib/grails/plugin/formfields/FormFieldsTagLib.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy index fa30af35..fc82987d 100644 --- a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy +++ b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy @@ -168,6 +168,9 @@ class FormFieldsTagLib { * @attr widget Specify the folder inside _fields where to look up for the widget template. * @attr templates Specify the folder inside _fields where to look up for the wrapper and widget template. * @attr theme Theme name + * @attr divClass class for optional div that will be added if set and contain the widget + * @attr invalidClass class added to wrapper that if property is invalid. Also available for widget. + * @attr requiredClass class added to wrapper when property is required. Also available for widget. */ def field = { attrs, body -> attrs = beanStack.innerAttributes + attrs From 668a132673fcdea25e22fc66df70f083048a630f Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 13 Sep 2024 14:50:16 -0700 Subject: [PATCH 8/8] Document defaults --- .../grails/plugin/formfields/FormFieldsTagLib.groovy | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy index fc82987d..51619c72 100644 --- a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy +++ b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy @@ -168,9 +168,10 @@ class FormFieldsTagLib { * @attr widget Specify the folder inside _fields where to look up for the widget template. * @attr templates Specify the folder inside _fields where to look up for the wrapper and widget template. * @attr theme Theme name - * @attr divClass class for optional div that will be added if set and contain the widget - * @attr invalidClass class added to wrapper that if property is invalid. Also available for widget. - * @attr requiredClass class added to wrapper when property is required. Also available for widget. + * @attr css html css attribute for wrapper (default: field-contain) + * @attr divClass class for optional div that will be added if set and contain the widget (default: no div created) + * @attr invalidClass class added to wrapper that if property is invalid. (default: error) Also available for widget. + * @attr requiredClass class added to wrapper when property is required. (default: required) Also available for widget. */ def field = { attrs, body -> attrs = beanStack.innerAttributes + attrs