Grails UI
Dependency :
compile ":grails-ui:1.2.3"Custom repositories :
mavenRepo "http://localhost:8081/artifactory/plugins-releases-local/"
Summary
Provides a standard UI tag library for ajaxy widgets using YUI.
Installation
This plugin is deprecated and no longer maintained. It will see no further development. It is recommended that you use the native API of each widget toolkit to make the most out of them whether it be jQuery-UI, Dojo etc.GUI depends on the installation of the YUI Plugin (version 2.6.0) and the Bubbling Plugin (version 1.5.0). For Grails 1.1:
grails install-plugin grails-ui
grails install-plugin yui grails install-plugin bubbling grails install-plugin grails-ui
Description
GrailsUI
GrailsUI (GUI) is a plugin that provides an extensive tag library of rich ajax components based on the Yahoo! UI (YUI) JavaScript library and a YUI extension called the Bubbling Library. It has been designed for ease-of-use as well as configurability.Quick Start
Just want some working code to get a quick start on using GUI? There is a GUI demo project on Google Code that shows working examples of every tag in the library. To check it out:svn checkout http://guidemo.googlecode.com/svn/trunk/ guidemo-read-only
Screencasts
A series of screencasts is under construction:- Part 1 - GrailsUI Basics
- Part 2 - AutoComplete Basics
- Part 3 - Advanced AutoComplete
- Part 4 - DataTable (coming soon)
- Part 5 - Advanced Concepts (coming soon)
Key Concepts
Configuration Pass-Through
Most YUI widgets accept a configuration object during creation that helps to set up the widget, and most GUI tags map directly to a YUI component. Any attributes passed into a GUI tag that the tag library doesn't consume are assumed to be configuration settings for the underlying YUI widget. This allows the user to specify YUI config options directly within the tag description as attributes. For example, the YUI Dialog component accepts a config option called 'modal'. If modal="true", the dialog will not allow interaction with any other component until it is dismissed. Because of GUI's configuration pass-through, the user can specify an attribute of 'modal' when defining the <gui:dialog/> tag:<gui:dialog
title="Modal Dialog"
triggers="[show:[type:'link', text:'Click for dialog', on:'click']]"
modal="true"
>
This message will appear in a modal dialog.
</gui:dialog>Component Accessibility
Each YUI component that is created can be referenced via JavaScript in other areas of the page. This allows users to create event listeners to trigger custom actions. If an 'id' attribute is passed along to the component tag, this can be used to access the YUI component. Here is an example of attaching a custom select handler to a GUI datePicker component:<gui:datePicker id="myDatePicker" />
<script>
YAHOO.util.Event.onDOMReady(function() {
function selectHandler(){
alert('date selected on myDatePicker!');
}
GRAILSUI.myDatePicker.selectEvent.subscribe(selectHandler);
});
</script>Simple Dependency Mapping
YUI dependencies include JavaScript and CSS files, and can sometimes be complicated. YUI has a dependency configurator on their website to help developers define the proper includes in the correct order. GUI is designed to make dependency declaration as easy as possible by providing one resources tag for users to define what will be used within each GSP. In this manner, users can declare what components are active in a page in the HEAD once, and use their tags throughout the page. Any additions or changes to dependencies should only require a simple update of the resources tag in HEAD. More information is below in the resources section. The resources tag also makes it easy to include a specific CSS or JavaScript file existing within the YUI or Bubbling Library. Even when multiple components and individual files are marked for inclusion, any redundant dependencies are filtered out.Usage
There are two steps to using a GUI component. First, users must specify what components will be used within a GSP in the Resources tag in the HEAD of the page. Users can then declare the tags anywhere else within the page.Resources tag
Each GSP page that uses any GUI component must declare what components are used on the page within the HEAD tag. This is done with the <gui:resources/> tag. Very simply, the components uses on the page must be declared within a 'components' attribute in order for GUI to know what YUI and Bubbling Library files to include with the page. For example, for a page that will use a richEditor and a dialog:<gui:resources components="['richEditor','dialog']"/><gui:resources components="richEditor, dialog"/><gui:resources components="richEditor, dialog" mode="debug"/>
Component tags
Component tags provide UI widgets for users. Each tag, if not specifically given an 'id' attribute, will have a unique id generated for it. As noted in the section on Configuration Pass-Through above, any attributes not consumed by the tag library will be passed on as configuration options to the YUI component that is created. Some components, like the dataTable, also allow specific attributes that will pass on configuration to additional supporting YUI components that are created to support it (see the section below on DataTable Paginator Configuration).Styling Components
All GUI component styles can be overridden with local CSS. For more information on skinning YUI, see here.If you want to use the built-in skin of the components, then you need to attach the class "yui-skin-sam" to the HTML element that contains the GrailsUI components (such as a "div" container). For instance, if you want to use the skin on all components in the entire application, then you can modify the "body" element in the "grails-app/views/layouts/main.gsp" as follows:<body class="yui-skin-sam">List of Component tags
Dialog
Provides a dialog and an optional trigger to show the dialog. This component wraps the YUI Dialog object, and all configuration properties of the dialog can be passed through as attributes to this tag.Attribute definitions
Dialog attribute: triggers
Allows user to define how the dialog will be triggered to show or hide. This can be used to declare existing components as triggers, or to have GUI create new triggers (currently only links or buttons). If there is already a component with id on the page, it can be specified by id as a trigger. Here is a code example of using the triggers attribute to create a new button to trigger the dialog to show:triggers="[show:[type:'button', text:'Press for Dialog', on:'click']]"triggers="[
show:[id:'show4', on:'click'],
hide:[id:'hide4', on:'click']
]"Dialog attribute: form
If the form attribute is true, the dialog will set itself up to contain a form. Default buttons are created with the proper event handling to remotely submit any form data that is contained within the form. If form="true", there should be enough information within the rest of the attributes to create a URL (either controller and action, or url attribute). The attributes are sent into createLink to resolve to a URL. There should also be an 'update' attribute set if there is data passed back from the asynchronous call to the form's URL.Dialog attributes: controller, action, params, url
If form="true", there must be enough data within the remaining attributes to resolve to a URL if sent to the createLink Grails method.Dialog attribute: update
This should be the id of an element to update with response text from the server. This element should contain HTML, as its innerHTML value is set. If any <script/> tags are contained in the response, they are executed after the innerHTML is loaded.Dialog Usage Examples
Modal confirm dialog (local)
This script provides an event handler and passes it into the dialog tag attached to a button. This dialog does not make a server call, but transfers the confirmation back to a local script.<script>
var yesHandler = function(o) {
alert('You clicked "Yes"');
this.cancel();
}
</script>
<gui:dialog
title="Modal Confirm Dialog"
draggable="false"
modal="true"
buttons="[
[text:'Yes', handler: 'yesHandler', isDefault: true],
[text:'No', handler: 'function() {this.cancel();}', isDefault: false]
]"
triggers="[show:[type:'link', text:'Confirm', on:'click']]"
>
Are you sure?
</gui:dialog>Modal confirm dialog (remote)
Here is an example of a confirm dialog that calls a URL when the 'Submit' button is clicked.<gui:dialog
form="true"
controller="demo" action="confirmSomething"
update="thingBeingUpdatedByResponse"
triggers="[show:[type:'link', text:'Confirm', on:'click']]"
>
Are you sure?
</gui:dialog>Remote form dialog
Any form elements can be placed inside the dialog tag without defining a form. When form="true", the dialog creates its own form.<gui:dialog
title="Dialog with Form"
form="true"
controller="demo"
action="acceptForm"
update="replaceMe"
triggers="[show:[type:'link', text:'click me for a form', on:'click']]"
>
<input type="text" id="input1" name="input1"/>
</gui:dialog>Other Useful Dialog Configuration Pass-Throughs
- buttons: Allows user to create their own buttons and event handling.
Tab View
Provides a tabbed view. This component wraps the YUI TabView object. Tab content is defined within <gui:tab/> elements.The tabView component only accepts one possible attribute, which is optional: id.Defining tabs
Tabs can be defined either by declaring static content within a tab element, or by defining enough attributes in the tab to resolve to a URL that will load the tab. A tab has only one required attribute, a 'label' that will define what is displayed on the tab marker. Setting the 'active' attribute to true will render the tab open initially. You may also specify a CSS class attribute for tabs that will be applied to the underlying 'li' element.TabView Usage Examples
Static Tabs
Any HTML, JavaScript, or GUI tags can be placed inside a tabView tab. Here is an example of several different components being rendered in a tabView.<gui:tabView>
<gui:tab label="Tab 1" active="true">
<h1>Inside Tab 1</h1>
<p/>You can put whatever markup you want here.
</gui:tab>
<gui:tab label="Tab 2">
<h1>Inside Tab 2</h2>
<gui:richEditor id='editor' value="You can use gui components within tabs, too!"/>
</gui:tab>
</gui:tabView>Dynamic Tabs
If enough data is provided within the tab attributes to create a URL, the tab will be loaded by the URL.If the dataSrc attribute is set, it will be used explicitly to render the tab content<gui:tab label="Loaded with dataSrc" dataSrc="/path/to/my/page.gsp" active="true"/>
<gui:tab
label="Loaded from controller"
controller="demo"
action="tabLoader"/>TabView Resources
AutoComplete
The autoComplete component wraps the YUI autoComplete widget. This tag can be used with local or remote data, and can depend on the value of another input element in the DOM. It can also be arranged to send along extra data to the server to be used for additional filtering.Setting up your controller
Proper controller setup is essential to use the autoComplete effectively. The autoComplete will send along a query string attached to the HTTP request that defines what the user has input into the text box.Currently, the GUI autoComplete is only configured to use JSON data format. This means that your controller must use the Grails JSON translation to convert autoComplete data into a usable format. Following is an example of a dummy controller action that provides data for an autoComplete.import grails.converters.JSON
def autoCompleteJSON = {
def list = DummyDomain.list(params.query)
def jsonList = list.collect { [ id: it.id, name: it.name ] }
def jsonResult = [
result: jsonList
]
render jsonResult as JSON
}Local AutoComplete
An autoComplete can be setup using only local data.<gui:autoComplete
id="localData"
options="['red','blue','yellow','orange','purple']"
/>Remote AutoComplete
Once an autoComplete is setup to use a server to provide the data, the configuration gets more complex.<gui:autoComplete
id="remoteData"
resultName="TestData"
labelField="description"
idField="id"
controller="demo"
action="testFruitDataAsJSON"
/>Filtering AutoComplete
There are some cases where the autoComplete data may need additional filtering at the server. This filtering data must be used by the controller to filter the data. Following are two examples of ways to specify additional filtering data for the server.<gui:autoComplete
id="restrictedControl1"
resultName="TestData"
labelField="description"
idField="id"
controller="demo"
action="testDataAsJSON"
filterBy="link"
filter="1"
/><gui:autoComplete
id="restrictedControl2"
resultName="TestData"
labelField="description"
idField="id"
controller="demo"
action="testDataAsJSON"
queryAppend="link=3"
/>AutoComplete Dependency
There are many cases where the value from another form input element needs to be send along with the autoComplete request. In this manner, an autoComplete can be dependent on the value of another form input.<input id="dependedOn" type="text" value="my value"/> <gui:autoComplete id="dependsOn" controller="demo" action="testDataAsJSON" dependsOn="dependedOn" />
AutoComplete Common Configurations
You probably don't want your autoComplete querying the server every few milliseconds (queryDelay), or maybe you want to restrict it from querying until a certain number of keys have been typed (minQueryLength). Use these attributes to control this:<gui:autoComplete
minQueryLength='3'
queryDelay='1.5'
…
</gui:autoComplete>Accordion
The accordion widget creates markup that is used by the Bubbling Library to transform into expandable panels.Initially the accordion will expand to take 100% width available, so it must be bound by a containing element.Accordion attributes
- multiple: allows multiple panels open at once
- bounce: provides a bounce effect on open and close
- persistent: panels stay open
- slow: slows down the open/close animation
- fade: provides a fade effect on open and close
Standard Accordion
<gui:accordion>
<gui:accordionElement title="Accordion element 1">
Accordion element 1 content
</gui:accordionElement>
<gui:accordionElement title="Accordion element 2">
<h3>Markup is fine in here</h3>
</gui:accordionElement>
</gui:accordion>Accordion with Options
<gui:accordion bounce="true" slow="true" multiple="true"> <gui:accordionElement title="Accordion element 1"> Accordion element 1 content </gui:accordionElement> <gui:accordionElement title="Accordion element 2"> <g:each var="i" in="(1..10)">Hello ${i}<br/></g:each> </gui:accordionElement> </gui:accordion>
Expandable Panel
The expandablePanel component is very similar to the accordion, and uses the same Bubbling Library source files. But the expandablePanel allows the user to define one panel that can be retracted into a title bar anywhere on the page, unattached to any other components.Expandable Panel Examples
<gui:expandablePanel title="This panel is already expanded" expanded="true"> I am expanded. Close me. </gui:expandablePanel><br/><br/><gui:expandablePanel bounce="false" title="Expand me for a fun surprise!" expanded="false"> I was not expanded at first, but if you are reading this, I must be expanded now. </gui:expandablePanel>
Data Table
The GUI dataTable is build by creating a YUI DataTable object. The YUI dataTable is a very rich component that can be configured in many different ways. There are many configuration attributes available for users to modify the YUI table upon creation.By default, GUI currently always uses server-side pagination and sorting. There is a JIRA to provide an option for client paging and sorting.Column Definitions
To create a dataTable, GUI needs to know what the columns are going to look like, so it needs a map of data for each column. This list of maps representing the columns to be displayed is the column definitions. You can see a table of all the possible values of column definitions here in the instantiating section. Here is example of what a columnDefs attribute on a dataTable might look like:columnDefs="[
[key:'id', sortable:true, resizeable: true, label:'ID'],
[key:'name', sortable:true, resizeable: true, label:'Name'],
[key:'birthDate', type:'date', sortable:true, resizeable: true, label: 'Birth Date'],
[key:'age', type:'number', sortable:true, resizeable: true, label: 'Age'],
[key:'netWorth', type:'currency', sortable:true, resizeable: true, label: 'Net Worth']
]"Paginator Configuration
A default Paginator is created, so no paginator config is necessary, but users may specify the config object for a custom Paginator. More information on Paginator configuration attributes can be found here. Following is an example of a custom Paginator configuration:paginatorConfig="[
template:'{PreviousPageLink} {PageLinks} {NextPageLink} {CurrentPageReport}',
pageReportTemplate:'{totalRecords} total records'
]"Setting up the Controller
Following is an example of a controller action that prepares data for a dataTable:import grails.converters.JSON def dataTableDataAsJSON = { def list = [] response.setHeader("Cache-Control", "no-store") def data = [ totalRecords: Demo.count(), results: Demo.list(params) ] render data as JSON }
Row Click Navigation
If you want your dataTable to navigate to a certain URL when a row is clicked, you need to ensure a 'dataUrl' field is included in your JSON data that defines the URL. Then you just need to add rowClickNavigation='true' to your dataTable attributes, and each row click will navigate to the dataUrl specified in the row.Row Expansion
The dataTable provides a row expansion feature. When configured properly, each row will expand on screen to provide more data about the row. This is done by inspecting the data for the clicked row and looking for a dataUrl.If the JSON data received by the dataTable contains a row value called 'dataUrl', it assumes that this URL should be called and rendered on row click. Row expansion is turned off by default, so the rowExpansion attribute must be set to true as well.Following is an example of how a controller might attach this dataUrl within the JSON return value:import grails.converters.JSON def dataTableDataAsJSON = { def list = [] def demoList = Demo.list(params) response.setHeader("Cache-Control", "no-store") demoList.each { list << [ id: it.id, name: it.name, birthDate: it.birthDate.toString(), age: it.age, netWorth: it.netWorth, dataUrl: g.createLink(action: 'dataDrillDown') + "/$it.id" ] } def data = [ totalRecords: Demo.count(), results: list ] render data as JSON }
DataTable Example
The following markup defines a dataTable and expects the controller to provide dataUrl values for row expansion. It also sends an additional parameter to the controller to use for filtering (maxAge=52).<gui:dataTable
id="dt_2"
draggableColumns="true"
columnDefs="[
[key:'id', sortable:true, resizeable: true, label:'ID'],
[key:'name', sortable:true, resizeable: true, label:'Name'],
[key:'birthDate', type:'date', sortable:true, resizeable: true, label: 'Birth Date'],
[key:'age', type:'number', sortable:true, resizeable: true, label: 'Age'],
[key:'netWorth', type:'currency', sortable:true, resizeable: true, label: 'Net Worth']
]"
paginatorConfig="[
template:'{PreviousPageLink} {PageLinks} {NextPageLink} {CurrentPageReport}',
pageReportTemplate:'{totalRecords} total records'
]"
controller="demo" action="nonStandardDataTableDataAsJSON"
params="[maxAge:52]"
resultsList="myResults"
rowExpansion="true"
rowsPerPage="3"
/>Other Useful Dialog Configuration Pass-Throughs
Other DataTable Resources
Rich Editor
The richEditor tag is one of the most useful and easy to use tags in the library. It is used very much like a textarea input component. The id it is passed will be the id of the textarea it actually uses, and is treated as such within a form.<gui:richEditor id='myEditor' value='initial value of the editor'/>
Draggable List
Draggable lists have items that can be dragged within each list to reorder the list, or dragged into other draggable lists to trade items between them.Draggable lists must be declared within a draggableListWorkArea in order to allow dragging between lists.<gui:draggableListWorkArea formReady="true"> <gui:draggableList id="myList1" class="list1" prepend="list_"> <li id="a1">A:1</li> <li id="a2">A:2</li> <li id="a3">A:3</li> <li id="a4">A:4</li> </gui:draggableList> <gui:draggableList id="myList2" class="list2"> <li>B:1</li> <li>B:2</li> <li>B:3</li> <li>B:4</li> </gui:draggableList> </gui:draggableListWorkArea>
Tooltip
You will need the JS resources refered as "toolTip". Do this by adding inside the HEAD part of your gsp (or layout):
<gui:resources components="['toolTip']"/><gui:toolTip text="This text shows in a tool tip."> <img src="/myImg.png"/> </gui:toolTip>
<gui:toolTip
controller="demo"
action="toolTipLoader"
params="[message:'worked']"
>
<div style="width: 200px; height: 200px; background:#8EC3E2; padding: 10px; border: 1px solid black">
Hover over this box for a server rendred tooltip.
</div>
</gui:toolTip>def toolTipLoader = {
render """
<h3>Tool tip markup from the server!</h3>
<p/>Here are my params: ${params}.
"""
}Date Picker
The GUI datePicker creates a YUI Calendar object along with an input element for its selection data. It is simple to create, and allows many configuration options. For a simple datePicker:<gui:datePicker id='simpleDatePicker' />
<gui:datePicker id='withDateValue' value="${new Date()}" /> <gui:datePicker id='withCalendar' value="${new GregorianCalendar(2008, 9, 31)}" formatString="yyyy/MM/dd HH:mm:ss" />
<gui:datePicker id='withCalendarAndTime' value="${new GregorianCalendar(1978,6,11,21,45,15)}" includeTime="true"/>