diff --git a/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy b/grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy index fa2f13d3..b33bb07b 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 @@ -167,6 +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 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 @@ -190,26 +195,32 @@ 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') ?: '') + 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(model, wrapperAttrs) } } } @@ -338,7 +349,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)) @@ -356,10 +367,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 + } } @@ -389,8 +401,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 } @@ -425,11 +438,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) { @@ -474,8 +485,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 @@ -541,8 +551,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 ?: '' } @@ -617,34 +628,45 @@ 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 } - private CharSequence renderDefaultField(Map model) { - List classes = ['fieldcontain'] - if (model.invalid) classes << 'error' - if (model.required) classes << 'required' - + 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') + 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(MarkupBuilderHelper 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) } @@ -713,18 +735,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') diff --git a/grails-app/views/templates/_fields/_list.gsp b/grails-app/views/templates/_fields/_list.gsp index 0f477ce6..f096cde1 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 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 @@ - + - +