Thursday, February 27, 2014

Binding a Grails date from params in a controller

Add the following lines in 'Config.groovy' file


grails.databinding.trimStrings=true
grails.databinding.convertEmptyStringsToNull=true
grails.databinding.dateFormats = ['yyyy-MM-dd HH:mm:ss.S', 'yyyy-MM-dd', "yyyy-MM-dd'T'hh:mm:ss'Z'"]

First line triming all string values on data binding to domain instance.
Second line convert all empty string to null.
And third line accept the date formats provided to convert it to date.

Or you can use those as following:


package com.pkm.test.domains

import org.grails.databinding.BindingFormat

class TestOne {
    Long id
    String name;
    String display;
    @BindingFormat("yyyy-mm-dd")
    Date created;

    static constraints = {
        display(nullable: true)
    }
}

And from controller


params.name = "YES-Pritom K Mondal";
params.display = " ";
params.created = "2014-02-27";
TestOne testOne = new TestOne();
DataBindingUtils.bindObjectToInstance(testOne, params)
testOne.save(failOnError: true);

Will create a following row in the table


Field 'display' is null because value from controller: ' ' first trim() and then convert empty string to null according to Config.groovy settings above.

Sunday, February 23, 2014

jQuery Change Event: Proper Binding


$("input[name='name']").on("input", function(event) {
    $("div").prepend("Text: " + $(this).val() + "<br/>");
});

// or

<input type='text' name='name' oninput='$(this).changeOccurs()'/>

and in jquery:
jQuery.fn.changeOccurs = function () {
    // do something you wish to do.
};

jsFiddle link

Wednesday, February 19, 2014

Groovy template engine

Included with Groovy are several template engines:
  • SimpleTemplateEngine - for basic templates
  • GStringTemplateEngine - stores the template as writable closures (useful for streaming scenarios)
  • XmlTemplateEngine - works well when the template and output are valid XML

SimpleTemplateEngine


import groovy.text.SimpleTemplateEngine

def text = 'Dear ${contact.firstName} ${contact.lastName}${contact.nickName? " " + contact.nickName: ""}.\n${time} is good.'

def binding = ["time":"this evening", month: "February", "contact": ["firstName": "Pritom", "lastName": "Mondal", age: 27]]

def engine = new SimpleTemplateEngine()

def template = engine.createTemplate(text).make(binding)

println template.toString();
Output
Dear Pritom Mondal.
this evening is good.

GStringTemplateEngine


import groovy.text.GStringTemplateEngine

File templateFile = new File("web-app/test.txt");
def templateEngine = new GStringTemplateEngine();
def templateObject = templateEngine.createTemplate(templateFile).make(binding);
println templateObject.toString();
File content
Dear ${contact.firstName} ${contact.lastName},
So nice to meet you in <% out << (time == "this evening" ? "\"${time}\"" : "\"this morning\"") %>.
So nice to meet you in <% out << (time == "this evening" ? "\"this morning\"" : time) %>.
See you in ${month?: ""}
Output
Dear Pritom Mondal,
So nice to meet you in "this evening".
So nice to meet you in "this morning".
See you in February

Tuesday, February 18, 2014

Check the validity of an HTML5 form that does not contain a submit button


/* Check if html5 validation enabled */
function hasHtml5Validation () {
    return typeof document.createElement('input').checkValidity === 'function';
}


var $form = $("form");
$form.find("input.save-and-next").click(function() {
    $form.find(":input").each(function() {
        //if(!$(this).checkValidity()) {
        if(!$(this)[0].checkValidity()) {            
            $form.find("input[type='submit']").click();
            return false;
        }
    });
});

When you select  $(this) you get a collection of nodes, to access actual DOM properties you must select the numbered one such $(this)[0].

Or you can bind validation process to a form field such as:

form.find("input[type='text']").keyup(function() {
    if(!$(this)[0].checkValidity()) {
        form.find("input[type='submit']").click();
    }
});

Hooking into GORM custom event listener from plugin in Grails

In our custom listener, the method ‘onPersistenceEvent’ is called on all GORM events that we filter to what we interested – PreInsert, PreUpdate
Create a groovy file under src/groovy suppose named 'FieldValidationListener.groovy' with the following contents. And create required method in your domain class, suppose one method can be named: 'doValidationPreInsert' or 'doValidationPreUpdate'.

package com.groovy.fieldSetting

import grails.util.Holders
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.grails.datastore.mapping.core.Datastore
import org.grails.datastore.mapping.engine.event.*
import org.springframework.context.ApplicationEvent

/**
 * Created by pritom on 17/02/14.
 */
class FieldValidationListener extends AbstractPersistenceEventListener {
    public FieldValidationListener(Datastore datastore) {
        super(datastore)
    }

