FilterPane Plugin
Help Grails be an active, open community! If you use this plugin and haven't rated it, please do so. If you've rated this plugin anything but 5 stars, please let me know why (See #Support for how). I'm striving to make this plugin the best that it can be, and constructive feedback is always appreciated.
This plugin adds filtering capabilities to any Grails application. The primary goals of this plugin include:
- Easy integration with Grails list pages via custom tags
- Smart operator display. Only practical filter operations are available for a property's data type. (e.g. no "like" operators for numeric properties)
- Support for major operators including =, <>, >, <, >=, <=, like, ilike, between, is null, and is not null.
- Smart filter value entry. Date properties display a date picker, boolean's display radio buttons, etc.
- Support for a custom list of values, such as a filtering a text property with a constrained list of values.
- Works with Grails list sorting out of the box with only minor modification to your list gsp.
- Works with Grails pagination out of the box with only minor modification to your list gsp.
- Honors domain constraints: nullable, blank, inList ( Since 0.4 )
Please note that you may experience duplicate rows if you use eager fetching. See http://jira.codehaus.org/browse/GRAILSPLUGINS-2063 for more information.
Index
Usage
The plugin is typically used in an application's list pages. To use the filters, make the following changes to your list.gsp pages. For a full list of tags and attributes, see the Reference section later in this document.
list.gsp
- Add the javascript and stylesheet includes to the head section of your page.
2. Somewhere in your page (typically near the bottom of the body tag), add the filter pane. This has the result of rendering a container div in your page.
<filterpane:filterPane domainBean="MyDomainClass" />
3. Add a button to invoke the filter pane somewhere on your list page. (Inside the pagination div works well on applications built from scaffolding.)
A custom tag is provided to help create this button. The title attribute is optional. If omitted, the value of the button will be "Filter".
<filterpane:filterButton text="Whatever You Wish" />
If your application uses scriptaculous and you use the filterButton tag, the filter pane will fade in and out. Otherwise the filter pane simply appears and disappears.
Pagination
If you want to support pagination use something similar to the following:
<g:paginate total="${bookCount == null ? Book.count(): bookCount}" params="${filterParams}" />
In the example above,
bookCount is any variable you pass into the controller's render model that contains the total number of records returned in your filtered data. Its value can be obtained from the filter service's
count method.
(See the controller section later in this document for more info.)
Note that you should not use Groovy's Elvis operator to test for bookCount's existence, as an empty set (count of 0) will return false, thus causing Book.count() to be used. See Groovy Truth for more info.
Also in the example above, filterParams is set in the model map of the render call in the controller. The value is a sub-map of the request params, and can be obtained by calling the "extractFilterParams" method on the included FilterUtils class. See the example below from the books example app.
render( view:'list',
model:[ bookList: filterService.filter( params, Book ),
bookCount: filterService.count( params, Book ),
filterParams: com.zeddware.grails.plugins.filterpane.FilterUtils.extractFilterParams(params),
params:params ] )Starting with version 0.6 of the plugin, you can use the new paginate tag. The paginate tag wraps the Grails paginate tag and encapsulates the steps listed above. See the
#paginate tag for more info.
List Sorting
If you want to support Grails's list sorting, you must add a the filter parameters to each Grails sortableColumn tag, as shown in the example below. It is not recommended to put the entire "params" map in here. Instead, it is recommended that you only include the FilterPane parameters, which is the same sub-map as is described in the Pagination section above.
<g:sortableColumn property="id" title="Id" params="${filterParams}" />Controller
- Inject the filter service into your controller.
2. Create the filter action in your controller.
def filter = {
if(!params.max) params.max = 10
render( view:'list',
model:[ domainClassList: filterService.filter( params, DomainClass ),
domainClassCount: filterService.count( params, DomainClass ),
filterParams: com.zeddware.grails.plugins.filterpane.FilterUtils.extractFilterParams(params),
params:params ] )
}Where…
- domainClassList is the name of the return list model the list action uses
- DomainClass is the name of the domain class you are filtering
- domainClassCount is the name of a variable that contains the total number of filtered results. This parameter is optional, but is useful if you want to use Grails pagination (which most of the time you will).
- filterParams is a sub-map of the request parameters obtained via the FilterUtils.extractFilterParams method.
Keep in mind that you don't have to name the action "filter". You can name it anything you want, just remember to assign the same name to the filterPane tag's action attribute so your action gets called when applying the filter.
Examples
An example Grails application can be downloaded from
http://svn.codehaus.org/grails-plugins/grails-filterpane/trunk/docs/examples/books.zip. It provides a tiny filterable book database that demonstrates the usage of the filterpane plugin.
Plugin Version History
| Date | Version | Notes | Known Compatible Grails Versions |
|---|
| 2010-07-26 | _0.7 | Bug fixes. | 1.1.1 + |
| 2010-03-24 | _0.6.8 | Bug fix for boolean 'false' value not working | 1.1.1 + |
| 2010-03-23 | _0.6.7 | Some bug fixes and improvements to Grails-1.2.1 compatibility | 1.1.1 + |
| 2010-02-17 | _0.6.6 | Should now work with Grails-1.2.1 and 1.1.1 | 1.1.1 + |
| 2010-02-03 | _0.6.5 | Fixed several issues. See http://jira.codehaus.org/browse/GRAILSPLUGINS/fixforversion/16016 | 1.1.1 + |
| 2009-11-17 | _0.6.4 | Added customForm attribute to filterPane tag. See docs for details in the reference section below. | 1.1.1 |
| 2009-10-27 | _0.6.3 | Fixed http://jira.codehaus.org/browse/GRAILSPLUGINS-1629 | 1.1.1 |
| 2009-09-13 | _0.6.2 | Child collection filtering fixed (GRAILSPLUGINS-1503). Filter service reworked to use Grails API instead of Groovy meta classes. Several integration tests added. | 1.1.1 |
| 2009-07-28 | _0.6.1 | Fixed a minor issue when rendering dropdowns for enums in associated properties. | 1.1.1 |
| 2009-07-27 | _0.6 | Completed several JIRA issues. See http://jira.codehaus.org/browse/GRAILSPLUGINS/fixforversion/15149 for details. | 1.1 |
| 2009-04-07 | _0.5 | Fixed JIRA issues 836, 988, and 1045. Form action is now POST. Entering in a filter value will now auto-select the first operator in its associated dropdown if none is selected (except for date properties). More info on issues at http://jira.codehaus.org/browse/GRAILSPLUGINS/fixforversion/15114 | 1.1 |
| 2009-03-22 | _0.4.3 | JIRA GRAILSPLUGINS-985 fixed. | 1.1 |
| 2009-03-22 | _0.4.2 | Now compatible with Grails 1.1. See JIRA GRAILSPLUGINS-999 | 1.0.4, 1.1 |
| 2009-03-22 | _0.4.1 | Completed JIRAs GRAILSPLUGINS-822 and GRAILSPLUGINS-903 | 1.0.4 |
| 2009-01-27 | _0.3.1 | Moved debug output to log.debug statements. | 1.0.4 |
| 2009-01-26 | _0.3 | Fixed packaging glitches. Updated example app. | 1.0.4 |
| 2009-01-25 | _0.2 | No major changes. Mainly code cleanup. Classes are now in packages, as per plugin specs. | 1.0.4 |
| 2009-01-16 | _0.1 | First release. | 1.0.4 |
Roadmap
- Minor versions of 0.6 will address issues that arise with 0.6 and any critical bugs that are found.
- Revised documentation that is more user friendly and improved examples of each feature.
- Release 0.7 will focus on adding more unit and functional tests and simplifying the code base.
- Future releases will be kept smaller than 0.5 and 0.6 in an attempt to deliver changes faster.
Support
- For support questions, please use the Grails user mailing list (user@grails.codehaus.org), and include the word "filterpane" somewhere in your message.
- JIRA issues may be found here.
Reference
The reference section is only applicable to versions 0.4 and above. Version 0.4 addressed some redundancy in the tag and attribute names provided by the plugin. While all previous attribute names are still supported, it is recommended to use those documented below instead. The deprecated tag and attribute names may be removed in the future.
Data Types and their Available Filter Operators
Text
(String, char) | Operator | Select Option Display Text |
|---|
| ILike | Contains |
| Not ILike | Does Not Contain |
| Like | Contains (Case Sensitive) |
| Not Like | Does Not Contain (Case Sensitive) |
| "=" | Equal To |
| <> | Not Equal To |
| Is Null | Is Null |
| Is Not Null | Is Not Null |
Numeric
(Integer, Long, Short, Float, Double, BigDecimal, BigInteger) | Operator | Select Option Display Text |
|---|
| "=" | Equal To |
| <> | Not Equal To |
| < | Less Than |
| <= | Less Than or Equal To |
| > | Greater Than |
| >= | Greater Than or Equal To |
| Between | Between |
| Is Null | Is Null |
| Is Not Null | Is Not Null |
Date
| Operator | Select Option Display Text |
|---|
| "=" | Equal To |
| <> | Not Equal To |
| < | Less Than |
| <= | Less Than or Equal To |
| > | Greater Than |
| >= | Greater Than or Equal To |
| Between | Between |
| Is Null | Is Null |
| Is Not Null | Is Not Null |
Boolean
| Operator | Select Option Display Text |
|---|
| "=" | Equal To |
| <> | Not Equal To |
| Is Null | Is Null |
| Is Not Null | Is Not Null |
Enum
(since 0.6) | Operator | Select Option Display Text |
|---|
| "=" | Equal To |
| <> | Not Equal To |
Domain Constraint Modifications
If a property is not nullable (constraint nullable="false"), the
Is Null and
Is Not Null operators will not be available for that property.
Tags
includes
The includes tag should be used in the head section of your pages. It includes the necessary stylesheet and javascript file for the plugin. There are no attributes for this tag.
currentCriteria
This tag renders an unordered list of the currently applied filter criteria, along with links to remove individual filter criteria.
| Attribute Name | Required | Default Value | Description |
|---|
| domainBean | Yes | The domain bean being filtered. May be a String (e.g. "Book") or a class instance (e.g. ${Book}) |
| id | No | filterPaneCurrentCriteria | The id of the unordered list. |
| title | No | None | The title attribute for the unordered list. |
| class | No | None | The CSS class to apply to the list. |
| style | No | None | The style attribute for the list. |
| dateFormat | No | yyyy-MM-dd HH:mm:ss | The format to apply when displaying date criteria. |
| action | No | filter | The controller action to submit to when removing criteria. Set this to the same as your filterPane tag's action attribute. |
| removeImgDir | No | None | The directory the remove image file is located in. |
| removeImgFile | No | None | The image filename to be used for the remove link. Note that if either removeImgDir or removeImgFile are missing, the text "(X)" will be used for the remove link. |
filterButton
This tag renders an HTML link that shows the filter pane when clicked. When one or more filters are applied, this button will have the
filter-applied css class.
| Attribute Name | Required | Default Value | Description |
|---|
| text | No | "Filter" | The text that is displayed on the button. |
| appliedText | No | "Change Filter" | The text displayed on the button when one or more filters are applied. |
| id | No | "filterpane" | The id of the html element. |
| textKey | No | "fp.tag.filterButton.text" | The message bundle key that contains the text of this button. (i18n) |
| appliedTextKey | No | "fp.tag.filterButton.appliedText" | The message bundle key that contains the applied text of this button. (i18n) |
filterPane
This tag generates the filter pane itself. As of release 0.4, this tag pulls as much filtering information from the domain class as possible by default. All attributes from 0.3.1 are still supported, but are considered deprecated in favor of more sensible alternatives.
| Attribute Name | Required | Default Value | Description |
|---|
| domainBean | Yes | None | The name of the Grails domain class to be filtered. This can either be a string (e.g. "Book"), or an actual class instance (e.g. "${Book}") |
| title | No | "Filter" | The title text that is displayed at the top of the filter pane. |
| titleKey | No | None | A message bundle key for looking up title text. (i18n) |
| id | No | "filterpane" | The id of the container div that holds the filter pane. |
| class | No | None | The class attribute to add to the container div that holds the filter pane. |
| style | No | None | The style attribute to add to the container div that holds the filter pane. |
| formName | No | "filterForm" | The name of the filter form element. Useful if you will use custom javascript on the form. |
| filterProperties | No | None | If specified then no default properties are included, only those specified. |
| associatedProperties | No | None | Use this if you wish to filter any properties of associated domain objects (e.g. author.lastName). The value may either be a comma-delimited string, or a List of strings. |
| additionalProperties | No | None | By default, identifier, version, and lastUpdated properties are not available to filter by. Use this attribute to allow them to be filtered. The value may either be a comma-delimited string, or a List of strings. Valid values are "id", "identifier", "version", and "lastUpdated" |
| excludeProperties | No | None | By default all persistent properties of the domain object are filterable. If you wish to exclude any properties from filtering, specify them in this attribute. The value may either be a comma-delimited string, or a List of strings. |
| action | No | "filter" | The controller action to call when the filter is applied. |
| filterPropertyValues | No | None | A map of property values to pass through to each property's value form control. For example, since Date properties render a date picker control, you could pass the following to limit the years in the date picker: filterPropertyValues="${[someDateProperty:[years:2015..1999]]}" or get the values from the database: filterPropertyValues="${['author.lastName':[values:Author.executeQuery('select t.lastName from Author t')], 'readPriority.name':[values:ReadPriority.list()]]}" |
| customForm | No | false | If true or "true", the tag will not render the surrounding form or the "Apply" button on it. This is left to the developer. This attribute is useful if you want to embed the filterpane form in an existing form. (since 0.6.4) See the example below: |
<form id="author-form" name="author-form" method="post">
<filterpane:filterPane id="author-filter-pane" domainBean="com.zeddware.grails.plugins.filterpane.Author"
associatedProperties="books.title, books.bookType"
titleKey="fp.tag.filterPane.titleText" customForm="true"
formName="author-form"/>
<g:actionSubmit value="Apply Filter From Outside Filter Pane" action="filterCustomForm" />
</form>
isFiltered
This is a logical tag that will render its body if any filters are applied.
| Attribute Name | Requred | Default Value | Description |
|---|
| (None) | | | |
isNotFiltered
This is a logical tag that will render its body if no filters are applied.
| Attribute Name | Requred | Default Value | Description |
|---|
| (None) | | | |
paginate
This is a convenience tag that may be used in place of the grails paginate tag. It encapsulates adding the appropriate parameters to the grails paginate tag for the developer.
| Attribute Name | Required | Default Value | Description |
|---|
| total | Yes | None | A numeric total to be used when calculating pages. This is the same total that is passed to the Grails paginate tag. |
| domainBean | Yes, if total's value is null | None | If total's value is null, this tag is used to perform a count for the user and pass that to the Grails paginate tag. If not specified and total's value is null, 0 will be used as the total count. |
Internationalization (i18n)
The following keys are supported in message bundles for internationalizing the text in the plugin.
Filter Operators
- fp.op.ILike
- fp.op.NotILike
- fp.op.Like
- fp.op.NotLike
- fp.op.Equal
- fp.op.NotEqual
- fp.op.IsNull
- fp.op.IsNotNull
- fp.op.GreaterThan
- fp.op.GreaterThanEquals
- fp.op.LessThan
- fp.op.LessThanEquals
- fp.op.Between
Property Name Display
By default, each property's natural name is displayed . To override this, use keys of the form:
fp.property.text.propertyName , where
propertyname is the domain property name. For associated properties, use keys of the form:
fp.property.text.associatedBean.propertyName .
Filter Button Tag
If no textKey or appliedTextKey attributes are given, the default keys are checked.
- fp.tag.filterButton.text
- fp.tag.filterButton.appliedText
Filter Pane Tag
The filterPane tag's keys are listed below, along with their default values for clarity.
- fp.tag.filterPane.titleText=Filter
- fp.tag.filterPane.property.boolean.true=Yes
- fp.tag.filterPane.property.boolean.false=No
- fp.tag.filterPane.property.betweenValueSeparatorText=and
- fp.tag.filterPane.sort.orderByText=Order by
- fp.tag.filterPane.sort.noSelection.text=Select a Property
- fp.tag.filterPane.sort.ascending=Ascending
- fp.tag.filterPane.sort.descending=Descending
- fp.tag.filterPane.button.cancel.text=Cancel
- fp.tag.filterPane.button.clear.text=Clear
- fp.tag.filterPane.button.apply.text=Apply
The full default message bundle can be found in the installed plugin's folder in the messages-filterpane.properties file.
The plugin supports the
i18n Templates plugin's domain property format:
<domainClass>.<property> <-- for each property