Provides a declarative way of defining Data Grids.
It works currently with jqGrid, google visualization and jquery dataTables.
Out of the box it provides sorting, filtering, exporting and inline edit just by declaring a grid in a controller and adding a tag to your gsp.
It also provides a powerful selection widget ( a direct replacement for drop-boxes )
compile ":easygrid:1.4.0"
- For minimum functionality you need: jquery-ui and export plugins.
- For google visualization you also need: google-visualization
You will need to add dependencies to:
- compile('org.mvel:mvel2:2.1.3.Final')
- compile('com.google.visualization:visualization-datasource:1.1.1') {
exclude (group: 'commons-logging', name: 'commons-logging')
exclude (group: 'commons-lang', name: 'commons-lang')
}
and the dynamic-controller and jquery-ui plugins
The issues that Easygrid tackles are:
-
big learning curve for each ajax Grid framework
-
once integrated into a grails project the business logic for each ajax Grid resides in multiple places ( Controller, gsp ). Usually, in the controller, there's a different method for each aspect ( search, export, security, etc)
-
a lot of concerns are addressed programmatically, instead of declaratively (like search, formats )
-
duplicated code (javascript, gsp, controllers). Each project has to create individual mechanisms to address it.
-
combo-boxes are suitable only when the dataset is small. Easygrid proposes a custom widget based on the same mechanism of defining grids, where for selecting an element you open a grid (with pagination & filtering) in a dialog and select the desired element
Easygrid solves these problems by proposing a solution based on declarations & conventions.
-
custom builder - for defining the grid
-
easy to mock and test:
-
easy to start able to generate a Grid from a domain class without any custom configuration
-
reloads & regenerates the grid when source code is changed
-
convenient default values for grid & column properties
-
DRY: predefined column types - ( sets of properties )
-
define the column formatters in one place
-
customizable html/javascript grid templates
-
built-in support for exporting to different formats ( using the exporter plugin )
-
easy inline editing - using jqgrid
-
configurable dynamic filtering form -
-
Jquery-ui widget and custom tag for a powerful selection widget featuring a jquery autocomplete textbox and a selection dialog built with Easygrid ( with filtering, sorting,etc)
The entire grid is defined in the Controller (or in an outside file) using the provided custom builder.
For each grid you can configure the following aspects:
- datasource
- grid implementation
- columns:
- name
- value ( could be a property of the datasource row, or a closure )
- formatting
- Optional specific grid implementation properties ( that will be available in the renderer)
- filterClosure - in case the grid supports per column filtering - this closure will act as a filter ( will depend on the underlying datasource )
- security
- global formatting of values
- export
- custom attributes
The plugin provides a clean separation between the model and the view ( datasource & rendering ) Currently there are implementations for 3 datasources ( GORM , LIST - represents a list of objects stored in the session (or anywhere), CUSTOM ) and 4 grid implementations ( JqGrid, GoogleVisualization, Datatables, static (the one generated by scaffolding) )
All grids will be defined in controllers - which must be annotated with @Easygrid.
In each annotated controller you need to define a static closure called "grids" where you define the grids which will be made available by this controller (using the dynamic controllers plugin, at startup, custom grid methods will be added to the controller for each defined grid ).
The plugin provides a custom Builder for making the configuration more straight forward.
Ex: ( from the easygrid petclinic )
static grids = {
ownersGrid {
dataSourceType 'gorm'
domainClass Owner
columns {
id {
type 'id'
}
firstName
lastName
address
city
telephone
nrPets {
label 'owner.nrPets.label'
enableFilter false
value { owner ->
owner.pets.size()
}
}
}
}
}
In the gsp, you can use this grid via the following tags: <grid:grid name="ownersGrid" jqgrid.width='600' columns.id.jqgrid.formatter='customShowFormat' jqgrid.caption='"Owners"' addUrl="${g.createLink(controller: 'owner',action: 'add')}"/> <grid:exportButton name="ownersGrid"/>
You also have to add the resource modules for the grid implementation: <r:require modules="easygrid-jqgrid-dev,export"/>
From this simple example, you can see how we can define all aspects of the grid . ( datasource, rendering, security, export properties, filtering )
The UI properties can be added to the Config file ( as defaults or types), in the Controller, or in the gsp page.
The simple grid from this example - which can be viewed here, comes with all features - including basic default filtering.
-
jqgrid - implements jqgrid
-
visualization - implements google visualization datatable
-
datatables - implements datatables
-
classic - implements the classic grails grid ( the static one generated by scaffolding )
To create your own implementation you need to: 1. create a service 2. create a renderer 3. declare the service & renderer in Config
On installation of the plugin , the renderer templates for the default implementations are copied to /grails-app/views/templates/easygrid from the plugin, to encourage the developer to customize them.
-
gorm
- the datasource is a Gorm domain class.
- domainClass - the domain ( mandatory)
- initialCriteria - a filter that will be applied all the time
For fast mock-up , grids with this type can be defined without columns, in which case these will be generated at runtime from the domain properties (dynamic scaffolding).
A filter closure is defined per column and will have to be a closure which will be used by a GORM CriteriaBuilder
-
list
- used when you have a list of custom objects (for ex. stored in the session ) that must be displayed in the grid
- context - the developer must declare the context where to find the list ( defaults to session )
- attributeName - and the attribute name (in the specified context)
- pagination will be handled by the framework
-
custom
- when the list to be displayed is dynamic ( generated by a closure )
- dataProvider - closure that returns the actual data ( must implement pagination )
- dataCount - closure that returns the number of items
If you want to create your own datasource (skip this as a beginner):
- Create a service - which must implement the following methods:
-
generateDynamicColumns() - optional - if you want to be able to generate columns
-
verifyGridConstraints(gridConfig) - verify if the config is setup properly for this datasource impl
-
list(Map listParams = [:], filters = null) - ( * returns the list of rows * by default will return all elements * @param listParams - ( like rowOffset maxRows sort order) * @param filters - array of filters )
-
countRows(filters = null)
-
in case you want to support inline editing you need to define 3 closures ( updateRow , saveRow ,delRow)
-
- declare this service in Config.groovy , under easygrid.dataSourceImplementations
Columns section [see] (https://github.com/tudor-malene/Easygrid/blob/master/src/groovy/org/grails/plugin/easygrid/ColumnConfig.groovy)
The name of each column will be the actual name of the closure. Beside the actual column name, from the name property other properties can be inferred, like: * the label ( the column header ) can be automatically generated ( see below) * in case there is no property or value setting ( see below ), name will be used as the column property ( see below) * also you can access the columns using this name ( in case you want to override some properties in the taglib - see below) * name is also used as the name of the http parameter when filtering or sorting on a column
-
Column Label: The label can be defined , but in case it's missing it will be composed automatically using the 'labelFormat' template - defined in Config.groovy. ( see comments in Config.groovy)
-
Column Value: For each column you have to define the value that will be displayed in the cell. There's 2 options for this: In case the type of the grid is "gorm" or "list", and you just want do display a plain property you can use "property".
Otherwise you need to use the "value" closure, whose first parameter will be the actual row, and it will return whatever you need.
(There is a possibility to define the "property" columns more compact by using the actual property as the name of the column )
- Javascript settings: Another important section of each column is the javascript implementation section. All the properties defined here will be available in the render template to be used in whatever way.
-
enableFilter - if this columns has filtering enabled
-
filterFieldType - one of the types defined per datasource - used to generate implicit filterClosures. In case of gorm the type can be inferred
-
filterClosure: When the user filters the grid content, these closures will be applied to the dataset. In case of grid type = gorm, the search closure is actually a GORM CriteriaBuilder which will be passed to the list method of the domain.
Easygrid also comes integrated with the export plugin This plugin has different settings for each export format , which can pe declared in the config file or in the grid.
Each column has an optional export section, where you can set additional properties like width, etc.
From the example you can also notice the type property of a column. Types are defined in Config.groovy, and represent a collection of properties that will be applied to this column, to avoid duplicate settings.
Default values:
- all columns have default values ( defined in Config)- which are overriden.
- defined globally (Config.groovy) based on the type(class) of the value ( because - usually , applications, have global settings for displaying data . Ex: date format, Bigdecimal - no of decimals, etc.)
- formatters can also be defined per column
The format to apply to a value is chosen this way:
- formatter - provided at the column level
- formatName - a format defined in the formats section of the Config file
- the type is matched to one of the types from the formats section of each datasource
- the value as it is
If you define the property securityProvider : then it will automatically guard all calls to the grid
Easygrid comes by default with a spring security implementation. Using this default implementation you can specify which roles are allowed to view or inline edit the grid
You can customize every aspect of the grid - because everything is a property and can be overriden. You can also add any property to any section of the configuration, and access it from the customizable template ( or from a custom service, for advanced use cases )
The Selection widget is meant to replace drop down boxes ( select ) on forms where users have to select something from a medium or large dataset. It is composed from a jquery autocomplete textbox ( which has a closure attached on the server side) and from a selection dialog whith a full grid (with filtering & sorting), where the user can find what he's looking for, in case he can't find it using the fast autocomplete option. It can also by constrained by other elements from the same page or by statical values. ( for ex: in the demo , if you only want to select from british authors)
You can use any grid as a selection widget by configuring an "autocomplete" section ( currently works only with JqGrid & Gorm )
Like this:
autocomplete {
idProp 'id' // the id property
labelValue { val, params -> // the label can be a property or a closure ( more advanced use cases )
"${val.name} (${val.nationality})"
}
textBoxFilterClosure { filter -> // the closure called when a user inputs a text in the autocomplete input
ilike('name', "%${filter.paramValue}%")
}
constraintsFilterClosure { params -> // the closure that will handle constraints defined in the taglib ( see example)
if (params.nationality) {
eq('nationality', params.nationality)
}
}
}
- idProp - the name of the property of the id of the selected element (optionKey - in the replaced select tag)
- labelProp - each widget will display a label once an item has been selected - which could be a property of the object
- labelValue - or a custom value (the equivalent of "optionValue" in a select tag)
- textBoxFilterClosure - this closure is similar to filterClosure - and is called by the jquery autocomplete widget to filter
- constraintsFilterClosure - in case additional constraints are defined this closure is applied on top of all other closures to restrict the dataset
- maxRows - the maximum rows to be shown in the jquery autocomplete widget
Easygrid provies the following tags:
-
<grid:grid name="grid_name">will render the taglib - see [doc] (https://github.com/tudor-malene/Easygrid/blob/master/grails-app/taglib/org/codehaus/groovy/grails/plugins/easygrid/EasygridTagLib.groovy) -
<grid:exportButton name="grid_name">the export button - see [doc] (https://github.com/tudor-malene/Easygrid/blob/master/grails-app/taglib/org/codehaus/groovy/grails/plugins/easygrid/EasygridTagLib.groovy) has all the attributes of the export tag from the export plugin, plus the name of the grid -
<grid:selection >renders a powerful replacement for the standard combo-box see the taglib document ( see doc and Example)
- in each annotated controller, for each grid defined in "grids" , the plugin injects multiple methods:
*
def ${gridName}Html ()*def ${gridName}Rows ()*def ${gridName}Export ()*def ${gridName}InlineEdit ()*def ${gridName}AutocompleteResult ()*def ${gridName}SelectionLabel ()
Guide on extending the default functionality:
- on installing the plugin you will have 4 templates ( one for each renderer) in your /templates/easygrid folder and a lot of configurations at the end of Config.groovy
- You can customize the templates so that the layout matches your needs
- you can add new properties in the grid or in the column.
General guide for properties:
- all the properties from jqgrid {} ( or visualization, etc, ) will be dynamically added to the javascript framework ( only use properties available in the library documentation )
- you can access any other property from the template, so you can customize the layout for your needs
A: First you need to annotate the controller with @Easygrid, and define the static grids property where you can define the grid. In the gsp (if it belongs to that controller), all you have to do is:
- add <r:require modules="easygrid-jqgrid-dev,export"/> ( or whatever impelementation you're using )
- <grid:grid name="your_grid_name"/>
A: It is so large because the plugin is highly configurable, and designed to minimize code duplication.
I need to customize the grid template. What are the properties of the gridConfig variable from the various templates?
A: Check out GridConfig
A: Check out Filter
A: Because on this implementation you also get the current row so that you can apply the filter on it, as opposed to the gorm implementation where the filter closure is a criteria.
A: You don't have to put view stuff in the controller. You are encouraged to define column types and as many default view properties as possible in the config. Also , you can override any grid property in the grid tag in the gsp.
A: No problem, everything is extensible, just put it in the builder, and you can access it in the template. If it is a implementation attribute, you are encouraged to put it in the implementation section.
A: Yes you can. If there is no securityProvider defined, then no security restrictions are in place.
A: Yes, you can override the defined grid properties from the taglib. Check out the taglib section.
A: No problem, you can replace the export service with your own.
A: Yes.
A: The labelFormat is transformed into a SimpleTemplateEngine instance and populated at runtime with the gridConfig and the prefix.
A: All public methods are guarded by the security provider you defined either in the config or in the grid.
A: The JqGrid plugin is a wrapper over JqGrid. It provides the resources and a nice taglib. But you still have to code yourself all the server side logic. Easygrid goes a step further and allows you to define a grid and it will render it for you.
A: If you already use jqgrid, then you probably have the grid logic split between the controller and the view. If you have inline editing enabled, then you probably have at least 2 methods in the controller. Basically, you need to strip the grid to the minimum properties ( the columns and additional properties) , translate that intro the easygrid builder and just use the simple easygrid taglib. If, after converting a couple of grids, you realize there's common patterns, you are encouraged to set default values and define column types, to minimize code duplication. After the work is done, you will realize the grid code is down to 10%.
A: You can create a gsp template just for it and set it in the builder.
A: It's designed to be flexible, to be able to be used in multiple contexts.
A: Yes, but you will need to also specify the controller & gridName
A: Just create a new autocomplete renderer template and use the selection jquery ui widget
A: You can raise a ticket or drop me an email : tudor.malene at gmail.com, or use the mailing list
- improved export ( support for additional filtering closure, and for custom export values -per column )
- removed the AST transformation that was injecting the grid methods in the controller and used the dynamic-controller plugin for this
( this opens up the possibility of defining grids at runtime - in the next versions )
- added first draft of a dynamic filter form definition ( it may be subject to change in the next versions )
- added possibilty to define grids in other files
- the autocomplete widget can be configured to display the label in the textbox ( instead of in the adjacent div )
- changed the delegate of all the closures defined in the grid to the instance of the parent controller - so you can use any injected service or params, request, etc
- improved error reporting on inline editing
- upgraded jqgrid version to 4.5.4
- cleaned up tests
- improved performance
- fixed bugs
- upgraded jqgrid version to 4.4.4
- added master-slave feature for jqgrid grids
- support for fixed columns for datatables
- added 'globalFilterClosure' - a filter closure that can be used independently of any column
- upgraded rendering templates
- improved performance
- fixed bugs
- refactored exporting so that to take full advantage over the export plugin
- upgraded to grails 2.2.0
- upgraded jqgrid & visualization javascript libraries
- added support for default ( implicit ) filter Closures
- added support for 'where queries' when defining initial criterias
- added default values for the autocomplete section
- the selection widget is now customizable
- improved documentation
- changed the column definition ( instead of the label, the column will be defined by the name)
- resolved some name inconsistencies ( @Easygrid instead of @EasyGrid, etc )
- grids are customizable from the taglib
- columns are accessible by index and by name
- rezolved some taglib inconsistencies
- replaced log4j with sl4j
- first version
- merge the rendering templates to benefit from the latest features in Config.groovy :
- add a default.export.maxRows value
- add default.autocomplete.autocompleteService = org.grails.plugin.easygrid.AutocompleteService
- add default.idColName = 'id'
- if you want to use the dynamic filtering form , you need to add the config for this filterForm { defaults{ filterFormService = org.grails.plugin.easygrid.FilterFormService filterFormTemplate = '/templates/filterFormRenderer' } }
- merge the rendering templates to benefit from the latest features
- some new default configs in Config.groovy
- the autocomplete 'constraintsFilterClosure' has only the 'params' parameter instead of filter
- in the 'defaults' section of the configuration, you must add a export section, where you need to define the service and default parameters for each export type
- on install, templates are copied to the /templates/easygrid folder ( & the default configuration was updated too )
- filter closures now have 1 parameter which is a Filter class
- the labelFormat has a slightly different format (for compatibility reasons with groovy 2.0 - see the comments )
- 'domain' datasource has been replaced with 'gorm' - for consistency
- maxRows - has been added to the autocomplete settings
- a filters section has been added to each datasource implementation , with predefined closures for different column types
-
change the annotation to @Easygrid
-
change the columns ( the column name/property in the head now instead of label)
-
replace datatable to dataTables
-
overwrite or merge the renderers
-
In Config.groovy
- the labelFormat is now a plain string: labelFormat = '${labelPrefix}.${column.name}.label'
- replace EasyGridExportService with EasygridExportService
- replace DatatableGridService with DataTablesGridService and datatableGridRenderer with dataTablesGridRenderer
-
configure the label format for grids
-
in the taglib - replace id with name
Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.