How can I change the way GRAILS GSP fieldValue formats Integers?

15,616

Solution 1

I think you have at least two possible solutions.

One is to use the JSTL taglib as described in the docs.

Another, cooler way is to use the 'formatNumber' tag included with grails - also in the docs.

For your purpose, the use of that tag might look like this:

<g:formatNumber number="${fieldValue(bean: myBean, field: 'minPrice')}" format="######" />

Solution 2

Better use custom PropertyEditor in order not to bother with formatNumber tag every time you output a value. Like, declare a bean in resources.groovy:

myOwnCustomEditorRegistrar(CustomEditorRegistrar)

And create your class:

class CustomEditorRegistrar implements PropertyEditorRegistrar {
    void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(BigDecimal.class, new MyBigDecimalEditor(BigDecimal.class))
    }
}

Solution 3

Use the 'groupingUsed' attribute in combination with your format:

<g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}" 
                format="#" 
                groupingUsed="true" />

Solution 4

Change

var x = <g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}" format="#" />;

to

var x = <g:formatNumber number="${personInstance.minPrice}" format="#" />;

Solution 5

I found the best way to handle this was doing what Victor Sergienko (upped btw) hinted at with using a PropertyEditor.

Create an editor for Integer, put in src/groovy:

class IntegerEditor extends PropertyEditorSupport {
    void setAsText(String s) {
        if (s) value = s as Integer
    }

    public String getAsText() {
        value
    }
}

and register it using a PropertyEditorRegistrar (also in src/groovy):

class MyEditorRegistrar implements PropertyEditorRegistrar {
    public void registerCustomEditors(PropertyEditorRegistry reg) {
        reg.registerCustomEditor(Integer, new IntegerEditor())
    }
}

add your registrar into the spring config (grails-app/conf/spring/resource.groovy):

beans = {
    customEditorRegistrar(MyEditorRegistrar)
}

From now on any Integers that are bound, receive errors (or not) and then redisplayed with the fieldValue tag should be displayed by Integer's default toString - you can customise this behaviour in the editor by amending the getAsText implementation.

Personally I would create a wrapper for this kind of thing so you can set up an editor just for that type rather than across the board for a frequently used type. Though I realise this would mean a little bit of mapping when persisting to the DB...

Share:
15,616
Simon
Author by

Simon

Updated on July 01, 2022

Comments

  • Simon
    Simon almost 2 years

    I have a field in my domain object which I define as an Integer...

    Integer minPrice
    

    I then access it in a GSP page as follows:

    ${fieldValue(bean: myBean, field: 'minPrice')}
    

    and what I get in my HTML is...

    100,000
    

    which is not an Integer, it's a String. Worse still it's a formatted String in a particular locale.

    This is a problem because I have a SELECT control on an HTML FORM which has a (non-ordinal) range of values for minPrice which I want to store in my domain object as integers, and I don't want to store an index to some array of values that I have to repeatedly map back and forth between, I want the value itself.

    My select control looks like this...

    <g:select name="minPrice" 
    value="${fieldValue(bean: personInstance, field: 'minPrice')}"  
    onchange="setDirty()"
    noSelection='${['0':'Select a number...']}'
    from="${[
        ['name':'100,000', 'id':100000],
        ['name':'200,000', 'id':200000],
        ['name':'300,000', 'id':300000]
        ]}"
    optionKey="id" optionValue="name"
    />
    

    When I get the value from the SELECT field to post back to the server it correctly has an Integer value, which I persist. However the return trip never pre-selects the right row in the drop-down because the value is this comma separated String.

    This works fine elsewhere in my code for small numbers where the comma formatting doesn't come into play, and the round-trip in and out of the SELECT is successful. But values >999 don't work.

    The docs say "This tag will inspect a bean which has been the subject of data binding and obtain the value of the field either from the originally submitted value contained within the bean's errors object populating during data binding or from the value of a bean's property. Once the value is obtained it will be automatically HTML encoded."

    It's that last bit that I want to avoid as it appears to format Integers. So, what little bit of Grails/GSP magic do I need to know so I can get my Integer to be rendered as an integer into my SELECT and pre-select the right row?

    EDIT: I have tried some further things based on the answers below, with pretty disappointing results so far...

    If I put the <gformatNumber/> tag in my <g:select/> I get the page code as text in the browser.

    <g:select name="minPrice" 
    value='<g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}" format="#" />'
    onchange="setDirty()"
    noSelection='${['0':'Select a number...']}'
    from="${[
    ['name':'100,000', 'id':100000],
    ['name':'200,000', 'id':200000],
    ['name':'300,000', 'id':300000],
    ]}"
    optionKey="id" optionValue="name"
    />
    

    Using the number format tag from GSP on my Integer value of 100000 like this...

    var x = <g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}" format="#" />;
    

    gives 100. Remember that the fieldValue gives back 100,000, so this is not a surprise.

    If I use the jsp taglib like this...

    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    var y = <fmt:formatNumber value="${fieldValue(bean: personInstance, field: 'minPrice')}" pattern=".00"/>;
    

    I get an error from the page compiler Cannot format given Object as a Number.

    I guess I have a wider concern than I can't seem to get an Integer value as a genuine integer into my code if it is greater than 999 because of the default (and unconfigurable) behaviour of the fieldValue directive. However my specific problem of not being able to pre-select an Integer value in a SELECT control is not going away. At the moment I'm at a bit of a loss.

    Anyone have any further ideas?