Tuesday, February 21, 2012

Grails nullable boolean and integer fields

When your GORM domain object fields are defined as Java primitives like boolean it can only be specific value like true or false. But sometimes you may want to have also third value for undefined values or NULL as in database. With primitives you will get error like:
Executing action [edit] of controller [com.test.TestController] caused exception: Null value was assigned to a property of primitive type setter of com.test.TestObject.testproperty; nested exception is org.hibernate.PropertyAccessException: Null value was assigned to a property of primitive type setter of com.test.TestObject.testproperty

To enable nulls for such fields, you just have to change type from boolean primitive to Boolean object and that is it.

Thursday, February 16, 2012

sprintf, printf bug in Groovy

Recently I found strange issue when running Grails application on Linux. Sometimes, when formatting decimal number with sprintf it does not work and simply returned original number. After some investigation I found that this is general Groovy behavior and simple script with:

println sprintf('%.2f', 1/609)

returns 0.0016420361 as result, when it suppose to return just 0.00. I was not able to reproduce this bug on Windows or in plain Java, so most probably this is GDK issue, but I didn't investigated it very carefully.
Anyway, this is very easy to fix by casting numbers into decimals explicitly, so this code:

println sprintf('%.2f', 1d/609)

returns nice and correct results.

Friday, February 10, 2012

JavaScript parseInt does not work as expected when parameter starts with 0

There is standard way to parse Strings into Numbers in JavaScript by using parseInt function. But sometimes it works unexpected, and for example, returns 0 when you expect 8. If that is the case, then most probably problem is that when parameter starts with 0 (like '08') parseInt treats it as octal number and changes radix to 8.
This is annoying and can take some time to discover. Luckily, it is easy to fix by specifying radix directly as second parameter. For example like:

parseInt('08', 10)




Wednesday, February 8, 2012

Copy method in Groovy

If you need to copy method reference between objects at runtime, it is super easy in Groovy. Just add & in front of method name and this it. For example:

new Expando(id:it.id, user:it.user, mymethod:it.&othermethod, ...)

For some reason this is not very wide known feature (was not for me), and I unsuccessfully tried to use metaClass and other complicated things before finding this.

Friday, February 3, 2012

Freezing table header with JQuery

When you have long table on page, with a lot of numbers, and you scroll down and header is not visible anymore, it is often difficult to track what number is which column. One nice solution to this problem is to lock and freeze table header when scrolling page.
Unfortunately there is no ready out of the box solution with JQuery for this problem. Fortunately, it is easy to implement with few methods. There are examples how to do it, but for some reason they didn't worked well in my situation (no support for resize and horizontal scroll), so I have adjusted them for my situation.

For example, you have your table named tabs.

First, you have to create invisible table that will hold frozen header.

<table cellpadding='0' cellspacing='0' id="header-fixed"  style="position: fixed; top: 0px; display:none;"></table>

Then just add this Javascript:

     <script type="text/javascript">
       var tableOffset;
       var header;
       var fixedHeader;

       function resize() {
         var totalwidth = $('#tabs').css('width');
         fixedHeader.css('width', totalwidth);
         var widths = [];
         $('#tabs thead th').each(function() {
           widths.push($(this).width());
         });
         var i=0;
         $('#header-fixed th').each(function() {
           this.width = widths[i];
           i++;
         });
       }
       function resizeAndShow() {
         var offset = $(this).scrollTop();
         if (offset >= tableOffset && fixedHeader.is(":hidden")) {
           fixedHeader.show();
           resize();
         } else if (offset < tableOffset) {
           fixedHeader.hide();
         }
         fixedHeader.css('left', $('#tabs').position().left - $(this).scrollLeft());
       };
        $(document).ready(function() {
          tableOffset = $("#tabs").offset().top;
          header = $("#tabs > thead").clone();
          fixedHeader = $("#header-fixed").append(header);

          $(window).bind("scroll", resizeAndShow);
          $(window).resize(resize);
        });
     </script>

This is it.