Tuesday, December 2, 2008

Using the Filter Plugin for Grails

The filter plugin is a very useful tool if you want to be able to narrow down your lists in a convenient way.   For more info on the plugin check out http://www.grails.org/Filter+plugin.

Once you install the plugin, also check out the example app.  Some of the things that were not so obvious when using Filter are as follows:

1. Version 0.1 only works for the "list" action.  Make sure you only use this on the controller/list type of action.  Any other action, and it will not work.

2. Make sure you include prototype and scriptaculous in the header of your list.gsp.  This is important because the plugin will just sit there and do nothing without these libraries.

3. Use a template called _list.gsp for your list table.  Filter currently expects this.

4. Name your model data after your plugin with the "List" keyword added.  For example: if your controller is foobar, make sure your data model contains and is expecting a "foobarList" to iterate over for the list.  If you re-name this to something else, it won't work.

5. Add a div tag where the id is "list"around your entire list in the template.  Filter expects this when rendering.  (you can see an example in the example app you downloaded)

6. It doesn't hurt to initialize max and offset in your controller. 

class foobar {
  def list = {
    if (!params.max) params.max = 10
    if (!params.offset) params.offset = 0 
  }
}

7. Paginate requires "total" to be in the model.  Define total and return it in your controller.

One current limitation to the Filter plugin is this: If you want to use a narrowed result set (i.e. Foobar.findAllByStatus("Active")), and you set "total" in your controller using Foobar.countByStatus("Active"),  Filter only takes the count from the class when it finishes filtering.  That is, it does a clazz.count() which is basically a Foobar.count().  It also just takes the list from the parameters given (i.e. Foobar.list(params)).  What happens is, your count will be off in your pagination and result set after filtering.  You will see more data in your results after you  filter than the initial list.  You'll have to either modify the FilterController to fix this, or wait for a new release where this might be addressed.

This is a great plugin with lots of potential.  

Enjoy!
-Keith 

Thursday, July 31, 2008

Creating a new Grails tag to convert military time to standard time

I have a situation where I need to store time as military time (i.e. 0900 is 9:00 AM), but I want to display it as standard time. So, I created a new tag library in Grails to perform this for me. I then simply use the new tag in my gsp page, and all is well. Here's the code you would put in your taglib directory:

class MyTagLib {
def standardTime = {attrs, body ->
out << showStdTime(body())
}

def showStdTime(militaryTime) {
def hours
def minutes
def ampm = "AM"
if (militaryTime.trim().size() != 4) {
return militaryTime
}
try {
hours = new Integer(militaryTime[0..1])
minutes = militaryTime[2..3]

if (hours > 12) {
ampm = "PM"
hours -= 12
}

if (hours == 12) {
ampm = "PM"
}

if (hours == 0) {
hours = 12
}
} catch (Exception e) {
return "Format Exception: " + e.toString()
}

return hours + ":" + minutes + " " + ampm
}
}

And here's the tag as it looks in my gsp page (substitute the lt for <>. I couldn't get the formatting working right):

<g:standardtime>${myClass.closingTime}</g:standardTime>

Wednesday, March 26, 2008

More on changing a button name when toggling show/hide

Here's a cool way of making the div area hidden first:

http://you.gotfoo.org/using-scriptaculous-to-toggle-a-div/

Dynamic messaging and i18n in Grails

When using Grails for internationalization (i18n), you sometimes want to send some dynamic data to insert into the string. For instance, you don't just want to say "User not found.", you want to say "Keith Cochran not found.". So, to do that, you need to pass that information at runtime. Here's how to do that.

In your messages.properties file, add an {x} where you want your dynamic parameters to go. Number them starting at 0. For instance, define user.not.found in messages.properties like this:

user.not.found={0} {1} not found

Now, when you call up that particular string from a controller, pass in the variables you want to display at runtime:

flash.message = message(code: "user.not.found",
args: [user.firstname, user.lastname])


This makes your messaging back to the user much more informative.

-Keith

Notes on Grails Internationalization (i18n)

