<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-5079888946107534313</atom:id><lastBuildDate>Mon, 01 Aug 2011 19:53:58 +0000</lastBuildDate><category>grails install-plugin searchable</category><title>moongrails</title><description>def blog = {println "all about grails"}</description><link>http://moongrails.blogspot.com/</link><managingEditor>noreply@blogger.com (Mo Sayed)</managingEditor><generator>Blogger</generator><openSearch:totalResults>12</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/Moongrails" /><feedburner:info uri="moongrails" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-7204143878507412246</guid><pubDate>Wed, 14 Jan 2009 14:53:00 +0000</pubDate><atom:updated>2009-01-16T09:38:36.934Z</atom:updated><title>grails: Inline Editing For Collection Elements</title><description>Consider the scenario, where a User is viewing data on a page and then realises that a change needs to be made to a particular field. Usually this would require navigating to an 'edit' page, modifying the field and then resubmitting the form before viewing the original page to confirm the changes.&lt;br /&gt;&lt;br /&gt;Javascript offers a better alternative, an approach called Inline Editing whereby the User can dynamically edit &amp;amp; validate a field, update the server in the process without the need to perform form submission and page refreshes.&lt;br /&gt;&lt;br /&gt;In this post I'll demonstrate how to implement this feature using the Scriptaculous library. The example is based upon a simplified issue tracker as shown below.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_aesd62-K5Pg/SW4TNl1rv5I/AAAAAAAAAFw/UyQHW4IHlMc/s1600-h/createIssueForm.png"&gt;&lt;img style="cursor: pointer; width: 277px; height: 211px;" src="http://2.bp.blogspot.com/_aesd62-K5Pg/SW4TNl1rv5I/AAAAAAAAAFw/UyQHW4IHlMc/s400/createIssueForm.png" alt="" id="BLOGGER_PHOTO_ID_5291187736423415698" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is the &lt;span style="font-style: italic;"&gt;create.gsp page&lt;/span&gt;, which allows the User to create a new Issue and assign it a priority which can assume a value of either Low, Medium, or High.&lt;br /&gt;&lt;br /&gt;The following image shows&lt;span style="font-style: italic;"&gt; list.gsp&lt;/span&gt;, where we display a typical 'list' view of the issues collected.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_aesd62-K5Pg/SW4XRGWiaJI/AAAAAAAAAGA/vlNqxaRiUsY/s1600-h/before.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 118px;" src="http://3.bp.blogspot.com/_aesd62-K5Pg/SW4XRGWiaJI/AAAAAAAAAGA/vlNqxaRiUsY/s400/before.png" alt="" id="BLOGGER_PHOTO_ID_5291192194737268882" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now it would be useful if the User could adjust the priorities for the various issues without having to perform the typical edit/submit/refresh cycle just to alter a single field. By implementing Inline Editing for the priority field we would enhance User productivity and application usability in one fell swoop.&lt;br /&gt;&lt;br /&gt;So let's follow through the steps to implement this feature.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 1: Import scriptaculous&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 20px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;       &amp;lt;g:javascript library="scriptaculous"/&amp;gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 2: Code up the Ajax&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In &lt;span style="font-style: italic;"&gt;list.gsp&lt;/span&gt; locate the line as follows, which simply outputs the priority for a given issue&lt;br /&gt;&lt;br /&gt;&lt;div face="courier" size="12px" style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 30px; color: rgb(0, 0, 0); text-align: left;"&gt;&lt;pre&gt;&amp;lt;td&amp;gt;${fieldValue(bean:issue, field:'priority')}&amp;lt;/td&amp;gt;&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;and replace it with&lt;br /&gt;&lt;br /&gt;&lt;div face="courier" size="12px" style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 1050px; height: 200px; color: rgb(0, 0, 0); text-align: left;"&gt;&amp;lt;td&amp;gt;             &lt;br /&gt;&lt;pre&gt;    &amp;lt;span id="${issue.id}" class="editInPlace"&amp;gt;${fieldValue(bean:issue, field:'priority')}&amp;lt;/span&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;new Ajax.InPlaceCollectionEditor("${issue.id}",&lt;br /&gt;      "/inlineApp/issue/updatePriority",&lt;br /&gt;      { collection: [['Low','Low'], ['Medium','Medium'], ['High','High']], ajaxOptions: {method: 'post'} })&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The replacement snippet consists of wrapping a &amp;lt;span&amp;gt; element around our original statement and inserting some javascript code. The &amp;lt;span&amp;gt; element is assigned an id equal to the identifier of our Issue object and a styling class of 'editInPlace' which is defined in step 4.&lt;br /&gt;&lt;br /&gt;Below the &amp;lt;span&amp;gt; element we insert some javascript code that invokes the &lt;a href="http://wiki.github.com/madrobby/scriptaculous/ajax-inplacecollectioneditor"&gt;&lt;span style="font-style: italic;"&gt;Ajax.InPlaceCollectionEditor&lt;/span&gt;&lt;/a&gt; function defined in Scriptaculous. This function is specifically for creating editable fields for collection types and it works in the following way. Firstly it binds a listener to an HTML element (e.g.  &amp;lt;span&amp;gt; )to detect for mouse clicks. In this event, it will replace the element with a drop down list populated with a collection of values, e.g. Low, Medium and High. Once the User has made a selection, the new value is transmitted to a grails action via an Ajax call. The action will update the relevant Issue object on the server with the new value. Finally the function will update the page by replacing the drop down list with the newly updated value.&lt;br /&gt;&lt;br /&gt;The function takes a number of arguments, the first of which specifies the id for the element that needs to be editable. In our case this is the &amp;lt;span&amp;gt; element. &lt;span style="font-weight: bold;"&gt;Note&lt;/span&gt; that this argument serves as the id of the editable element as well as the identifier for the Issue object that needs to be updated on the server. The second argument is the URL pointing to the grails action that will update the relevant Issue object on the server with the new priority value selected by the User. Thirdly we define a collection of items to be displayed in the drop down list when the User clicks on the editable field. These simply match the options available on the &lt;span style="font-style: italic;"&gt;create.gsp&lt;/span&gt; page, when a User creates an Issue. Lastly we specify the HTML transport method.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 3: Implement the grails action&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 200px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;def updatePriority = {&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if(params.editorId)&lt;br /&gt;{&lt;br /&gt;  Issue issue = Issue.get(params.editorId)&lt;br /&gt;&lt;br /&gt;  issue.priority = params.value&lt;br /&gt;&lt;br /&gt;  issue.save()&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;render params.value&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This is quite a simple action. It takes the id for the selected Issue, retrieves its corresponding object and then resets its priority value. The updated Issue object is then persisted. Finally the action returns the newly updated value to be displayed on the page. In principle this action can validate any new data provided by the User, but in this example there is no requirement to do so since the User is restricted to a list of options.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 4: Apply styling via css&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The final step is to apply css styling,&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 70px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;pre&gt;span.editInPlace {&lt;br /&gt;color: blue;&lt;br /&gt;background:#efefef;&lt;br /&gt;border-bottom: 1px solid black&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid ; background: black none repeat scroll 0% 0%; height: 1px; width: 800px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The page &lt;span style="font-style: italic;"&gt;list.gsp&lt;/span&gt; now looks like this&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_aesd62-K5Pg/SW5Z5YziOsI/AAAAAAAAAGI/JfnDb4LTBcg/s1600-h/after_start.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 117px;" src="http://4.bp.blogspot.com/_aesd62-K5Pg/SW5Z5YziOsI/AAAAAAAAAGI/JfnDb4LTBcg/s400/after_start.png" alt="" id="BLOGGER_PHOTO_ID_5291265454652930754" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note that the styling on the priority fields allows the User to discern which fields can be edited inline or not. Now when the User clicks on a priority value, e.g. for the first record, this field is replaced by a drop down list from which the User can select a new value, as shown below&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_aesd62-K5Pg/SW5aC7kofBI/AAAAAAAAAGQ/E67YqV4_cDM/s1600-h/after_dropdown.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 109px;" src="http://4.bp.blogspot.com/_aesd62-K5Pg/SW5aC7kofBI/AAAAAAAAAGQ/E67YqV4_cDM/s400/after_dropdown.png" alt="" id="BLOGGER_PHOTO_ID_5291265618604489746" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The drop down list is accompanied by two buttons, one for submitting the change, and the other to cancel the operation. The image below shows the result when the User has selected a priority of 'High' before clicking on the 'ok' button. This has updated the corresponding Issue object on the server and the value on the page.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_aesd62-K5Pg/SW5aS7DI5II/AAAAAAAAAGY/STapcts2lbQ/s1600-h/after_final.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 116px;" src="http://4.bp.blogspot.com/_aesd62-K5Pg/SW5aS7DI5II/AAAAAAAAAGY/STapcts2lbQ/s400/after_final.png" alt="" id="BLOGGER_PHOTO_ID_5291265893341914242" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately this not the end of the story. The images shown above were viewed under FireFox. Attempting to do the same thing with IE6 results in a viewing problem, where the Priority field is not visible once it's been clicked on.  This defect is attributed to a &lt;a href="http://prototype.lighthouseapp.com/projects/8887/tickets/25-ajax-inplacecollectioneditor-in-ie-error-on-creation"&gt;bug&lt;/a&gt; within earlier versions of the scriptaculous library but has been rectified for the most recent versions of the api. However the versions of the javascript libraries bundled with grails are not the most recent and are still susceptible to this defect. If I recall correctly, even grails 1.1 still employs version 1.8.0 for scriptaculous and 1.6.0 for prototype. This raises an interesting question as to when these libraries will be updated for the grails distribution.&lt;br /&gt;&lt;br /&gt;In the meantime, there are two possible solutions to this problem. The most obvious is to manually upgrade the javascript libraries supplied with grails to the most recent versions. However it's not readily apparent what implications this would have in terms of incompatibilities, since some grails code such as the Ajax tags depend on these libraries. It may be the case that such an approach would have no significant impact, but it is difficult to adopt this solution without further investigation.&lt;br /&gt;&lt;br /&gt;Alternatively and this is the approach I've taken, is to apply the fix documented &lt;a href="http://prototype.lighthouseapp.com/projects/8887/tickets/25-ajax-inplacecollectioneditor-in-ie-error-on-creation"&gt;here&lt;/a&gt;. The solution requires us to replace this line in control.js [line 761]&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="keywords"&gt;var&lt;/span&gt; list = &lt;span class="global"&gt;document&lt;/span&gt;.createElement&lt;span class="brackets"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'select'&lt;/span&gt;&lt;span class="brackets"&gt;)&lt;/span&gt;;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;with this&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="javascript"&gt;list=&lt;span class="keywords"&gt;new&lt;/span&gt; Element&lt;span class="brackets"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'select'&lt;/span&gt;&lt;span class="brackets"&gt;)&lt;/span&gt;;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Redeploying the app and viewing it under IE6 shows that the problem has indeed disappeared.&lt;br /&gt;&lt;br /&gt;On a final note, we could tidy up the code by abstracting the javascript code into a custom grails tag in a similar fashion to the inline editing example given by Graeme Rocher in the first edition of his excellent &lt;a href="http://www.amazon.co.uk/Definitive-Guide-Grails-Experts-Voice/dp/1590597583/ref=sr_1_2?ie=UTF8&amp;amp;s=books&amp;amp;qid=1232032857&amp;amp;sr=8-2"&gt;book&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-7204143878507412246?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/GKY4Kxx02hI/grails-inline-editing-for-collection.html</link><author>noreply@blogger.com (Mo Sayed)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_aesd62-K5Pg/SW4TNl1rv5I/AAAAAAAAAFw/UyQHW4IHlMc/s72-c/createIssueForm.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2009/01/grails-inline-editing-for-collection.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-5179163029994751354</guid><pubDate>Thu, 18 Dec 2008 14:01:00 +0000</pubDate><atom:updated>2008-12-18T14:09:40.298Z</atom:updated><title>grails + ajax: chained selects</title><description>From time to time, I run into the following problem. A User has to fill in a form containing 2 drop down lists, and I need to populate the second one with options that depend on the selection made on the first select box. This problem, commonly known as 'Chained Selects' can be easily solved using a dusting of ajax magic.&lt;br /&gt;&lt;br /&gt;The principle behind the solution is reasonably straightforward and involves attaching a listener to an HTML element such as a select field. This listener detects events triggered when a User moves the mouse, clicks on an element, etc. In our case, we're interested in the event when the User makes a selection. The listener will then fire a javascript function to perform an ajax call to the server. This call will inform the server as to the selection made on the first select element. Based on this information, the Server will create an HTML snippet containing the second select element and populate it with the correct set of options. This snippet is then sent back as a response to the ajax function, which updates the page accordingly.&lt;br /&gt;&lt;br /&gt;To demonstrate this feature, I'm using a simple example containing 2 domain classes, Family and Member. These are listed below,&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 380px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;class Family{&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;String surname&lt;br /&gt;&lt;br /&gt;String description&lt;br /&gt;&lt;br /&gt;static hasMany = [members : Member]&lt;br /&gt;&lt;br /&gt;static constraints = {&lt;br /&gt;surname(blank:false)&lt;br /&gt;description(blank:false)&lt;br /&gt;members(nullable:true)&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;String toString()&lt;br /&gt;{&lt;br /&gt;return surname&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;and,&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 380px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;class Member{&lt;br /&gt;&lt;pre&gt; &lt;br /&gt; String firstName&lt;br /&gt; &lt;br /&gt; String lastName&lt;br /&gt;&lt;br /&gt; String username&lt;br /&gt; &lt;br /&gt; static belongsTo = Family&lt;br /&gt; &lt;br /&gt; Family family&lt;br /&gt;&lt;br /&gt; static constraints = {&lt;br /&gt;  firstName(blank:false)&lt;br /&gt;  lastName(blank:false)&lt;br /&gt;  username(blank:false)&lt;br /&gt;  family(nullable:true)&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; String toString()&lt;br /&gt; {&lt;br /&gt;  return lastName + ", " + firstName&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_aesd62-K5Pg/SUpPVrh7LDI/AAAAAAAAAFQ/GQ4rr21nSBk/s1600-h/selectBefore.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 400px; height: 180px;" src="http://2.bp.blogspot.com/_aesd62-K5Pg/SUpPVrh7LDI/AAAAAAAAAFQ/GQ4rr21nSBk/s400/selectBefore.png" alt="" id="BLOGGER_PHOTO_ID_5281120746926189618" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The example revolves around 2 drop down lists. The first of these lists families, e.g. Simpsons and Griffins [Family Guy]. Now I want the second drop down list to be correctly populated with the family members depending on the selection made to the first drop down list.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 1: &lt;span style="font-style: italic;"&gt;Import 'prototype'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We're going to use prototype to implement this feature. Note you're not restricted to using this library. Others such as jQuery will do an equally impressive job. So the first step is to import in the library as follows,&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 20px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;       &amp;lt;g:javascript library="prototype"/&amp;gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 2: &lt;span style="font-style: italic;"&gt;Implement 2 drop down lists&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Next we need to implement the 2 select elements as so,&lt;br /&gt;&lt;br /&gt;&lt;div   style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 680px; height: 300px; color: rgb(0, 0, 0); text-align: left;font-family:courier;font-size:12px;"&gt;&amp;lt;td&amp;gt;&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&amp;lt;label for="family"&amp;gt;Family:&amp;lt;/label&amp;gt;&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;td&amp;gt;          &lt;br /&gt;&amp;lt;g:select id="&lt;span style="color: rgb(255, 0, 0);"&gt;familySelect&lt;/span&gt;" from="${Family.list().surname}" name="familySelected" value=""&amp;gt;&lt;br /&gt;&amp;lt;/g:select&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;td valign="top" class="name"&amp;gt;&lt;br /&gt; &amp;lt;label for="member"&amp;gt;Member:&amp;lt;/label&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;         &lt;br /&gt;&lt;br /&gt;&amp;lt;td&amp;gt;&lt;br /&gt;&amp;lt;div id="&lt;span style="color: rgb(255, 0, 0);"&gt;memberSelect&lt;/span&gt;"&amp;gt;&lt;br /&gt; &amp;lt;g:select from="${Member.list()}" name="memberSelected" value=""&amp;gt;&lt;br /&gt;&amp;lt;/g:select&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 3: &lt;span style="font-style: italic;"&gt;Hook up the ajax code&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div   style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 680px; height: 250px; color: rgb(0, 0, 0); text-align: left;font-family:courier;font-size:12px;"&gt;&lt;pre&gt;&amp;lt;g:javascript&amp;gt;&lt;br /&gt;document.observe('dom:loaded', function() {&lt;br /&gt;   $("&lt;span style="color: rgb(255, 0, 0);"&gt;familySelect&lt;/span&gt;").observe("change", respondToSelect);&lt;br /&gt;   });&lt;br /&gt;&lt;br /&gt;   &lt;br /&gt; function respondToSelect(event)&lt;br /&gt; {&lt;br /&gt;       new Ajax.Updater("&lt;span style="color: rgb(255, 0, 0);"&gt;memberSelect&lt;/span&gt;",&lt;br /&gt;          "/chainedSelect/family/updateSelect",&lt;br /&gt;          {method:'get', parameters: {selectedValue : $F("&lt;span style="color: rgb(255, 0, 0);"&gt;familySelect&lt;/span&gt;")} }&lt;br /&gt;         );&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;&amp;lt;/g:javascript&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Here we do a couple of things; first of all we attach a listener to the 'f&lt;span style="font-style: italic;"&gt;amilySelect&lt;/span&gt;' element to detect for any '&lt;span style="font-style: italic;"&gt;change&lt;/span&gt;' events fired when a User makes a selection on this element. Secondly we implement the ajax function that will be triggered when such an event is detected. In this instance we're using prototype's &lt;span style="font-style: italic;"&gt;Ajax.Updater()&lt;/span&gt; function, which takes a number of arguments that are fully documented &lt;a href="http://www.prototypejs.org/api/ajax/updater"&gt;elsewhere&lt;/a&gt;.  The first argument is the id of the element that will be updated by the response to the ajax function. In our case this will be a second drop down list. The second argument is a url pointing to a grails action to service our ajax request. Finally we have a set of optional parameters, the important one being '&lt;span style="font-style: italic;"&gt;selectedValue&lt;/span&gt;' which will hold the selection made by the User on the first drop down list.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 4: &lt;span style="font-style: italic;"&gt;Write the  grails action&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 920px; height: 120px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;def updateSelect = {&lt;br /&gt;&lt;pre&gt;     def familySelected = Family.find("from Family as family where family.surname=:surname", [surname:params.selectedValue])&lt;br /&gt;&lt;br /&gt;render (template:"selectMember", model : ['familySelected' : familySelected])&lt;br /&gt;  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;This the grails action that will service the ajax call. Quite simple really. It takes the value selected from the Family dropdown list and uses it to retrieve the corresponding Domain object. This object is then supplied to a tempate file that is subsequently returned as a response to the ajax function in step 3.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 5: &lt;span style="font-style: italic;"&gt;Write the HTML to update the page.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 80px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&amp;lt;g:select                     &lt;br /&gt;&lt;pre&gt;      from="${familySelected.members}"&lt;br /&gt;   name="memberSelected" value=""&amp;gt;&lt;br /&gt;&amp;lt;/g:select&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;This is the HTML that will be used to update the page by the ajax function defined above. Note that since I'm using templates, I need to save this in a file whose name should be prefixed with an underscore, '_'.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_aesd62-K5Pg/SUo5WqHAuWI/AAAAAAAAAFI/oI-ZK9zLJ44/s1600-h/selectExample.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 400px; height: 131px;" src="http://3.bp.blogspot.com/_aesd62-K5Pg/SUo5WqHAuWI/AAAAAAAAAFI/oI-ZK9zLJ44/s400/selectExample.png" alt="" id="BLOGGER_PHOTO_ID_5281096574468929890" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;And here is the final result.&lt;br /&gt;When we select 'Simpsons' as the Family, the second drop down is correctly populated with the members of the Simpson family.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_aesd62-K5Pg/SUpWJ4GjdzI/AAAAAAAAAFY/uAuYKLxDoZo/s1600-h/selectExample2.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: left; cursor: pointer; width: 400px; height: 117px;" src="http://3.bp.blogspot.com/_aesd62-K5Pg/SUpWJ4GjdzI/AAAAAAAAAFY/uAuYKLxDoZo/s400/selectExample2.png" alt="" id="BLOGGER_PHOTO_ID_5281128240724014898" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Conversely, if we select 'Griffin' as the family, the second drop down list is populated with the members of the Griffin family.&lt;br /&gt;&lt;br /&gt;As a final note, this example could be improved by displaying a default set of values for both selects, when the page is first loaded.&lt;br /&gt;&lt;br /&gt;You can download the code [chainSelectFamily.zip] from &lt;a href="http://sites.google.com/site/moongrailscode/"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-5179163029994751354?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/slgJJmSa4uw/grails-ajax-chained-select.html</link><author>noreply@blogger.com (Mo Sayed)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_aesd62-K5Pg/SUpPVrh7LDI/AAAAAAAAAFQ/GQ4rr21nSBk/s72-c/selectBefore.png" height="72" width="72" /><thr:total>9</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2008/12/grails-ajax-chained-select.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-5367567755958415461</guid><pubDate>Mon, 15 Dec 2008 16:02:00 +0000</pubDate><atom:updated>2008-12-16T14:45:50.053Z</atom:updated><title>grails: Implementing Search [2]</title><description>In a previous &lt;a href="http://moongrails.blogspot.com/2008/12/grails-implementing-search.html"&gt;post&lt;/a&gt;, I described a quick start approach to using the &lt;a href="http://grails.org/Searchable+Plugin"&gt;Searchable Plugin&lt;/a&gt;. This approach relied on the controller and view supplied with the plugin and is useful for prototyping and testing. However it's not the ideal way to implement search using the plugin.&lt;br /&gt;&lt;br /&gt;In this post, I demonstrate how you can customise the Searchable plugin by developing your own controller and views.&lt;br /&gt;&lt;br /&gt;The Searchable plugin provides us with 2 distinct ways to search the properties of domain classes. The first of these is the dynamic 'search' method&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 20px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;pre&gt;DomainClass.search(String query)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;which is attached to Domain classes that have the static property '&lt;span style="font-style: italic;"&gt;Searchable&lt;/span&gt;' defined within them. This restricts the search to only the properties of the Domain Class.&lt;br /&gt;&lt;br /&gt;Secondly the plugin provides the SearchableService class,&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 20px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;searchableService.search(String query)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;which enables us to search properties across a set of Domain classes. The documentation &lt;a href="http://www.grails.org/Searchable+Plugin+-+Methods+-+search#Returns"&gt;here&lt;/a&gt; provides further details for both approaches.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 1-3: &lt;span style="font-style: italic;"&gt;Setup&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Repeat Steps 1,2  &amp;amp; 3 from the previous &lt;a href="http://moongrails.blogspot.com/2008/12/grails-implementing-search.html"&gt;post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I'm using a different example for this post to better demonstrate the similarities and differences between the 2 search mechanisms. This example consists of 2 domain classes listed below and whose purpose is self-explanatory,&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 250px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;class Film{&lt;pre&gt;    String actor&lt;br /&gt;&lt;br /&gt;String title&lt;br /&gt;&lt;br /&gt;String releaseDate&lt;br /&gt;&lt;br /&gt;static Searchable = true&lt;br /&gt;&lt;br /&gt;static constraints = {&lt;br /&gt;           actor()&lt;br /&gt;           title()&lt;br /&gt;           releaseDate()&lt;br /&gt;         }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;and,&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 300px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;class Music{&lt;br /&gt;&lt;pre&gt;    String artist&lt;br /&gt;&lt;br /&gt;String title&lt;br /&gt;&lt;br /&gt;String releaseDate&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;static Searchable = true&lt;br /&gt;&lt;br /&gt;static constraints = {&lt;br /&gt;           artist()&lt;br /&gt;           title()&lt;br /&gt;           releaseDate()&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;I've implemented step 3 slightly differently. Instead of 1 text-field, we now have the following,&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 820px; height: 200px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;pre&gt;&amp;lt;g:form url='[controller: "customSearch", action: "searchMusic"]' id="searchMusic" name="searchMusic" method="get"&amp;gt;&lt;br /&gt;&amp;lt;g:textField name="query" value="${params.query}" size="10"/&amp;gt; &amp;lt;input type="submit" value="Search Music" /&amp;gt;&lt;br /&gt;&amp;lt;/g:form&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;g:form url='[controller: "customSearch", action: "searchFilm"]' id="searchFilm" name="searchFilm" method="get"&amp;gt;&lt;br /&gt;&amp;lt;g:textField name="query" value="${params.query}" size="10"/&amp;gt; &amp;lt;input type="submit" value="Search Film" /&amp;gt;&lt;br /&gt;&amp;lt;/g:form&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;g:form url='[controller: "customSearch", action: "searchAll"]' id="searchAll" name="searchAll" method="get"&amp;gt;&lt;br /&gt;&amp;lt;g:textField name="query" value="${params.query}" size="10"/&amp;gt; &amp;lt;input type="submit" value="Search All" /&amp;gt;&lt;br /&gt;&amp;lt;/g:form&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Which look like,&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_aesd62-K5Pg/SUeiDG-23CI/AAAAAAAAAEo/Xq1gyHZcsR0/s1600-h/searchFields.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 42px;" src="http://4.bp.blogspot.com/_aesd62-K5Pg/SUeiDG-23CI/AAAAAAAAAEo/Xq1gyHZcsR0/s400/searchFields.png" alt="" id="BLOGGER_PHOTO_ID_5280367262412758050" border="0" /&gt;&lt;/a&gt;&lt;/pre&gt;&lt;br /&gt;These text-fields, allow the User to search individually for Music or Film or indeed collectively across both categories.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 4: &lt;span style="font-style: italic;"&gt;Implement the Controller&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Next, we build our custom search controller, containing actions that will service the text-fields defined in the forms above.&lt;br /&gt;&lt;br /&gt;&lt;div   style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 1300px; color: rgb(0, 0, 0); text-align: left;font-family:courier;font-size:12px;"&gt;&lt;br /&gt;import org.compass.core.engine.SearchEngineQueryParseException&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class CustomSearchController{&lt;br /&gt;&lt;br /&gt;     def &lt;span style="color: rgb(255, 0, 0);"&gt;searchableService&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;     def searchMusic = {&lt;br /&gt;&lt;br /&gt;             if(params.query?.trim())&lt;br /&gt;             {&lt;br /&gt;&lt;br /&gt;               &lt;span style="color: rgb(255, 0, 0);"&gt;def searchResults = Music.search(params.query)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;               &lt;span style="color: rgb(255, 0, 0);"&gt;def total = searchResults.total&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;               &lt;span style="color: rgb(255, 0, 0);"&gt;def list = searchResults.results&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;               return [totalResults: total, list:list, type:"music"]&lt;br /&gt;              }&lt;br /&gt;              else&lt;br /&gt;              {&lt;br /&gt;                return [:]&lt;br /&gt;              }&lt;br /&gt;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;       def searchFilm = {&lt;br /&gt;&lt;br /&gt;           if(params.query?.trim())&lt;br /&gt;           {&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;                def searchResults = Film.search(params.query)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;              def total = searchResults.total&lt;br /&gt;&lt;br /&gt;              def list = searchResults.results&lt;br /&gt;&lt;br /&gt;              return [totalResults:total, list:list, type:"film"]&lt;br /&gt;           }&lt;br /&gt;           else&lt;br /&gt;           {&lt;br /&gt;               return [:]&lt;br /&gt;           }&lt;br /&gt;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;      def searchAll = {&lt;br /&gt;&lt;br /&gt;            if(!params.max) params.max = 5&lt;br /&gt;            if(!params.offset) params.offset = 0&lt;br /&gt;&lt;br /&gt;            if(params.query?.trim())&lt;br /&gt;            {&lt;br /&gt;              try&lt;br /&gt;              {&lt;br /&gt;&lt;br /&gt;                     &lt;span style="color: rgb(255, 0, 0);"&gt;def searchResult = searchableService.search(params.query, params)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;                     def total = searchResult.total&lt;br /&gt;&lt;br /&gt;                     def list  = searchResult.results&lt;br /&gt;&lt;br /&gt;                     return [totalResults:total, list:list, type:"all"]&lt;br /&gt;               }&lt;br /&gt;              catch (SearchEngineQueryParseException ex)&lt;br /&gt;              {&lt;br /&gt;                   return [parseException: true]&lt;br /&gt;               }&lt;br /&gt;            }&lt;br /&gt;           else&lt;br /&gt;           {&lt;br /&gt;              return [:]&lt;br /&gt;           }&lt;br /&gt;&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;At the top of the code, we inject in the plugins' SearchService class. Next we declare the actions required to service the search textfields defined in step 3. The first 2 actions implement search using the dynamic method, whereas the '&lt;span style="font-style: italic;"&gt;searchAll&lt;/span&gt;' action uses the &lt;span style="font-style: italic;"&gt;SearchableService&lt;/span&gt; class mechanism. Both approaches return a '&lt;span style="font-style: italic;"&gt;searchResult&lt;/span&gt;' object which contains a number of results data documented &lt;a href="http://www.grails.org/Searchable+Plugin+-+Methods+-+search#Returns"&gt;here&lt;/a&gt;. For our purposes, we're only interested in the search results and their count.&lt;br /&gt;&lt;br /&gt;I'm not going to list the view pages here as they're typical gsp pages and don't add any value to the current discussion. However if you're interested, the full code is available [compass2.zip] for download &lt;a href="http://sites.google.com/site/moongrailscode/"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The following images show the results from performing individual and collective searches.&lt;br /&gt;&lt;br /&gt;This one shows a 'Music' only search, when I enter a value of 'Will Smith' into the 's&lt;span style="font-style: italic;"&gt;earch Music&lt;/span&gt;' textfield.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_aesd62-K5Pg/SUej01w6ZEI/AAAAAAAAAEw/BDYMYiF1YMc/s1600-h/music.png"&gt;&lt;img style="cursor: pointer; width: 325px; height: 191px;" src="http://3.bp.blogspot.com/_aesd62-K5Pg/SUej01w6ZEI/AAAAAAAAAEw/BDYMYiF1YMc/s400/music.png" alt="" id="BLOGGER_PHOTO_ID_5280369216295953474" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This one shows a 'Film' only search, when I enter a value of 'Will Smith' into the 's&lt;span style="font-style: italic;"&gt;earch Film&lt;/span&gt;' textfield.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_aesd62-K5Pg/SUej_RU7egI/AAAAAAAAAE4/VP_ZRBsRplU/s1600-h/film.png"&gt;&lt;img style="cursor: pointer; width: 353px; height: 223px;" src="http://2.bp.blogspot.com/_aesd62-K5Pg/SUej_RU7egI/AAAAAAAAAE4/VP_ZRBsRplU/s400/film.png" alt="" id="BLOGGER_PHOTO_ID_5280369395493468674" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And finally a collective search, where I enter the value 'Will Smith' into the '&lt;span style="font-style: italic;"&gt;searchAll&lt;/span&gt;' text-field.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_aesd62-K5Pg/SUekHeryMcI/AAAAAAAAAFA/RuOZ_BRth_0/s1600-h/complete.png"&gt;&lt;img style="cursor: pointer; width: 375px; height: 311px;" src="http://1.bp.blogspot.com/_aesd62-K5Pg/SUekHeryMcI/AAAAAAAAAFA/RuOZ_BRth_0/s400/complete.png" alt="" id="BLOGGER_PHOTO_ID_5280369536517943746" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As expected the plugin returns Music and Films associated with the artist/actor, Will Smith.&lt;br /&gt;&lt;br /&gt;The complete code for this example [compass2.zip] is available &lt;a href="http://sites.google.com/site/moongrailscode/"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;ps - I noticed something peculiar with Searchable plugin, when using the test data that I generate in the bootstrap class. If I perform a query on 'Will', it returns nothing. Yet if I enter a value of  'Will Smith', then the search returns the correct results. However if I repeat this for 'Mark' and 'Mark Wahlberg', I get identical results, i.e. I get back, Music/Film or both depending on the search type.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-5367567755958415461?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/cgGSimJD-8c/grails-implementing-search-2.html</link><author>noreply@blogger.com (Mo Sayed)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_aesd62-K5Pg/SUeiDG-23CI/AAAAAAAAAEo/Xq1gyHZcsR0/s72-c/searchFields.png" height="72" width="72" /><thr:total>6</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2008/12/grails-implementing-search-2.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-790777252101274540</guid><pubDate>Fri, 12 Dec 2008 11:49:00 +0000</pubDate><atom:updated>2008-12-12T12:06:27.078Z</atom:updated><title>grails: ajax powered validation [Update]</title><description>In a previous &lt;a href="http://moongrails.blogspot.com/2008/12/grails-ajax-powered-validation.html"&gt;post&lt;/a&gt;, I outlined a method to perform textfield validation using jQuery. However a recent comment by Jan correctly pointed out a simpler method using grails' own ajax tag, &amp;lt;g:remoteField&amp;gt;.&lt;br /&gt;&lt;br /&gt;So to implement this feature in a simpler way do the following,&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 1: &lt;span style="font-style: italic;"&gt;Import scriptaculous&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 20px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&amp;lt;g:javascript library="scriptaculous"/&amp;gt; &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 2: &lt;span style="font-style: italic;"&gt;Code up the textfield and message div&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 800px; height: 120px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;&amp;lt;g:remoteField controller="member" action="validateUsername" paramName="username" name="username"&lt;br /&gt;&lt;br /&gt;              update="messageDiv" value="${fieldValue(bean:member,field:'username')}"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/g:remoteField&amp;gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;div id="messageDiv" style="color:blue"&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The message div will be populated with the ajax response, i.e. the uniqueness validation result.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 3: &lt;span style="font-style: italic;"&gt;Code up the grails action&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 350px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;  def validateUsername = {&lt;br /&gt;&lt;pre&gt;        def message = ""&lt;br /&gt;&lt;br /&gt;      def inUse = ["homer", "marge", "bart", "maggie", "lisa"]&lt;br /&gt;&lt;br /&gt;      if (params.username)&lt;br /&gt;      {&lt;br /&gt;          if(inUse.contains(params.username))&lt;br /&gt;          {&lt;br /&gt;              message = "The username has already been taken. Please enter a different one"&lt;br /&gt;          }&lt;br /&gt;          else&lt;br /&gt;          {&lt;br /&gt;              message = "Username Ok !...."&lt;br /&gt;          }&lt;br /&gt;      }     &lt;br /&gt;&lt;br /&gt;      render message&lt;br /&gt;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Not much change here other than the response messages.&lt;br /&gt;&lt;br /&gt;And that's it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-790777252101274540?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/CC8UZPmT1x0/grails-ajax-powered-validation-update.html</link><author>noreply@blogger.com (Mo Sayed)</author><thr:total>3</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2008/12/grails-ajax-powered-validation-update.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-533485702201961473</guid><pubDate>Thu, 11 Dec 2008 15:49:00 +0000</pubDate><atom:updated>2008-12-12T12:21:14.567Z</atom:updated><title>grails: ajax powered validation</title><description>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_aesd62-K5Pg/SUGH-B0VKkI/AAAAAAAAADo/ijrnbCP2Fh8/s1600-h/BasicForm.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 311px; height: 400px;" src="http://1.bp.blogspot.com/_aesd62-K5Pg/SUGH-B0VKkI/AAAAAAAAADo/ijrnbCP2Fh8/s400/BasicForm.png" alt="" id="BLOGGER_PHOTO_ID_5278649737964497474" border="0" /&gt;&lt;/a&gt;[The contents of this post have been superseded by this &lt;a href="http://moongrails.blogspot.com/2008/12/grails-ajax-powered-validation-update.html"&gt;post&lt;/a&gt;]&lt;br /&gt;&lt;br /&gt;I'm not keen on filling in forms, especially ones which are badly designed. To give you an idea of what I'm talking about, consider the typical registration form as shown.&lt;br /&gt;&lt;br /&gt;Here the User is expected to provide many personal details in addition to a username which must be unique. The catch is that the user won't know whether the value they have entered is unique until the form has been submitted.&lt;br /&gt;&lt;br /&gt;If the username is already in use, the form is redisplayed and the User is asked to provide an alternative.&lt;br /&gt;&lt;br /&gt;Now one could envisage situations where the User&lt;br /&gt;has many attempts at providing a valid username until they are successful. This would result in a number of redundant form submissions, page refreshes and clearly a frustrating experience for the User.&lt;br /&gt;&lt;br /&gt;One way to address this shortcoming, is to use ajax to perform a validation check. Specifically, once a User has entered a value into the textfield, an ajax call is made to the server requesting that the textfield content be validated. The server performs the check and returns a response which will be used to generate a message informing the User about the validity of the data they have just entered. This approach overcomes the need to perform pointless form submissions and page regeneration.&lt;br /&gt;&lt;br /&gt;To demonstrate how to implement this, I've developed a contrived example using jQuery's excellent support for ajax functionality.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 1: &lt;span style="font-style: italic;"&gt;Download &amp;amp; Import jQuery&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In this step you need to download &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; and place it in the web-app/js folder.&lt;br /&gt;Next you need to import it into the gsp page by placing the following statement between the &amp;lt;head&amp;gt;  tags, e.g.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 20px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&amp;lt;g:javascript library="jquery-1.2.6.min"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 2: &lt;span style="font-style: italic;"&gt;Setup the Textfield for validation.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Next we need to set up the textfield for validation. In this example I'm going to validate usernames.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 820px; height: 180px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&amp;lt;tr class="prop"&amp;gt;&lt;br /&gt;&lt;pre&gt;     &amp;lt;td valign="top" class="name"&amp;gt;&lt;br /&gt;    &amp;lt;label for="username"&amp;gt;Create Username:&amp;lt;/label&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;td valign="top" class="value ${hasErrors(bean:member,field:'username','errors')}"&amp;gt;&lt;br /&gt;    &amp;lt;input type="text" id="username" name="username" value="${fieldValue(bean:member,field:'username')}"/&amp;gt;&lt;br /&gt;    &amp;lt;div id="usernameMessageOk" style="color:green"&amp;gt;Username Ok !....&amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div id="usernameMessageFail"  style="color:red"&amp;gt;Please try again... That username is already in use&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Nothing difficult here. The only modifications I've made to the code generated by scaffolding  is to add 2 extra divs which will be used to inform the User about the success/failure of the validation. Note these divs are hidden by default as shown in the next section.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 3: &lt;span style="font-style: italic;"&gt;Hook up the ajax call&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In this step we set up jQuery's ajax function to perform the validation&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 1100px; height: 620px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;&amp;lt;g:javascript&amp;gt;&lt;br /&gt;&lt;pre&gt;    var $j = jQuery.noConflict();&lt;br /&gt;&lt;br /&gt;$j(document).ready( function()&lt;br /&gt;{&lt;br /&gt;  $j('#usernameMessageOk').hide()&lt;br /&gt;  $j("#usernameMessageFail").hide()&lt;br /&gt;&lt;br /&gt;  $j("#username").change(function()&lt;br /&gt;   {&lt;br /&gt;       var name=this.value&lt;br /&gt;&lt;br /&gt;       $j.ajax({&lt;br /&gt;                url: "/ajax_validation/member/validateUsername",&lt;br /&gt;                dataType: "text",&lt;br /&gt;                data: "username=" + this.value,&lt;br /&gt;                cache: false,&lt;br /&gt;                success:function(text)&lt;br /&gt;                  {&lt;br /&gt;                    if(text == 'ok')&lt;br /&gt;                    {&lt;br /&gt;                       $j('#usernameMessageOk').show();&lt;br /&gt;                       $j("#usernameMessageFail").hide()&lt;br /&gt;                       $j("#username").css({'background-color': 'yellow', 'color':'black' });                                   &lt;br /&gt;                     }&lt;br /&gt;                    else&lt;br /&gt;                     {&lt;br /&gt;                       $j("#usernameMessageFail").show();                                   &lt;br /&gt;                       $j('#usernameMessageOk').hide()&lt;br /&gt;                       $j("#username").css({'background-color': 'red', 'color':'white' , 'border':'solid 1px #0000ff'});                                   &lt;br /&gt;                     }&lt;br /&gt;                  }&lt;br /&gt;&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;});               &lt;br /&gt;&amp;lt;/g:javascript&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;There's quite a few things going on in this snippet. Firstly we hide the message div's using jQuery. Next we bind the username text-field to jQuery's .ajax() function. This will fire whenever the User moves focus away from the textfield e.g. by clicking elsewhere or hitting the 'tab' key.&lt;br /&gt;&lt;br /&gt;The ajax function makes a call to a grails action, 'validateUsername' which determines whether the username is in use or not. The Server's response will be used to determine which of the messages contained within the hidden div's are displayed.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 4: &lt;span style="font-style: italic;"&gt;Code up the grails action to validate the Text-field&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In this step we implement the grails action which will receive the text-field content and validate it accordingly.  For the purposes of this example I have used a simple list. You can easily replace this with code to inspect the database using GORM.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 300px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;def validateUsername = {&lt;br /&gt;&lt;pre&gt;       def message = "fail"&lt;br /&gt;&lt;br /&gt;  def inUse = ["homer", "marge", "bart", "maggie", "lisa"]&lt;br /&gt;&lt;br /&gt; if (params.username)&lt;br /&gt; {&lt;br /&gt;     if(inUse.contains(params.username))&lt;br /&gt;     {&lt;br /&gt;        message = "fail"&lt;br /&gt;     }&lt;br /&gt;     else&lt;br /&gt;     {&lt;br /&gt;         message = "ok"&lt;br /&gt;      }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; render message&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Quite simple really. The action will perform the uniqueness validation and return the result to the client.&lt;br /&gt;&lt;br /&gt;The two possible outcomes are shown below,&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_aesd62-K5Pg/SUGRFoRLmAI/AAAAAAAAAEA/3q6Ok4uKw0Y/s1600-h/invalidUsernameForm.png"&gt;&lt;img style="cursor: pointer; width: 396px; height: 400px;" src="http://1.bp.blogspot.com/_aesd62-K5Pg/SUGRFoRLmAI/AAAAAAAAAEA/3q6Ok4uKw0Y/s400/invalidUsernameForm.png" alt="" id="BLOGGER_PHOTO_ID_5278659764149786626" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;and,&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_aesd62-K5Pg/SUGRTfQ8AsI/AAAAAAAAAEI/luxd42zif7k/s1600-h/validUsernameForm.png"&gt;&lt;img style="cursor: pointer; width: 289px; height: 400px;" src="http://1.bp.blogspot.com/_aesd62-K5Pg/SUGRTfQ8AsI/AAAAAAAAAEI/luxd42zif7k/s400/validUsernameForm.png" alt="" id="BLOGGER_PHOTO_ID_5278660002251014850" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;That's almost it. One problem remains and that is if the User decides to hit the submit button when the username field has an invalid entry. To solve this simply alter your 'save' action as follows,&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 720px; height: 450px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;def save = {&lt;br /&gt;&lt;pre&gt;            def member = new Member(params)&lt;br /&gt;&lt;br /&gt;         def inUse = ["homer", "marge", "bart", "maggie", "lisa"]&lt;br /&gt;&lt;br /&gt;         //check that username is still available&lt;br /&gt;&lt;br /&gt;         if(inUse.contains(member.username))&lt;br /&gt;         {&lt;br /&gt;           member.errors.rejectValue('username', 'member.username.validator.invalid')&lt;br /&gt;           render(view:'create',model:[member:member])&lt;br /&gt;         }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;         if(!member.hasErrors() &amp;amp;&amp;amp; member.save())&lt;br /&gt;         {&lt;br /&gt;          flash.message = "Member ${member.id} created"&lt;br /&gt;          redirect(action:show,id:member.id)&lt;br /&gt;         }&lt;br /&gt;         else&lt;br /&gt;        {&lt;br /&gt;          render(view:'create',model:[member:member])&lt;br /&gt;        }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;where 'member.username.validator.invalid' is a custom error message defined in the message.properties file. The code above will perform another validity check and ensure that in the case of failure, the User is redirected to the form and our custom error is displayed.&lt;br /&gt;&lt;br /&gt;Finally this code could be improved by providing suggestions of usernames to the User.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-533485702201961473?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/BjvMSeBvwP8/grails-ajax-powered-validation.html</link><author>noreply@blogger.com (Mo Sayed)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_aesd62-K5Pg/SUGH-B0VKkI/AAAAAAAAADo/ijrnbCP2Fh8/s72-c/BasicForm.png" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2008/12/grails-ajax-powered-validation.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-208551062574173499</guid><pubDate>Fri, 05 Dec 2008 15:00:00 +0000</pubDate><atom:updated>2008-12-05T17:31:14.972Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">grails install-plugin searchable</category><title>grails: Implementing Search</title><description>The ability for a User to perform searches for entries in the database is a critical requirement for most web apps. Implementing such a feature from scratch can be a rather daunting task. Thankfully there are open source solutions that alleviate the need to build your own search engine. Two of these, Lucene and Compass are bundled into a grails plug-in, enabling you to provide a powerful and flexible search capability for your Users.&lt;br /&gt;&lt;br /&gt;Implementing search using this plugin is actually quite simple, as I will demonstrate in this post.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 1: &lt;span style="font-style: italic;"&gt;Download &amp;amp; Install Searchable plugin&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The first step is to download and install the &lt;a href="http://grails.org/Searchable+Plugin"&gt;Searchable plugin&lt;/a&gt;. Navigate to your project folder and issue the following command from a console window.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 20px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;grails install-plugin searchable &lt;pre&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Please note this assumes you are using JDK1.5+. If however, you're still using JDK1.4, then invoke the above command ensuring that the term 'searchable' is replaced with 'searchable14'.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 2: &lt;span style="font-style: italic;"&gt;Modify Domain class&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Open up your Domain class and add the statement '&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(255, 0, 0); font-style: italic;font-family:courier new;" &gt;static searchable = true&lt;/span&gt;&lt;/span&gt; '. In the example below, my domain class is called Issue and is a simplification of an actual class that I'm developing for a pet project. This flag enables the plugin to identify which records are available for search.&lt;br /&gt;&lt;br /&gt;&lt;div   style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 280px; color: rgb(0, 0, 0); text-align: left;font-family:courier;font-size:12px;"&gt;&lt;pre&gt;class Issue{&lt;br /&gt;&lt;br /&gt;String title&lt;br /&gt;String description&lt;br /&gt;String status&lt;br /&gt;String priority&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;static searchable = true&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;static constraints = {&lt;br /&gt;title()&lt;br /&gt;description()&lt;br /&gt;status(inList : ["UNASSIGNED", "ASSIGNED", "CLOSED"])&lt;br /&gt;priority(inList: ["TRIVIAL", "MINOR", "MAJOR", "CRITICAL", "BLOCKER"])&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;String toString(){&lt;br /&gt;return title&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 3: &lt;span style="font-style: italic;"&gt;Create Search Textfield.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The next step is to provide a search box into which the User can enter search parameters. I do this by creating a search textfield like the one below,&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 700px; height: 100px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;&amp;lt;g:form url='[controller: "searchable", action: "index"]' id="searchableForm" name="searchableForm" method="get"&amp;gt;&lt;br /&gt;&lt;pre&gt;      &amp;lt;g:textField name="q" value="${params.q}" size="15"/&amp;gt; &amp;lt;input type="submit" value="Search" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/g:form&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The textfield is embedded within a form that will pass its contents to an 'index' action on the plugins 'search' controller, either when the User clicks on the 'search' button or hits the 'enter' key.&lt;br /&gt;&lt;br /&gt;To show what this would look like, I've knocked up a simple web app using the Issue class above. We now have something that looks like this&lt;br /&gt;&lt;pre&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_aesd62-K5Pg/STkjLMGJoZI/AAAAAAAAADQ/rOitSbKQMLE/s1600-h/search.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 197px;" src="http://1.bp.blogspot.com/_aesd62-K5Pg/STkjLMGJoZI/AAAAAAAAADQ/rOitSbKQMLE/s400/search.png" alt="" id="BLOGGER_PHOTO_ID_5276287113573802386" border="0" /&gt;&lt;/a&gt;&lt;/pre&gt;&lt;br /&gt;You can clearly see the search box at the top of  the navigation bar. I've used the bootstrap class to create some test data which are shown in the table. This data will enable me to demonstrate that the search capability works and returns the expected results.&lt;br /&gt;&lt;br /&gt;Entering a value of "CLOSED" into the search box returns the following response,&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_aesd62-K5Pg/STkj-XZnEoI/AAAAAAAAADY/-3c1cfBYJ68/s1600-h/search_result1.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 66px;" src="http://4.bp.blogspot.com/_aesd62-K5Pg/STkj-XZnEoI/AAAAAAAAADY/-3c1cfBYJ68/s400/search_result1.png" alt="" id="BLOGGER_PHOTO_ID_5276287992781542018" border="0" /&gt;&lt;/a&gt;&lt;/pre&gt; Although the plugin has successfully returned the expected results, the presentation of the data leaves a lot to be desired. Our application no longer looks consistent. Every time a User performs a search they'll end up with a page that clearly doesn't look right and they'll have to click on the browser back button to navigate somewhere else. The next step should fix this.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 4: &lt;span style="font-style: italic;"&gt;Edit View for Search Results&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Create a new folder 'searchable' under grails-app/views.&lt;br /&gt;&lt;br /&gt;In this folder we want to place a copy of the view used by the plugin to display the results. The copy will subsequently be modified to generate a view that is consistent with our web application. So copy the file &lt;span style="font-style: italic;"&gt;web-app/plugins/searchable-0.5.1/grails-app/views/searchable/index.gsp &lt;/span&gt;&lt;span&gt;into the newly created folder. Here /web-app/ is the name of your project and searchable-0.5.1 refers to the version of the plugin that is in use. For my case, this happens to be 0.5.1. Check the version that you have downloaded and adjust accordingly.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Next we modify index.gsp in the following way,&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Delete all the css styling defined within the &amp;lt;head&amp;gt; tags&lt;/li&gt;&lt;li&gt;Place the following meta-tag between the &amp;lt;head&amp;gt; tags&lt;/li&gt;&lt;/ol&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 20px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&amp;lt;meta name="layout" content="main"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Following these two steps should be sufficient to generate a results view that is consistent with your application. However for my application, I had to perform an extra step and rename the id's for the &amp;lt;header&amp;gt; and &amp;lt;main&amp;gt; divs.  This was to prevent a conflict with css styling from my own style sheet.&lt;br /&gt;&lt;br /&gt;The end result now looks like&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_aesd62-K5Pg/STknXqmNwPI/AAAAAAAAADg/jrGKNUhcRbI/s1600-h/search_result2.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 204px;" src="http://3.bp.blogspot.com/_aesd62-K5Pg/STknXqmNwPI/AAAAAAAAADg/jrGKNUhcRbI/s400/search_result2.png" alt="" id="BLOGGER_PHOTO_ID_5276291725966295282" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This completes the tutorial, which I hope you will find useful. My intention was to enable you to get started quickly and in that respect the post has not and could not cover the full power of this fantastic plugin. That may be a task for another day.&lt;br /&gt;&lt;br /&gt;The example code demonstrated here is available for download [compass.zip] from &lt;a href="http://sites.google.com/site/moongrailscode/Home?previewAsViewer=1"&gt;here&lt;/a&gt;. Please note that this is missing the plugin. I had to delete that in order to comply with the File Upload limit. However this shouldn't be a problem if you follow the installation instructions given above.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-208551062574173499?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/fE2ehEEkQKs/grails-implementing-search.html</link><author>noreply@blogger.com (Mo Sayed)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_aesd62-K5Pg/STkjLMGJoZI/AAAAAAAAADQ/rOitSbKQMLE/s72-c/search.png" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2008/12/grails-implementing-search.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-915388556075280887</guid><pubDate>Tue, 02 Dec 2008 10:18:00 +0000</pubDate><atom:updated>2008-12-04T13:50:00.726Z</atom:updated><title>grails: Templates</title><description>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_aesd62-K5Pg/STepE2cRwYI/AAAAAAAAAC4/t-wl3os5RIc/s1600-h/blog_layout.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 400px; height: 300px;" src="http://2.bp.blogspot.com/_aesd62-K5Pg/STepE2cRwYI/AAAAAAAAAC4/t-wl3os5RIc/s400/blog_layout.jpg" alt="" id="BLOGGER_PHOTO_ID_5275871389286515074" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Previously I've used Tiles to provide a common look and feel to web applications.&lt;br /&gt;&lt;br /&gt;Template frameworks like Tiles achieve this effect by enabling developers to define page fragments which are then assembled into a complete page at run time. This approach is quite appealing since the fragments are by nature reusable view components, thereby enhancing maintainability and reducing development time.&lt;br /&gt;&lt;br /&gt;Grails uses 'SiteMesh' to perform this function, and does so in a simple and flexible way. No XML configuration here !!!&lt;br /&gt;&lt;br /&gt;A typical page layout is displayed in the figure on the left.  This is made up from the following view components. The topbar will display time/date and a welcome message identifying the user, and potentially a 'logout' button. The header will showcase the name of the application, and the navbar will contain a menu for navigation. The central panel will contain the content to be displayed to the user. Finally the footer will hold information such as Terms &amp;amp; Conditions, a Copyright notice etc.&lt;br /&gt;&lt;br /&gt;To implement the layout shown here execute the following steps,&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 1&lt;/span&gt;: &lt;span style="font-weight: bold; font-style: italic;"&gt;start&lt;/span&gt;&lt;br /&gt;You need a project, ideally populated with at least a single domain class with it's associated controllers and views. The picture below shows the default layout generated by grails.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_aesd62-K5Pg/STeptkDoGkI/AAAAAAAAADA/N0SZrKFE1DE/s1600-h/default.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 54px;" src="http://4.bp.blogspot.com/_aesd62-K5Pg/STeptkDoGkI/AAAAAAAAADA/N0SZrKFE1DE/s400/default.png" alt="" id="BLOGGER_PHOTO_ID_5275872088725920322" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 2&lt;/span&gt;: &lt;span style="font-weight: bold;"&gt;&lt;span style="font-style: italic;"&gt;create re-usable templates&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;With the exception of the header and content fragments, we start by creating the templates for the remaining view elements. Create a folder called 'common' under the views/ folder. In this folder create the following files&lt;br /&gt;&lt;ul&gt;&lt;li&gt;_topbar.gsp&lt;/li&gt;&lt;li&gt;_navbar.gsp&lt;/li&gt;&lt;li&gt;_footer.gsp&lt;/li&gt;&lt;/ul&gt;Note that these files should start with an underscore, '_' and should not contain any HTML tags such as &amp;lt;head&amp;gt;, &amp;lt;body&amp;gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 3:&lt;/span&gt; &lt;span style="font-weight: bold; font-style: italic;"&gt;populate _topbar.gsp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div   style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 120px; color: rgb(0, 0, 0); text-align: left;font-family:courier;font-size:12px;"&gt;&amp;lt;div id="welcome"&amp;gt;&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;      12th June 2008&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;div id="menu"&amp;gt;&lt;br /&gt;Welcome Ned Flanders | &amp;lt;a href="#"&amp;gt;logout&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 4: &lt;span style="font-style: italic;"&gt;populate _navbar.gsp&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 1000px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;pre&gt;&amp;lt;div class='navbarBox'&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div class="navcontainer1"&amp;gt;&lt;br /&gt;&amp;lt;h1 class="panelHeader"&amp;gt; &lt;br /&gt; Users&lt;br /&gt;&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;lt;ul class="navlist"&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&lt;br /&gt;  &amp;lt;g:link controller="user" action="list"&amp;gt;&lt;br /&gt;   List Users&lt;br /&gt;  &amp;lt;/g:link&amp;gt;&lt;br /&gt; &amp;lt;/li&amp;gt; &lt;br /&gt; &amp;lt;li&amp;gt;&lt;br /&gt;  &amp;lt;g:link controller="user" action="create"&amp;gt;&lt;br /&gt;    Add User&lt;br /&gt;  &amp;lt;/g:link&amp;gt;&lt;br /&gt; &amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&lt;br /&gt;  &amp;lt;g:link controller="user" action="edit"&amp;gt;&lt;br /&gt;   Edit User&lt;br /&gt;  &amp;lt;/g:link&amp;gt;&lt;br /&gt; &amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li style="color:grey"&amp;gt;&amp;lt;a href="#"&amp;gt;Remove User&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;/ul&amp;gt;   &lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div class="navbarBox"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div class="navcontainer1"&amp;gt;&lt;br /&gt;&amp;lt;h1 class="panelHeader"&amp;gt; &lt;br /&gt; Actions&lt;br /&gt;&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;lt;ul class="navlist"&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;do something 1&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;do something 2&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;do something 3&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;do something 4&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;do something 5&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;/ul&amp;gt;   &lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div class='navbarBox'&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div class="navcontainer1"&amp;gt;&lt;br /&gt;&amp;lt;h1 class="panelHeader"&amp;gt; &lt;br /&gt; Leisure&lt;br /&gt;&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;lt;ul class="navlist"&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Activity 1&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Activity 2&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Activity 3&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Activity 4&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Activity 5&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;/ul&amp;gt;   &lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 5: &lt;span style="font-style: italic;"&gt;populate _footer.gsp&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div face="courier" size="12px" style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 75px; color: rgb(0, 0, 0); text-align: left;"&gt;&lt;pre&gt;&amp;lt;span class="copyright"&amp;gt; © 2008&amp;lt;/span&amp;gt;&lt;br /&gt;&amp;lt;span class="tandc"&amp;gt; &amp;lt;a href="#"&amp;gt;Terms &amp;amp; Conditions &amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 6: &lt;span style="font-style: italic;"&gt;modify main.gsp&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Once the reusable view components have been defined, we include them into the layout. Open the file &lt;span style="font-style: italic;"&gt;/views/layout/main.gsp&lt;/span&gt; and replace all the code between the &amp;lt;body&amp;gt; tags with the following lines of code&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 350px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;pre&gt;&amp;lt;div id="page"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div id="topbar"&amp;gt;&lt;br /&gt;    &amp;lt;g:render template="/common/topbar" /&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div id='header'&amp;gt;&lt;br /&gt;    &amp;lt;h1 style="font-size:62px;text-align:center;"&amp;gt; Moongrails &amp;lt;/h1&amp;gt;   &lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div id="content"&amp;gt; &lt;br /&gt;    &amp;lt;g:layoutBody /&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div id='navbar'&amp;gt;&lt;br /&gt;    &amp;lt;g:render template="/common/navbar" /&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div id="footer"&amp;gt;&lt;br /&gt;    &amp;lt;g:render template="/common/footer" /&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/div&amp;gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The content in the central fragment will be populated by the gsp pages in the various view folders.  When the user navigates to any particular page, e.g. /user/list.gsp, grails imports in the contents of that page and inserts it into the content div via the &lt;span style="font-family:monospace;"&gt;&lt;/span&gt; &amp;lt;g:layoutBody /&amp;gt; tag.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 7: &lt;span style="font-style: italic;"&gt;modify main.css&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The final step is to code up the styling for the various templates.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 2800px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;pre&gt;/* --------------------------------------*/&lt;br /&gt;/* PAGE container                        */&lt;br /&gt;/* --------------------------------------*/&lt;br /&gt;/* Contains page contents e.g header,navbar, content, footer */&lt;br /&gt;#page {&lt;br /&gt;width: 90%;&lt;br /&gt;margin: 0px auto;&lt;br /&gt;padding: 4px 0;&lt;br /&gt;text-align:left;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* --------------------------------------*/&lt;br /&gt;/* TOPBAR                                */&lt;br /&gt;/* --------------------------------------*/&lt;br /&gt;#topbar {&lt;br /&gt;text-align:left;&lt;br /&gt;padding:5px 0;&lt;br /&gt;border-top: 1px solid #333;&lt;br /&gt;background: #aaaaaa;&lt;br /&gt;height: 15px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#topbar #welcome{&lt;br /&gt;float:left;&lt;br /&gt;text-align:left;&lt;br /&gt;font-size: 12px;&lt;br /&gt;padding: 0px 0px 0px 10px;&lt;br /&gt;color: #ffffff;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#topbar #menu{&lt;br /&gt;float: right;&lt;br /&gt;text-align: right;&lt;br /&gt;font-size: 12px;&lt;br /&gt;color: #ffffff;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#topbar #menu a{&lt;br /&gt;color: #ffffff;&lt;br /&gt;padding: 0px 10px 0px 0px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* --------------------------------------*/&lt;br /&gt;/* HEADER                                */&lt;br /&gt;/* --------------------------------------*/&lt;br /&gt;#header {&lt;br /&gt;background: #227ce8;&lt;br /&gt;height: 150px;&lt;br /&gt;margin: 0px auto;&lt;br /&gt;border-top: solid 2px #000000;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#header h1 {&lt;br /&gt;font-family:Arial,sans-serif;&lt;br /&gt;color: white;&lt;br /&gt;padding: 20px 0 0 6px;&lt;br /&gt;font-size:1.6em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* --------------------------------------*/&lt;br /&gt;/* CONTENT                               */&lt;br /&gt;/* --------------------------------------*/&lt;br /&gt;#content {&lt;br /&gt;float: left;&lt;br /&gt;width: 80%;&lt;br /&gt;color: #000;&lt;br /&gt;margin: 0 0.5em 0 0;&lt;br /&gt;padding: 0px 10px 5px 0px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* --------------------------------------*/&lt;br /&gt;/* NAVBAR                                */&lt;br /&gt;/* --------------------------------------*/&lt;br /&gt;#navbar {&lt;br /&gt;float: right;&lt;br /&gt;width: 210px;&lt;br /&gt;color: #ff0000;&lt;br /&gt;padding: 0px 5px 0px 5px;&lt;br /&gt;margin: 0 0 0 0;&lt;br /&gt;border-left: 1px solid #d3d3d3;&lt;br /&gt;border-right: 1px solid #d3d3d3;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/*2.12.08 IE correction to drop sidbarBox*/&lt;br /&gt;html&amp;gt;body #navbar .navbarBox {&lt;br /&gt;&lt;br /&gt;width: 200px;&lt;br /&gt;color: #0000ff;&lt;br /&gt;background: #2175bc;&lt;br /&gt;margin: 0px 5px 5px 5px  ; /*ensure there's a gap between text in navbar and start of navbar*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/*Header for boxes in navbar menu*/&lt;br /&gt;#navbar .navbarBox h1{&lt;br /&gt;background: #227ce8;&lt;br /&gt;color: #ffffff;&lt;br /&gt;font-weight: normal;&lt;br /&gt;font-size: 16px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* --------------------------------------*/&lt;br /&gt;/* FOOTER                                */&lt;br /&gt;/* --------------------------------------*/&lt;br /&gt;#footer {&lt;br /&gt;border: solid 1px #000000;&lt;br /&gt;height: 20px;&lt;br /&gt;clear:both;&lt;br /&gt;padding:5px;&lt;br /&gt;margin: 5px 5px 5px 5px;&lt;br /&gt;background: #ababab url(../images/footer.png) repeat;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#footer .tandc{&lt;br /&gt;float: right;&lt;br /&gt;color: #0000ff;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#footer .copyright{&lt;br /&gt;float: left;&lt;br /&gt;color: #0000ff;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/*----------------------------------------------------------*/&lt;br /&gt;/*  Improved vertical menu for navbar: see http://css.maxdesign.com.au/listamatic/vertical10.htm&lt;br /&gt;/*----------------------------------------------------------*/&lt;br /&gt;&lt;br /&gt;h1.panelHeader{&lt;br /&gt;font-weight: bold;&lt;br /&gt;text-align: center;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/*need html&amp;gt;body hack otw menu boxes, e.g. navbarbx all merge together*/&lt;br /&gt;html&amp;gt;body .navcontainer1&lt;br /&gt;{&lt;br /&gt;width: 100%;&lt;br /&gt;border-right: 1px solid #000;&lt;br /&gt;font-family: Verdana, Lucida, Geneva, Helvetica, Arial, sans-serif;&lt;br /&gt;background-color: #227ee8;&lt;br /&gt;color: #333;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.navcontainer1 ul&lt;br /&gt;{&lt;br /&gt;list-style: none;&lt;br /&gt;margin: 0;&lt;br /&gt;padding: 0;&lt;br /&gt;border-top: 2px solid #000000;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.navcontainer1 li&lt;br /&gt;{&lt;br /&gt;border-bottom: 1px solid #90bade;&lt;br /&gt;margin: 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.navcontainer1 li a&lt;br /&gt;{&lt;br /&gt;display: block;&lt;br /&gt;padding: 5px 5px 5px 0.5em;&lt;br /&gt;border-left: 10px solid #1958b7;&lt;br /&gt;border-right: 10px solid #508fc4;&lt;br /&gt;background-color: #2175bc;&lt;br /&gt;color: #ffffff;&lt;br /&gt;text-decoration: none;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;html&amp;gt;body .navcontainer1 li a { width: auto; }&lt;br /&gt;&lt;br /&gt;.navcontainer1 li a:hover&lt;br /&gt;{&lt;br /&gt;border-left: 10px solid #1c64d1;&lt;br /&gt;border-right: 10px solid #5ba3e0;&lt;br /&gt;background-color: #2586d7;&lt;br /&gt;color: #fff;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The menu in the navbar was styled using resources from &lt;a href="http://css.maxdesign.com.au/listamatic/vertical10.htm"&gt;Listomatic's Menu Tutorial&lt;/a&gt;. This is an excellent site which provides hints on how to create and style vertical/horizontal css menus.&lt;br /&gt;&lt;br /&gt;The final result is displayed below.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_aesd62-K5Pg/STfKP8Yn80I/AAAAAAAAADI/rEJnisxHTH4/s1600-h/complete.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 190px;" src="http://4.bp.blogspot.com/_aesd62-K5Pg/STfKP8Yn80I/AAAAAAAAADI/rEJnisxHTH4/s400/complete.png" alt="" id="BLOGGER_PHOTO_ID_5275907863744082754" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The application suffers from at least a couple of problems when viewed using IE6. One of these shows the menu items in the navbar displayed in gray even though the css marks them to be white. To resolve this simply remove '&lt;span style="font-style: italic;"&gt;a:visited&lt;/span&gt;' from the line&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 20px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;    a:link, a:visited, a:hover {&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;in the file main.css.&lt;br /&gt;&lt;br /&gt;This example was developed using grails 1.03 and can be downloaded [templates.zip]  from &lt;a href="https://sites.google.com/site/moongrailscode/"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-915388556075280887?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/5jcU5bc3h_M/grails-templates.html</link><author>noreply@blogger.com (Mo Sayed)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_aesd62-K5Pg/STepE2cRwYI/AAAAAAAAAC4/t-wl3os5RIc/s72-c/blog_layout.jpg" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2008/12/grails-templates.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-4011705257219937726</guid><pubDate>Mon, 01 Dec 2008 11:40:00 +0000</pubDate><atom:updated>2008-12-02T09:59:38.299Z</atom:updated><title>grails: jQuery '$' conflicts</title><description>I'm a keen user of jQuery, which I've found to be a very robust, reliable and fairly intuitive javascript library. It offers tonnes of documentation in addition to a vast library of plugins to cater for most if not all your needs.&lt;br /&gt;&lt;br /&gt;My experience with this library has been primarily problem-free except for one occasion when I attempted to use it in conjunction with another javascript library. Specifically, I discovered that previously working code implementing jQuery ceased to function when I imported in the scriptaculous library.&lt;br /&gt;&lt;br /&gt;Generally jQuery has been well designed to circumvent potential collisions with other libraries through the use of a unique namespace. In spite of this, problems do occur, because of the role of the '$' function in jQuery and other competing libraries. In jQuery it is a shortcut reference to jQuery but means something different for scriptaculous. The solution to this problem is actually quite straightforward and is documented &lt;a href="http://docs.jquery.com/Using_jQuery_with_Other_Libraries"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Consider the following code [described in post: &lt;span style="font-style: italic; color: rgb(0, 0, 153);"&gt;grails: toggling Form elements&lt;/span&gt;, step 3]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 250px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;&amp;lt;g:javascript&amp;gt;&lt;br /&gt;&lt;pre&gt;    $(document).ready(function() {&lt;br /&gt;&lt;br /&gt;      $("#status").change(function() {&lt;br /&gt;             $.ajax({&lt;br /&gt;                      url: "/myform/issue/selectOutcome",&lt;br /&gt;                      data: "status=" + this.value,&lt;br /&gt;                      cache: false,&lt;br /&gt;                      success: function(html) {$("#selectOutcome").html(html);}&lt;br /&gt;                   });&lt;br /&gt;             }&lt;br /&gt;       );&lt;br /&gt;&lt;br /&gt;    });&lt;br /&gt;&lt;br /&gt;&amp;lt;/g:javascript&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This code allows us to use ajax to display/hide form elements based upon the selection made by a user from a drop down list. The code works fine, until I import in scriptaculous&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 30px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&amp;lt;g:javascript library="scriptaculous"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Following this step, I discovered that the previous code stopped working. Implementing the suggestions provided by the jQuery website, I modified the above code as follows&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 270px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;&amp;lt;g:javascript&amp;gt;&lt;br /&gt;&lt;pre&gt;    var $j = jQuery.noConflict();&lt;br /&gt;  &lt;br /&gt;$j(document).ready(function() {&lt;br /&gt;&lt;br /&gt;   $j("#status").change(function() {&lt;br /&gt;   $j.ajax({&lt;br /&gt;    url: "/myform/issue/selectOutcome",&lt;br /&gt;    data: "status=" + this.value,&lt;br /&gt;    cache: false,&lt;br /&gt;    success: function(html) {$j("#selectOutcome").html(html);}&lt;br /&gt;           });&lt;br /&gt;           }&lt;br /&gt;       );&lt;br /&gt;&lt;br /&gt;  });      &lt;br /&gt;&lt;br /&gt;&amp;lt;/g:javascript&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Looking carefully at the before and after code snippets, you can see that the main difference has been to replace '$' with the variable '$j' where j is simply an alias for the function jQuery.noConflict().  This function tells jQuery to ignore the $ function so that scriptaculous or some other library can use it. Modifying the code in this way enables both the scriptaculous and jQuery code to function as expected. QED.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-4011705257219937726?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/eP1gNHTkzQU/grails-jquery-conflicts.html</link><author>noreply@blogger.com (Mo Sayed)</author><thr:total>0</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2008/12/grails-jquery-conflicts.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-3546920270187381069</guid><pubDate>Fri, 28 Nov 2008 17:33:00 +0000</pubDate><atom:updated>2008-12-09T21:34:54.208Z</atom:updated><title>grails: toggling Form elements</title><description>Occasionally the need arises to have form elements that can be displayed/hidden depending upon a selection made from a drop down list. In this post I demonstrate how this can be achieved using grails + jQuery. The example shown below was developed using grails.1.03 and jQuery-1.2.6&lt;br /&gt;&lt;br /&gt;The demonstration is based upon a simplified example of a bug tracker I have been developing. In this example, the domain class, Issue, has 4 fields,&lt;br /&gt;&lt;ul&gt;&lt;li&gt;name&lt;/li&gt;&lt;li&gt;description&lt;/li&gt;&lt;li&gt;status  [e.g. UNASSIGNED, ASSIGNED, CLOSED]&lt;br /&gt;&lt;/li&gt;&lt;li&gt;outcome [e.g. FIXED, NOT FIXED, DUPLICATE]&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;When a user is editing an issue, only the first three fields should be displayed by default.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_aesd62-K5Pg/SS7UoRy1sZI/AAAAAAAAACg/hGdlUGIwulA/s1600-h/BeforeToggle.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 170px;" src="http://3.bp.blogspot.com/_aesd62-K5Pg/SS7UoRy1sZI/AAAAAAAAACg/hGdlUGIwulA/s400/BeforeToggle.png" alt="" id="BLOGGER_PHOTO_ID_5273386002133922194" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;It makes sense to display the outcome field only  if an issue has been marked "CLOSED" as below. Changing the status to any other value will reset the form and the outcome field will be hidden.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_aesd62-K5Pg/SS7WcLKosjI/AAAAAAAAACw/SiUSGF7V09Q/s1600-h/AfterToggle.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 400px; height: 203px;" src="http://1.bp.blogspot.com/_aesd62-K5Pg/SS7WcLKosjI/AAAAAAAAACw/SiUSGF7V09Q/s400/AfterToggle.png" alt="" id="BLOGGER_PHOTO_ID_5273387993219510834" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now to move onto the implementation details.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 1: Download &amp;amp; Import jQuery library&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In this step you need to download jQuery and place it in the web-app/js folder.&lt;br /&gt;Next you need to import it into the gsp page by placing the following statement between the &amp;lt;head&amp;gt; tags, e.g.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div   style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 40px; color: rgb(0, 0, 0); text-align: left;font-family:courier;font-size:12px;"&gt;&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&amp;lt;g:javascript library="jquery-1.2.6.min"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 2: Create Basic Form Elements&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Staying with the gsp page, we create a form and embed within it 2 key elements. The first of these is a drop-down list for the status field.  The second element is a stub represented here as an empty table row. Depending on selections to the status field, the stub will either remain empty or be replaced by an extra form element, e.g. a drop-down list for the outcome field.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 230px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;pre&gt;&amp;lt;tr&amp;gt;&lt;br /&gt;&amp;lt;td valign="top" class="name"&amp;gt;&lt;br /&gt;&amp;lt;label for="status"&amp;gt;Status:&amp;lt;/label&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;td valign="top" class="value ${hasErrors(bean:issue,field:'status','errors')}"&amp;gt;&lt;br /&gt;&amp;lt;g:select id="status" name="status" from="${issue.constraints.status.inList}"&lt;br /&gt;value="${issue.status}" &amp;gt;&amp;lt;/g:select&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;tr class="prop" id="selectOutcome"&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 3: Code up Ajax call&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Next we need to bind the status drop-down list with javascript functions that will detect user selection and transmit its value to a grails controller via Ajax. The .change() and .ajax() functions provided by jQuery fit the bill. They will send the value selected from the status list to the controller and replace the empty stub with any html content sent back.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 250px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;&amp;lt;g:javascript&amp;gt; &lt;br /&gt;&lt;pre&gt;    $(document).ready(function() {&lt;br /&gt;&lt;br /&gt;     $("#status").change(function() {&lt;br /&gt; $.ajax({&lt;br /&gt;  url: "/myform/issue/selectOutcome",&lt;br /&gt;  data: "status=" + this.value,&lt;br /&gt;         cache: false,&lt;br /&gt;  success: function(html) {$("#selectOutcome").html(html);}&lt;br /&gt;  });&lt;br /&gt;}&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;});    &lt;br /&gt;&lt;br /&gt;&amp;lt;/g:javascript&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The first argument to the ajax function is the url to the grails action. This is of the form /web context/controller/action. The 'data' parameter is the value of the selection made by the user, and the success parameter will replace the empty stub with the response generated by the grails controller.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 4: Implement grails action code&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 250px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;   /**&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;* Enable user to select outcome when issue is closed&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;*/&lt;br /&gt;&lt;pre&gt;    def selectOutcome = {&lt;br /&gt;&lt;br /&gt;String status = params.status&lt;br /&gt;&lt;br /&gt;if(status == "CLOSED")&lt;br /&gt;{&lt;br /&gt;  render (template:"selectOutcome")&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;render ""&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;This action defined within an appropriate controller, simply checks to see if the selection made by the user for the status field is set to "CLOSED". If true, it will render the content placed within the template file defined below.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 5: create template&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Create a template file which should contain the form element you want to display/hide in your target gsp page. Note that this file should be named with a leading underscore, e.g. _selectOutcome.gsp&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 620px; height: 150px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;br /&gt;&amp;lt;td valign="top" class="name"&amp;gt;&lt;br /&gt;&amp;lt;label for="outcome"&amp;gt;Outcome:&amp;lt;/label&amp;gt;&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;td valign="top" class="value ${hasErrors(bean:issue,field:'outcome','errors')}"&amp;gt;&lt;br /&gt;&amp;lt;g:select id="selectOutcome" name="outcome"&lt;br /&gt;from="${Issue.constraints.outcome.inList}" value=""&amp;gt;&amp;lt;/g:select&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This will render a label and a select element for the outcome field.&lt;br /&gt;&lt;br /&gt;That's it !&lt;br /&gt;&lt;br /&gt;You can download the code for this example [myform.zip] &lt;a href="http://sites.google.com/site/moongrailscode/"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Following the steps above should enable you to implement a similar form toggling feature for your application. Bear in mind though, that there is a potential problem if you will be using jQuery in conjunction with other javascript libraries. However this is a post for another day.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-3546920270187381069?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/M3kgp_-qFhQ/x.html</link><author>noreply@blogger.com (Mo Sayed)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_aesd62-K5Pg/SS7UoRy1sZI/AAAAAAAAACg/hGdlUGIwulA/s72-c/BeforeToggle.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2008/11/x.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-2318166456791355216</guid><pubDate>Thu, 27 Nov 2008 13:31:00 +0000</pubDate><atom:updated>2008-11-27T13:33:56.374Z</atom:updated><title>grails: many-to-many Backref exception</title><description>&lt;span style="font-size:85%;"&gt;I ran into a rather interesting problem recently when attempting to save a many-to-many association between 2 domain objects. I've managed to reproduce the problem in a sanitised test case which I'll present here. The app was developed using grails 1.03.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;Consider the domain classes, Project and User defined as follows&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_aesd62-K5Pg/SS6ZzuW0LEI/AAAAAAAAACI/64PWA5EOKhs/s1600-h/Project.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 186px;" src="http://3.bp.blogspot.com/_aesd62-K5Pg/SS6ZzuW0LEI/AAAAAAAAACI/64PWA5EOKhs/s400/Project.png" alt="" id="BLOGGER_PHOTO_ID_5273321327593532482" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;and&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_aesd62-K5Pg/SS6aFrJuXrI/AAAAAAAAACQ/fqOodYYiXYU/s1600-h/User.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 205px;" src="http://1.bp.blogspot.com/_aesd62-K5Pg/SS6aFrJuXrI/AAAAAAAAACQ/fqOodYYiXYU/s400/User.png" alt="" id="BLOGGER_PHOTO_ID_5273321635970965170" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Note that I've used a &lt;span style="border-bottom: 1px solid;"&gt;&lt;span style="font-weight: bold;"&gt;List&lt;/span&gt;&lt;/span&gt; to store the objects on the other side of the many-to-many relationship. Next I create test data in the BootStrap class as follows,&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_aesd62-K5Pg/SS6bptEY6iI/AAAAAAAAACY/PAvDrRLtUPM/s1600-h/bootstrap.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 327px;" src="http://3.bp.blogspot.com/_aesd62-K5Pg/SS6bptEY6iI/AAAAAAAAACY/PAvDrRLtUPM/s400/bootstrap.png" alt="" id="BLOGGER_PHOTO_ID_5273323354472376866" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;Upon deploying the application, I get the following exception&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255); font-weight: bold;font-size:78%;" &gt;&lt;span style="font-family:courier new;"&gt;Caused by: org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value: Project._User_projectsBackref; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value: Project._User_projectsBackref&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;This is rather puzzling. The exception suggests an object referencing issue, but it's not clear what this is. Equally baffling is the solution to this problem. The domain classes above use a List to store their many-to-many collaborators. By simply reverting to the default collection type, i.e. a Set, the problem disappears. So for the code above remove line 7 in both classes and the app should deploy without any problems.&lt;/span&gt;  &lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;br /&gt;I recently came across this  &lt;a href="http://jira.codehaus.org/browse/GRAILS-2910"&gt;JIRA&lt;/a&gt; ticket which suggests this is a problem the grails team are aware of. &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-2318166456791355216?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/ccM38QjyXow/grails-many-to-many-backref-exception.html</link><author>noreply@blogger.com (Mo Sayed)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_aesd62-K5Pg/SS6ZzuW0LEI/AAAAAAAAACI/64PWA5EOKhs/s72-c/Project.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2008/11/grails-many-to-many-backref-exception.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-3345789562965334344</guid><pubDate>Thu, 13 Nov 2008 08:56:00 +0000</pubDate><atom:updated>2008-12-01T17:21:46.302Z</atom:updated><title>grails + ajax: autocomplete</title><description>Having an autocomplete feature on a textfield is a good way of improving user experience. As the user types a string, the textfield tries to anticipate what the user is typing and offers possible matches. Clicking on a match results in autopopulation of the textfield thereby saving time for the user.&lt;br /&gt;&lt;br /&gt;In this post I describe a way of implementing this feature using grails and scriptaculous. To help explain the method, I will develop a simple application where we have a form containing a text field for countries of the world. As a user begins to enter the name of the country, the textfield presents a list of possible matches. Finding the country he wants, the user clicks on the entry and the textfield is  immediately  populated with the selection [see picture below].&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_aesd62-K5Pg/SRywyXyP-RI/AAAAAAAAABA/gd0aM_pM2oE/s1600-h/Picture+7.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 400px; height: 331px;" src="http://3.bp.blogspot.com/_aesd62-K5Pg/SRywyXyP-RI/AAAAAAAAABA/gd0aM_pM2oE/s400/Picture+7.png" alt="" id="BLOGGER_PHOTO_ID_5268280043541362962" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Before ploughing into the specific details of how to implement this feature, it's useful to gain a high level view of what is going on "under the hood".&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;To begin with, the textfield needs to be registered with an ajax function [defined within scriptaculous]. This function will detect keystrokes and then call an appropriate action within a grails controller. The action in turn will employ GORM to perform a hibernate search for records that match the pattern of text the user has just entered. The results from this search will be returned to the view and rendered as a list just under the text box.&lt;br /&gt;&lt;br /&gt;So to build your own autocomplete search text field, you need to do the following,&lt;br /&gt;&lt;br /&gt;&lt;span style="border-bottom: 1px solid black; font-weight: bold;font-size:130%;" &gt;Step 1&lt;/span&gt;&lt;span style="font-size:130%;"&gt;: Import in scriptaculous&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Copy and paste the following into the header element of  your gsp page&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 20px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&amp;lt;g:javascript library='scriptaculous'/&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="border-bottom: 1px solid black; font-weight: bold;font-size:130%;" &gt;Step 2: &lt;/span&gt;&lt;span style="font-size:130%;"&gt;create the form with the text field and the supporting div&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Within the gsp page create a text field and a supporting div to display the results of the autocomplete feature.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div   style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 250px; color: rgb(0, 0, 0); text-align: left;font-family:courier;font-size:12px;"&gt;&lt;pre&gt;&amp;lt;g:form name="find"..... &amp;gt;&lt;br /&gt;&lt;br /&gt;..............&lt;br /&gt;..............&lt;br /&gt;&lt;br /&gt;&amp;lt;%-- Text field supporting autocomplete --%&amp;gt;          &lt;span style="color: rgb(51, 51, 255);"&gt;&lt;br /&gt;&amp;lt;input type="text" id="&lt;span style="color: rgb(255, 0, 0);"&gt;autocomplete&lt;/span&gt;" name="&lt;span style="color: rgb(255, 0, 0);"&gt;autocomplete_parameter&lt;/span&gt;"/&amp;gt;&lt;/span&gt;&lt;span style=""&gt;&lt;br /&gt;&lt;br /&gt;..........&lt;br /&gt;..........&lt;br /&gt;&lt;br /&gt;&amp;lt;/g:form&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;%-- supporting div to display potential matches.--%&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;&amp;lt;div id="&lt;span style="color: rgb(255, 0, 0);"&gt;autocomplete_choices&lt;/span&gt;" class="autocomplete" &amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="border-bottom: 1px solid black; font-weight: bold;font-size:130%;" &gt;Step 3: &lt;/span&gt;&lt;span style="font-size:130%;"&gt;Implement grails action to respond to ajax call&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In this section we create the action in a grails controller that will respond to the ajax function.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 300px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;    def ajaxAutoComplete = {&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if(params.autocomplete_parameter)&lt;br /&gt;{&lt;br /&gt; def input = params.autocomplete_parameter + '%'&lt;br /&gt;&lt;br /&gt; def list = Country.findAll("from Country as country&lt;br /&gt;                             where lower(country.name) like :name", [name:input])&lt;br /&gt;&lt;br /&gt; StringBuffer idList = new StringBuffer()&lt;br /&gt;&lt;br /&gt; idList.append("&amp;lt;ul&amp;gt;")&lt;br /&gt;&lt;br /&gt; list?.each{c -&gt; idList.append("&amp;lt;li&amp;gt;" + c.name+"&amp;lt;/li&amp;gt;")}&lt;br /&gt;&lt;br /&gt; idList.append("&amp;lt;/ul&amp;gt;")  &lt;br /&gt;&lt;br /&gt; render idList.toString()&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The action receives a variable called&lt;span style="color: rgb(255, 0, 0);"&gt; autocomplete_parameter &lt;/span&gt; containing the keystrokes entered by the user. This string is used in a hibernate query to retrieve the set of countries with matching names. Finally the search results are embedded within a html list that is subsequently rendered to view.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="border-bottom: 1px solid black;font-size:130%;" &gt;Step 4: &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:130%;"&gt;Code up the ajax call&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In this case, we're simply setting up and calling an ajax function [&lt;a href="http://github.com/madrobby/scriptaculous/wikis/ajax-autocompleter"&gt;Ajax.Autocompleter&lt;/a&gt;] from scriptaculous.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 280px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;&lt;pre&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&amp;lt;meta equiv="Content-Type" content="text/html; charset=UTF-8"&amp;gt;&lt;br /&gt;&amp;lt;meta name="layout" content="main"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;g:javascript library="'scriptaculous'/"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;g:javascript&amp;gt;&lt;br /&gt;window.onload = function()&lt;br /&gt;                {&lt;br /&gt;                  new Ajax.Autocompleter("&lt;span style="color:red;"&gt;autocomplete&lt;/span&gt;",&lt;br /&gt;                                         "&lt;span style="color:red;"&gt;autocomplete_choices&lt;/span&gt;",&lt;br /&gt;                                         "&lt;span style="color:red;"&gt;/autocomplete/country/ajaxAutoComplete&lt;/span&gt;",&lt;br /&gt;                                         {}&lt;br /&gt;                                        );&lt;br /&gt;                }&lt;br /&gt;&amp;lt;/g:javascript&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The first argument to this function is the id of the UI element we're monitoring, i.e. the textfield. Next we pass in the name of the element that will be updated with the results of the ajax call. Thirdly we pass in the url to the grails code that will service the ajax function. This url is of the form /web context/controller/action.&lt;br /&gt;&lt;br /&gt;&lt;span style="border-bottom: 1px solid black; font-weight: bold;font-size:130%;" &gt;Step 5:  &lt;/span&gt;&lt;span style="font-size:130%;"&gt;Add styling to the supporting div.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Place the following excerpt into /web-app/css/main.css&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px dashed rgb(170, 170, 170); padding: 4px; background: rgb(238, 238, 238) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 650px; height: 410px; color: rgb(0, 0, 0); text-align: left; font-family: courier; font-size: 12px;"&gt;div.autocomplete {&lt;br /&gt;&lt;pre&gt;position:absolute;&lt;br /&gt;width:250px;&lt;br /&gt;background-color:#8cafea;&lt;br /&gt;margin:0px;&lt;br /&gt;padding:0px;&lt;br /&gt;overflow:hidden;&lt;br /&gt;border: 1px solid;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;div.autocomplete ul {&lt;br /&gt;list-style-type:none;&lt;br /&gt;margin:0px;&lt;br /&gt;padding:0px;&lt;br /&gt;overflow:auto;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;div.autocomplete ul li.selected { background-color: #294597; color: #ffffff}&lt;br /&gt;&lt;br /&gt;div.autocomplete ul li {&lt;br /&gt;list-style-type:none;&lt;br /&gt;display:block;&lt;br /&gt;margin:0 0 0 5;&lt;br /&gt;padding:2px;&lt;br /&gt;height:12px;&lt;br /&gt;cursor:pointer;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Presto !!! We now have an autocomplete solution using grails + scriptaculous. This implementation could be improved further by limiting the number of matches found by hibernate to some number say 8. This would prevent situations whereby the user is inundated with a long list of possible matches. There are alternative ways to implement this feature. These include using the &lt;a href="http://docs.codehaus.org/display/GRAILS/RichUI+Plugin"&gt;RichUI&lt;/a&gt; or &lt;a href="http://grails.org/GrailsUI+Plugin"&gt;GrailsUI &lt;/a&gt;plugins.&lt;br /&gt;&lt;br /&gt;You can download this example [autocomplete.zip] &lt;a href="https://sites.google.com/site/moongrailscode/"&gt;here&lt;/a&gt;. This example was built using grails 1.03&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-3345789562965334344?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/yTL3aI3moqk/grails-ajax-autocomplete.html</link><author>noreply@blogger.com (Mo Sayed)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_aesd62-K5Pg/SRywyXyP-RI/AAAAAAAAABA/gd0aM_pM2oE/s72-c/Picture+7.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2008/11/grails-ajax-autocomplete.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5079888946107534313.post-3977030764018487476</guid><pubDate>Wed, 12 Nov 2008 10:28:00 +0000</pubDate><atom:updated>2008-11-25T14:42:56.705Z</atom:updated><title>grails: Hello</title><description>Hi,&lt;br /&gt;&lt;br /&gt;I'm currently in the process of learning grails by working on a pet project [an issue/bug tracker]. It's my intention to report some of what i've learnt on this blog not only for future recall but also hoping that others may find some of it useful.&lt;br /&gt;&lt;br /&gt;I selected a bug/issue tracker since it would capture many of the features you would expect to find on a business reporting tool. Furthermore there seems to be a splurge of example applications based on blogging tools. I wanted to do something different.&lt;br /&gt;&lt;br /&gt;I plan to document this exercise and release it together with the source code so that others may use it to learn grails. The application is called 'Gravitium' which is a combination of 'Grails' and 'Vitium' [latin for fault or defect].&lt;br /&gt;&lt;br /&gt;Typical features for this application include,&lt;br /&gt;&lt;ul&gt;&lt;li&gt;basic CRUD [using GORM]&lt;br /&gt;&lt;/li&gt;&lt;li&gt;security [using acegi plugin]&lt;/li&gt;&lt;li&gt;search [using the compass plugin]&lt;/li&gt;&lt;li&gt;Charts [using google-chart plugin]&lt;/li&gt;&lt;li&gt;Reporting [using jasper plugin]&lt;/li&gt;&lt;/ul&gt;Additionally the application will use JQuery to provide enriched ui experience to supplement the functionality provided by the scriptaculous library which is included with grails.&lt;br /&gt;&lt;br /&gt;In future posts, I'll present examples of how to implement some of these features.&lt;br /&gt;&lt;br /&gt;Here's a snapshot of the app as it stands.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_aesd62-K5Pg/SRq4Zut-HPI/AAAAAAAAAA0/Rs9tYwBS1vo/s1600-h/Picture+5.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 400px; height: 260px;" src="http://1.bp.blogspot.com/_aesd62-K5Pg/SRq4Zut-HPI/AAAAAAAAAA0/Rs9tYwBS1vo/s400/Picture+5.png" alt="" id="BLOGGER_PHOTO_ID_5267725466340302066" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5079888946107534313-3977030764018487476?l=moongrails.blogspot.com' alt='' /&gt;&lt;/div&gt;</description><link>http://feedproxy.google.com/~r/Moongrails/~3/FpAvbxleqIc/hello.html</link><author>noreply@blogger.com (Mo Sayed)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_aesd62-K5Pg/SRq4Zut-HPI/AAAAAAAAAA0/Rs9tYwBS1vo/s72-c/Picture+5.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://moongrails.blogspot.com/2008/11/hello.html</feedburner:origLink></item></channel></rss>