    @Override
    protected void onPersistenceEvent(AbstractPersistenceEvent event) {
        String operation;
        switch (event.eventType) {
            case EventType.PreInsert:
                operation = "PreInsert"
                break;
            case EventType.PreUpdate:
                operation = "PreUpdate"
                break;
        }
        if(operation) {
            String methodName = "doValidation${operation}"
            println "Searching for method: ${methodName}"
            if (event.entityObject.respondsTo(methodName)) {
                try {
                    event.entityObject."${methodName}"()
                } catch (Exception exp) {
                    event.cancel();
                    throw exp;
                }
            }
        }
    }

    @Override
    boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        if ([PreInsertEvent, PreUpdateEvent].any { def supportedType ->
            if (supportedType.isAssignableFrom(eventType)) {
                return true
            } else {
                return false
            }
        }) {
            return true
        } else {
            return false
        }
    }

    public static void initialize(GrailsApplication application) {
        application.mainContext.eventTriggeringInterceptor.datastores.each { k, datastore ->
            Holders.applicationContext.addApplicationListener(new FieldValidationListener(datastore))
        }
    }
}

And initialize start listening, insert the following code block into 'BootStrap.groovy' [in 'init' method]:

FieldValidationListener.initialize(grailsApplication)

Saturday, February 15, 2014

Accessing i18n Messages from exception (validation errors) in Grails in a Service

To access your message, inject the MessageSource into your service:
def messageSource  

And to get the error messages from exception based on your i18n properties file, use the following code block in your service:
 
def messageSource;

def printAllMessage(Exception ex) {
    ex.errors.allErrors.eachWithIndex { ObjectError objectError, Integer index ->
        println "Error #${index}: " + messageSource.getMessage(objectError, Locale.default);
    }
}


Will output something like this based on you i18n messages.properties file:

Error #0: Property [accountId] cannot be null
Error #1: Property [displayName] cannot be null
Error #2: Property [emailAddress] with value [pritom@bitmascot.com] must be unique

If your i18n file contains something like:

default.null.message=Property [{0}] cannot be null
default.not.unique.message=Property [{0}] with value [{2}] must be unique

Thursday, February 13, 2014

timezone get list, time zone id, time zone name, time offset using java


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package time.zone;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

/**
 *
 * @author Pritom Kumar Mondal
 */
public class TimeZoneList {
    public static void main(String[] args) {
        String supportedPattern = "^(Africa|America|Asia|Atlantic|Australia|"
                + "Europe|Indian|Pacific)/.*";
        String[] timeZoneIdList = TimeZone.getAvailableIDs();
        List<TimeZone> timeZoneList = new ArrayList<TimeZone>();
        for(String id : timeZoneIdList) {
            if(id.matches(supportedPattern)) {
                timeZoneList.add(TimeZone.getTimeZone(id));
            }
        }
        Collections.sort(timeZoneList, new Comparator<TimeZone>() {
            public int compare(final TimeZone a, final TimeZone b) {
                return a.getID().compareTo(b.getID());
            }
        });
        for(TimeZone timeZone : timeZoneList) {
            String zoneId = timeZone.getID();
            String displayName = timeZone.getDisplayName();
            double offset = (double) timeZone.getRawOffset() / 1000 / 60 / 60;
            String minute = ((offset - (int) offset) * 60) > 0 ? (" & " + (int) 
                    ((offset - (int) offset) * 60) + " minute") : "";
            System.out.println("Zone id: " + zoneId + ", name: " + displayName + ""
                    + ", offset: " + (int) offset + " hour" + minute);
        }
    }
}

And output would something like this...

.
.
.
Zone id: Asia/Bangkok, name: Indochina Time, offset: 7 hour
Zone id: Asia/Beirut, name: Eastern European Time, offset: 2 hour
Zone id: Asia/Bishkek, name: Kirgizstan Time, offset: 6 hour
Zone id: Asia/Brunei, name: Brunei Time, offset: 8 hour
Zone id: Asia/Calcutta, name: India Standard Time, offset: 5 hour & 30 minute
Zone id: Asia/Choibalsan, name: Choibalsan Time, offset: 8 hour
Zone id: Asia/Chongqing, name: China Standard Time, offset: 8 hour
Zone id: Asia/Chungking, name: China Standard Time, offset: 8 hour
Zone id: Asia/Colombo, name: India Standard Time, offset: 5 hour & 30 minute
Zone id: Asia/Dacca, name: Bangladesh Time, offset: 6 hour
Zone id: Asia/Damascus, name: Eastern European Time, offset: 2 hour
Zone id: Asia/Dhaka, name: Bangladesh Time, offset: 6 hour
.
.
.