How to control significant digits, ONLY when necessary, in a Thymeleaf template?

11,487

Solution 1

Hi you could try something like this.

<span th:text="${user.averageScore} % 1 == 0? ${user.averageScore} :${#numbers.formatDecimal(user.averageScore, 0, 2)}"/>

Solution 2

There's no easy way to do this in Thymeleaf 2.1, but there are two hard ways.

Hard way #1: Fork Thymeleaf and add a format method to class org.thymeleaf.expression.Numbers that does what you want (adding a method that takes a DecimalFormat pattern would seem like a logical extension)

Hard way #2: Add a dialect to Thymeleaf that provides a new expression class that does the formatting you want. My example below is based on using Spring with Thymeleaf to register a dialect to format numbers representing hours.

Step 1: Register the dialect:

@Component
public class ThymeLeafSetup implements InitializingBean {

@Autowired
private SpringTemplateEngine templateEngine;

@Override
public void afterPropertiesSet() throws Exception {
    templateEngine.addDialect(new HoursDialect());
}
}

Step #2: Create the dialect class (formatting logic delegated to TimeUtils static method) - based on Java8TimeDialect:

public class HoursDialect extends AbstractDialect implements IExpressionEnhancingDialect {
public static class Hours {
    public String format(BigDecimal hours) {
        return TimeUtils.formatHours(hours);
    }
}

@Override
public String getPrefix() {
    // No attribute or tag processors, so we don't need a prefix at all and
    // we can return whichever value.
    return "hours";
}

@Override
public boolean isLenient() {
    return false;
}

@Override
public Map<String, Object> getAdditionalExpressionObjects(IProcessingContext processingContext) {
    return Collections.singletonMap("hours", new Hours());
}
}

Step #3: Create formatting logic based on DecimalFormat

public class TimeUtils {

public static String formatHours(BigDecimal hours) {
    DecimalFormat format = new DecimalFormat("#0.##");
    format.setGroupingUsed(true);
    format.setGroupingSize(3);
    return format.format(hours);
}
}

Step #4: Tests of formatting logic

@Test
public void formatDecimalWilLFormatAsExpected() {
    verifyHourNumberFormatsAsExpected("1.5", "1.5");
    verifyHourNumberFormatsAsExpected("1.25", "1.25");
    verifyHourNumberFormatsAsExpected("123.0", "123");
    verifyHourNumberFormatsAsExpected("1230", "1,230");
}

void verifyHourNumberFormatsAsExpected(String number, String expected) {
    assertThat(TimeUtils.formatHours(new BigDecimal(number))).isEqualTo(expected);
}
Share:
11,487
Steve Perkins
Author by

Steve Perkins

I am a software developer working primarily in mixed Java / .NET shops, with occasional forays into Scala or Golang. Over the past several years I've been particularly focused on microservice architecture (e.g. messaging middleware and REST/HATEOAS)... and working with data at scale (e.g. Apache Cassandra, Spark, Storm, and ElasticSearch/Lucene). I am the author of 'Hibernate Search by Example', from Packt Publishing. Website/blog: http://steveperkins.com Twitter: https://twitter.com/stevedperkins GitHub: https://github.com/steve-perkins LinkedIn: http://www.linkedin.com/in/perkinssteve/

Updated on June 27, 2022

Comments

  • Steve Perkins
    Steve Perkins almost 2 years

    When using the th:text attribute to evaluate and render a numeric field, Thymeleaf displays the full number of digits available. For example, this:

    <span th:text="${user.averageScore}"/>
    

    ... might render on the browser screen:

    107.54896
    

    I would like to display this amount rounded to no more than two decimal places. From the Thymeleaf documentation, this:

    <span th:text="${#numbers.formatDecimal(user.averageScore, 0, 2)}"/>
    

    ... changes the output to this:

    107.55
    

    However, is there a way to make this more flexible... in cases where the value has FEWER than than two decimal places? I only want to remove decimal places, to get down to two. I never want to ADD decimal places, to get up to two. If the field above has a value of 107, then it would render as:

    107.00
    

    How can I make Thymeleaf format numbers for two decimal places, or less... rather than just two decimal places, no matter what?

  • Hussain
    Hussain over 5 years
    What are the 2nd and 3rd arguments?