When using Grails for web app development, it's desirable to place all text in messages.properties files. When you do this, you can easily change the text to another language, and that makes internationalization easier. You still have to deal with other issues such as date formats and currency, but at least the text is easy to modify that way.

Once you have all your properties files created, you can make change languages by passing in the lang parameter and setting it to the language of your choice. For instance, if you want to list out some values, you can call the "list" action and pass it the Spanish language as a parameter.

<g:link action="list" params="[lang:'es']">

Note: Once you pass this parameter to any page, your app will now be in that language until you change it.

So, in order to facilitate this, you need to create the necessary files and reference the properties in your code. For example, create a messages.properties file and add text such as "Search" for your home page.

home.search=Search

Now, you can create a messages_es.properties that contains your Spanish translations:

home.search=Buscar

So, once you have all the text defined in your messages.properties file(s), (one for each language you are translating to) all you then need to do is reference that particular property in your code. Grails picks that up and inserts the appropriate property for you. Here are some tips on doing that in your code.

In the GSP page, if you want to reference the file in just plain text, use the g:message tag:

<g:message code="home.search" default="Search"/>

This tells Grails to find the property "home.search" in the properties file, and insert that value here. If it cannot find "home.search" in your properties file, it will insert whatever is in the default parameter.

When using the sortableColumn GSP tag, use the "titleKey" parameter to set the property as follows:

<g:sortableColumn property="title" titleKey="certification.title"/>

To set the text of a button, you can pass in a variable from the controller. I've used the flash scope to pass data back to the view, but you can use another object if you want to and return it. First, grab the value from the flash scope in the GSP page as follows:

<input type="submit" value="${flash.search}"/>

Next, in the controller action, just set the flash.search parameter before you return:

flash.search = message(code: 'home.search')

Now, the action in the controller will grab the message and throw it into the flash.search variable. You simply read that variable in the GSP page and it's all good. There's probably a better way of reading that parameter directly in the GSP page, and when I find it, I'll post an update.

Tuesday, March 25, 2008

Changing a button name when toggling show/hide

If you have a button that does a Scriptaculous toggle, you can have the text of the button changes from "Show" to "Hide" by updating the text property of the button using JavaScript and AJAX. Here's how you do it:

First, define the button and the area in which you want to show/hide. In this case, I have a change history that can get quite large, so I want to be able to toggle it on and off, with some scriptactulous pizazz. So, I first define the button as:


Change History:
<form action="javascript:void%200" method="get">
<button type="button" id="switchoff" class="togglebutton"
name="switchoff">Hide</button></form>




Next, define then area to show/hide using a div tag with an id. I'll use the id of "change_history" in this case.


<div id="change_history">
<table><!-- this is just a table with my
change history in it --></table>
</div>



Ok, now that I have my elements named and defined, it's time to set up some JavaScript to handle the toggle.


window.onload = function() {
/**
* Toggle change history.
*/
if ($("change_history") && $("switchoff")) {
$("switchoff").onclick = function() {
// Effect.toggle(element,
// ['appear' | 'slide' | 'blind'], [options] );

Effect.toggle($("change_history"), 'appear');
var toggleButton =
document.getElementById('switchoff');

if (toggleButton.textContent == "Show") {
toggleButton.textContent = "Hide";
} else if (toggleButton.textContent == "Hide") {
toggleButton.textContent = "Show";
} else {
toggleButton.textContent = "Show";
}
}
}
}



Put this JavaScript at the top of your web page in the section or, preferably, in another JavaScript file and reference it from your web page. I usually do the later. Basically, this JavaScript looks for elements named "change_history" and "switchoff", and if they are available, then go ahead and perform the function.

It next defines the onclick() method for switchoff so that, when clicked, use Scriptaculous to toggle the change_history (i.e. if it's shown, then hide it. If it's hidden, show it). This is all basic Scriptactulous, so nothing new here. However, now the JavaScript tries to determine if the text of the button is currently "Show" or "Hide". It will change it to the opposite of what it is now. The last case is for those who didn't originally use "Show" or "Hide" for the button name. It will basically change it go "Show" the first time you hide what you want to hide.

That's about it. Enjoy!