Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding real number values to Grails domain attributes (values sent by Dojo widgets)

I am using Dojo 1.9 with Grails 2.3.9.

The Dojo NumberTextBox widget - that I use in my forms - sets real number values (e.g.: 12.56) in a fixed format (the JavaScript base format) into the HTML form input fields (but displays/edits them according to the browser locale, so the user always sees properly formatted numbers).

Grails on the other hand expects the input fields to be formatted according to the browser locale.

This results in a conversion mismatch and the effect is that Grails loses the decimal places when the browser locale is not English and saves the record incorrectly to the database.

I was trying to override the value conversion in Grails by implementing a custom ValueConverter and registering it in the application context.

The request sent by the browser contains the real value correctly ("12.45")

The main problem is that my converter doesn't seem to be used at all.

How do I register it to override the defaut Double data conversion?


The converter:

package gefc.dojo.binding

import org.grails.databinding.converters.ValueConverter
import java.text.NumberFormat

/**
 * Converter which allows that the doubles arrive 
 */
class DojoDoubleValueConverter implements ValueConverter {

  NumberFormat fmt

  DojoDoubleValueConverter() {
    // The number format sent by Dojo components
    // English locale for the decimal separator
    fmt = NumberFormat.getInstance(Locale.ENGLISH);
    // no grouping
    fmt.setGroupingUsed(false);
  }

  boolean canConvert(value) { value instanceof String }

  def convert(value) {
    Number n = fmt.parse(value)
    return n.doubleValue()
  }

  Class<?> getTargetType() {
    return Double.class
  }
}

My registration in the application context (resources.groovy)

beans = {
  // Dojo components send real values in a fixed, ISO format, while Grails
  // expects them to be formatted according to client/browser locale
  // So we need to override real value conversions
  doubleConverter gefc.dojo.binding.DojoDoubleValueConverter
}
like image 237
sola Avatar asked Mar 19 '23 07:03

sola


2 Answers

This issue is fixed in Grails 2.4+

I had similar issue with Long and Integer converter with Grails 2.3.8. Grails is using LocaleAwareNumberConverter which converts string like "123abc" to "123". So I defined my own converter and override the bean definition in resources.groovy

beans = {
    "defaultGrailsjava.lang.LongConverter"(LongValueConverter)
    "defaultGrailsjava.lang.IntegerConverter"(IntegerValueConverter) 
}

class LongValueConverter implements ValueConverter {


    public LongValueConverter() {
    }

    boolean canConvert(value) {
        value instanceof String
    }

    def convert(value) {
        return value?.toLong()
    }

    Class<?> getTargetType() {
        return Long.class
    }
}
like image 54
Ben W Avatar answered Apr 06 '23 22:04

Ben W


I have finally found the solution.

The primary problem was that, the naming of the converter beans was wrong. The two converters dealing with double/Double must be called the following (in the applicationContext):

  • "defaultGrailsdoubleConverter" (for double)
  • "defaultGrailsjava.lang.DoubleConverter" (for Double)

This is slightly confusing since "defaultDateConverter" is named in a much more simple way and I thought the double converter naming will be consistent with that.

Secondary problem is that if you want to override these from a plugin (as opposed to the application project), then you must do the registration from YourGrailsPlugin.doWithSpring() since resources.groovy will not be packaged with the plugin. If you want to do the override from the application project itself, then placing them in resources.groovy is fine.

You may also want to ensure that this registration happens AFTER the DataBinding plugin has been initialized, otherwise your plugin may be initialized first and the DataBinding plugin simply overwrites your converter registration with the defaults. This may be done by announcing a soft dependency on the DataBinding plugin from YourGrailsPlugin.groovy:

    def loadAfter = ['dataBinding']
like image 36
3 revs Avatar answered Apr 06 '23 22:04

3 revs