While the list screen is useful with sorting and pagination, it would be nice to filter the number of items on a list. This tutorial provides a way of implementing this.
For this tutorial, I'll show an employee filter. To make things a bit more useful, I'll also allow the user to filter by Department. Shown below is the datamodel.
My domain classes are as follows.
class Employee {
String firstName
String lastName
Department department
static mapping = { version false }
}
class Department {
String name
String toString() {"${this.name}"}
static mapping = { version false }
}
Lets have a look at the list without filters. Notice that there are around 100 employees from different departments.
I want to be able to filter by Department, First Name, and Last Name. I will add the code for the filters in views/employee/list.gsp. I created this file by running
grails generate-all Employee
The following snippits of code is from my edited version of list.gsp. Notice, I have added 3 new inputs fields. I have placed them above the List table. Notice I have used flash to default values. More on this later. You can see the full source here
<g:select name='department'
noSelection="['':'']"
optionKey="id"
value="${flash.department}"
from='${Department.list()}'>
</g:select>
<input type="text" id="firstName" name="firstName" value="${flash.firstName}"/>
<input type="text" id="lastName" name="lastName" value="${flash.lastName}"/>
The list screen now looks like this.
Of course, the filtering won't actually work unless we modify the controller to change the database query. He is the list method of the EmployeeController.groovy. The key thing to note is that I'm putting the request parameter back into flash so I can have the value defaulted when the list.gsp displays.
def list = {
//keep these values so we can rerender on the filters
flash.firstName = params.firstName
flash.lastName = params.lastName
flash.department = params.department
if(!params.max) {
params.max = 10
}
def query
def criteria = Employee.createCriteria()
def results
query = {
and {
like("firstName", params.firstName + '%')
like("lastName", params.lastName + '%')
if(params.department){
def selectedDepartment = Department.get(Integer.parseInt(params.department))
eq('department', selectedDepartment )
}
}
}
results = criteria.list(params, query)
render(view:'list', model:[ employeeList: results ])
}
I also need to change my list.gsp slightly. Note, I've changed the pagination tag and the each tag. We need to pass the flash params in the paginate tag so the controller will still know what the filter criteria was when the user selects next/prev. Full source code here.
<g:each in="${employeeList}" status="i" var="employee">
<g:paginate total="${employeeList.getTotalCount()}" params="${flash}"/>
And thats it, you should now be able to filter on Department, First Name, and Last Name. I suppose this could be scaffolded. It might get a bit more complex when dealing with dates, but it is not impossible.
Sunday, July 27, 2008
Subscribe to:
Post Comments (Atom)
12 comments:
Simple and useful! Thanks!
I like this idea to show a way to filter records.I am not sure when I created this that I had The Employee filter for department. How do you get Employees added to department?. I created the screen to add departments.
What am I missing?
Thanks
Jim
Jim,
In my empoyee/create.gsp, I have the following code
<tr class="prop">
<td valign="top" class="name">
<label for="department">Department:</label>
</td>
<td valign="top" class="value ${hasErrors(bean:employee,field:'department','errors')}">
<g:select optionKey="id" from="${Department.list()}" name="department.id" value="${employee?.department?.id}" ></g:select>
</td>
</tr>
I commented as anonymous previously.
I restudied your example. I saw where I could add employees.
It's a great article for me.!!!!!!!
Thanks
Jim
Great example, it helped me a lot.
You might want to add params="${flash}" to the sortable columns so you will order the filtered result set.
Thanks very much Chris
Would it be possible for you to post the EmployeeController.groovy file as well?
class EmployeeController {
def index = { redirect(action:list,params:params) }
// the delete, save and update actions only accept POST requests
def allowedMethods = [delete:'POST', save:'POST', update:'POST']
def list = {
//keep these values so we can rerender on the filters
flash.firstName = params.firstName
flash.lastName = params.lastName
flash.department = params.department
if(!params.max) {
params.max = 10
}
def query
def criteria = Employee.createCriteria()
def results
query = {
and {
like("firstName", params.firstName + '%')
like("lastName", params.lastName + '%')
if(params.department){
def selectedDepartment = Department.get(Integer.parseInt(params.department))
eq('department', selectedDepartment )
}
}
}
results = criteria.list(params, query)
render(view:'list', model:[ employeeList: results ])
}
def show = {
def employee = Employee.get( params.id )
if(!employee) {
flash.message = "Employee not found with id ${params.id}"
redirect(action:list)
}
else { return [ employee : employee ] }
}
def delete = {
def employee = Employee.get( params.id )
if(employee) {
employee.delete()
flash.message = "Employee ${params.id} deleted"
redirect(action:list)
}
else {
flash.message = "Employee not found with id ${params.id}"
redirect(action:list)
}
}
def edit = {
def employee = Employee.get( params.id )
if(!employee) {
flash.message = "Employee not found with id ${params.id}"
redirect(action:list)
}
else {
return [ employee : employee ]
}
}
def update = {
def employee = Employee.get( params.id )
if(employee) {
employee.properties = params
if(!employee.hasErrors() && employee.save()) {
flash.message = "Employee ${params.id} updated"
redirect(action:show,id:employee.id)
}
else {
render(view:'edit',model:[employee:employee])
}
}
else {
flash.message = "Employee not found with id ${params.id}"
redirect(action:edit,id:params.id)
}
}
def create = {
def employee = new Employee()
employee.properties = params
return ['employee':employee]
}
def save = {
def employee = new Employee(params)
if(!employee.hasErrors() && employee.save()) {
flash.message = "Employee ${employee.id} created"
redirect(action:show,id:employee.id)
}
else {
render(view:'create',model:[employee:employee])
}
}
def newEmployeeWizardFlow = {
}
}
This is like something I'm trying to do at the moment, so it's very helpful. How does the filter get executed, though? When the user refreshes the list having entered values into the filter fields? Would it work if you had a search button there? I imagine you'd have to pass it the parameters it needed.
There is a search button.
Thank you for posting this
I tried implementing this with Grails 1.3.6 and had a few issues.
* Instead of a ParseInt to match the department, I had to use a ParseLong.
* Instead of matching to department, I had to match to department.id
So, it looked like this:
eq('department.id', Long.parseLong(params.department))
Also, this code doesn't work with the standard table-sorting provided by g:sortableColumn. I ended up storing the selections in session rather than flash so that it would persist through sorting changes, together with a bit of logic surrounding this change.
Thanks for the original code, though!
Thanks, this was extremely helpful.
Post a Comment