Skip to content

tombray/Easygrid

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Easygrid

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 )

Installation

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

Overview

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.

Features:

  • 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)

Concepts

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) )

Usage

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.

Grid Implementations:

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.

Grid Datasource Types:

  1. 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

  2. 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
  3. 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):

  1. 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)

  2. declare this service in Config.groovy , under easygrid.dataSourceImplementations

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

  1. 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)

  2. 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 )

  1. 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.

Filtering:

  • 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.

Export

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.

Column types

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.

Formatters:

  • 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:

  1. formatter - provided at the column level
  2. formatName - a format defined in the formats section of the Config file
  3. the type is matched to one of the types from the formats section of each datasource
  4. the value as it is

Security:

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

Other grid properties:

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 )

Selection widget

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 )

Online demo

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

Taglib:

Easygrid provies the following tags:

Testing:

  • 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

FAQ:

I want to implement my first grid. What are the steps?

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:

  1. add <r:require modules="easygrid-jqgrid-dev,export"/> ( or whatever impelementation you're using )
  2. <grid:grid name="your_grid_name"/>

Why is the default configuration so large?

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

What is the deal with the Filter parameter of the filterClosures?

A: Check out Filter

Why does the filterClosure of the list implementation have 2 parameters?

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.

Isn't it bad practice to put view stuff in the controller?

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.

I need to pass other view attributes to the ajax grid.

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.

I don't use spring security, can I remove the default implementation?

A: Yes you can. If there is no securityProvider defined, then no security restrictions are in place.

Is it possible to reference the same grid in multimple gsp pages, but with slight differences?

A: Yes, you can override the defined grid properties from the taglib. Check out the taglib section.

I don't like the default export.

A: No problem, you can replace the export service with your own.

Are the grid configs thread safe?

A: Yes.

The labelFormat property is weird.

A: The labelFormat is transformed into a SimpleTemplateEngine instance and populated at runtime with the gridConfig and the prefix.

Are there any security holes I should be aware of?

A: All public methods are guarded by the security provider you defined either in the config or in the grid.

What is the difference between Easygrid and the JqGrid plugin?

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.

I use the jqgrid plugin. How difficult is it to switch to easygrid?

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%.

I have one grid with very different view requirements from the rest. What should I do.

A: You can create a gsp template just for it and set it in the builder.

The value formatting is complicated.

A: It's designed to be flexible, to be able to be used in multiple contexts.

Can I just replace a select box with a selection widget?

A: Yes, but you will need to also specify the controller & gridName

I want to customize the selection widget.

A: Just create a new autocomplete renderer template and use the selection jquery ui widget

I need more information on how to.. ?

I have a suggestion.

A: You can raise a ticket or drop me an email : tudor.malene at gmail.com, or use the mailing list

Version History

1.4.0

- 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

1.3.0

- 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

1.2.0

- refactored exporting so that to take full advantage over the export plugin

1.1.0

- 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

1.0.0

- 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

0.9.9

- first version

Upgrading to 1.4.0

  • 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' } }

Upgrading to 1.3.0

  • 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

Upgrading to 1.2.0

  • 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

Upgrading to 1.1.0

  • 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

Upgrading to 1.0.0

  • 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

License

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.

About

Grails plugin - simple and declarative way of defining a DataGrid

Resources

Stars

Watchers

Forks

Packages

No packages published