I have two simple classes Staff and Department and I want to list staff by department using HQL that then displayed in a view.
First of all these are Domain classes Staff and Department
class Staff {
String fullName
String dateOfBirth
static belongsTo = [department: Department]
}
class Department {
String department
static hasMany = [staff: Staff]
}
Department has instances such as Sea, Land, Air.
Here is a StaffController.groovy (only a listbysea action for example)
def listbysea() {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
//Query
def staffList = Staff.executeQuery("SELECT s.fullName from Staff s join s.department d WHERE d.department = 'Sea')
[staffInstance: staffList, staffInstanceTotal: staffList.size()]
}
This is my listbysea.gsp
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<g:sortableColumn property="fullName" title="${message(code: 'staff.fullName.label', default: 'Full Name')}" />
<g:sortableColumn property="dateOfBirth" title="${message(code: 'staff.dateOfBirth.label', default: 'Date of Birth')}" />
</tr>
</thead>
<tbody>
<g:each in="${staffList}" status="i" var="staffInstance">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<td style="vertical-align: middle;"><g:link action="show" id="${staffInstance.id}"> ${fieldValue(bean: staffInstance, field: "fullName")}</g:link></td>
<td style="vertical-align: middle;"> ${fieldValue(bean: staffInstance, field: "dateOfBirth")}</td>
</tr>
</g:each>
</tbody>
</table>
However, there is no data shown in the table and I am not sure whether the query indeed does not yields any results at all or it is the problem with the view. So I am asking did I do the right thing in returning the query result to the view.? I even tried this query
def staffList = Staff.executeQuery("SELECT new map(s.fullName as fullName, d.department as department)\
FROM Staff as s, Department as d \
WHERE s.department = d HAVING s.department = ('Sea')")
But still no result displayed.
Appreciate any hint.
The variable name you have in your controller doesn't match what the gsp file is using.
In the controller you use staffInstance:
def staffList = Staff.executeQuery("SELECT s.fullName from Staff s join s.department d WHERE d.department = 'Sea')
[staffInstance: staffList, staffInstanceTotal: staffList.size()]
But in the gsp you use staffList:
<g:each in="${staffList}" status="i" var="staffInstance">
Try changing the controller to:
[staffList: staffList, staffInstanceTotal: staffList.size()]
Related
How can I use the <g:paginate\> tag to paginate an array list in a table?
I have this in my controller
def selectevents(){
def events = DomainEvents.findAllByMonth('June')
[events:events, count:events.size()]
}
And I have this in My GSP:
<table id="results-table" class="table table-bordered table-striped" style="width:100%">
<thead>
<tr style="background: #d3d3d3;">
<th style="width: 3%;text-align: center;"></th>
<th style="width: 10%;text-align: center;">Name</th>
</tr>
</thead>
<g:each in="${events}" status="i" var="eventsInstance">
<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
<td>-</td>
<td>${eventsInstance?.Name}</td>
</tr>
</g:each>
and then this:
<g:paginate next="Forward" prev="Back" maxsteps="5" controller="Controller" action="selectevents" total="${count}" />
But the pagination tag doesn't appear. I want to make a pagination to get 5 rows per page, some one?
Your code is calling DomainEvents.findAllByMonth('June') which is returning all of the events for the month of June. You only want to retrieve a subset of 5 of them and then use pagination to navigate around those subsets.
You probably want something like this in your controller:
def showEvents() {
params.max = 5
def count = DomainEvents.countByMonth('June')
def events = DomainEvents.findAllByMonth('June', params)
[domainEventsInstanceCount: DomainEvents.count(), domainEventsInstanceList: events]
}
And then something like this in your GSP:
<div class="pagination">
<g:paginate total="${domainEventsInstanceCount ?: 0}" />
</div>
I hope that helps.
Hello im back with another tedious question!
Trying to get my table to paginate. There are 12 users in the table. Here is my controller function
def listDuplicates(params) {
def result = User.getAllWithDuplicateIDs()
def totalDupCount = result.size()
/*sout for troubleshooting */
System.out.println("Duplicate:" + result.ID + " " + result.username)
params.max = Math.min(params.max ? params.int('max') : 10, 100)
return [resultList: result, totalDupCount: totalDupCount, params:params ]
}
Here is my view
<div>
<fieldset class="warningFieldSet">
<h1 style="color: red" align="center">
<g:message code="Duplicate IDs" />
</h1>
<p style="color: red; margin-left: 20px;">Duplicate IDs Found!</p>
<table>
<thead>
<tr>
<g:sortableColumn property="Username" title="Username" />
<g:sortableColumn property="ID" title="ID" />
<g:sortableColumn property="Status" title="Status" />
</tr>
</thead>
<tbody>
<g:each in="${resultList}" status="i" var="resultDuplicate">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<td>
${resultDuplicate.username}
</td>
<td style="color: red; font-weight: bold">
${resultDuplicate.id}
</td>
<td>
${resultDuplicate.accountStatus }
</tr>
</g:each>
</tbody>
<tfoot>
<g:if test="${totalDupCount >10 }">
<div class="paginateButtons">
<g:paginate action= "listDuplicates" total="${totalDupCount}" />
</div>
</g:if>
</tfoot>
</table>
</fieldset>
</div>
Domain function for finding the duplicate IDs
static List<User> getAllWithDuplicateIDs() {
findAll("FROM User WHERE id IN (SELECT id FROM User group by id having count(*) > 1) AND id != '' ", [])
}
The buttons show up. And in the URL the offset and max is displayed. The table just puts all 12 displayed instead of 10 on one page and 2 on the other. 2 Page numbers show up so It knows that it is only suppose to display only 10 per page. It just isn't doing it in the table itself. Im assuming its some kind of issue with passing params and such.
Any Suggestions/Opinions/Help are/is greatly appreciated!
Grails pagination is based on two parameters: max and offset. max determines the page size, and offset determines where the current page starts. The controller receives these parameters and generally passes them to a database query. The list method added to domain objects by grails handles these parameters, and the finder methods take a queryParams. The usual pattern is to pass the params object directly to list or as the queryParams parameter to the finders. This returns a result set starting at the given offset, with one page length.
In your example, you're calling getAllWithDuplicateIDs without making use of these parameters. Update your query to take them, like this:
static List<User> getAllWithDuplicateIDs(params) {
findAll("FROM User WHERE id IN (SELECT id FROM User group by id having count(*) > 1) AND id != '' ", [], params)
}
Alternatively, page it in memory with something like
results = results.drop(params.offset).take(params.max)
Paging directly in the query is preferable, since it will perform better handle cases where the entire list doesn't fit in memory.
Provide max and offset function params this:
def result = User.getAllWithDuplicateIDs([max:params.max, offset:params.offset])
And use them in in query to database.
Or check the answer how to get results from list with max and offset in answer here
Look at this example .
Domain class ..
class Job {
static belongsTo = [company:Company]
String jobtitle
String jobdescription
String jobskills
String joblocation
String experience
String jobtype
String salary
}
Controller Code..
def uijobs () {
[res:Job.list(params),jobcount:Job.count()]
}
and view is here.
<div class="container" id="main">
<div class="row">
<g:each in="${res}">
<div class="col-sm-4">
<div class="panel panel-warning">
<div class="panel-heading">
<h4 class="panel-title"><g:link action="infopagejob" controller="Job" id="${it.id}">${it.jobtitle}</g:link></h4>
</div>
<div class="panel-body">
<table class="table">
<tr class="info" >
<td > Job Location</td>
<td >${it.joblocation}</td>
</tr>
<tr class="info">
<td>Description</td>
<td>${it.jobdescription}</td>
</tr>
</table>
</div>
</div>
</div>
</g:each>
</div>
<g:paginate next="Forward" prev="Back" maxsteps="10" controller="Job" action="uijobs" total="${jobcount}" params="${params}"/>
</div></div>
getting two arrays from controller and code is --
Sql db = new Sql(dataSource_wldb1) // Create a new instance of groovy.sql.Sql with the DB of the Grails app
def ivrColumns = []
db.eachRow(ivrColumnsQuery) {
rsRow ->
ivrColumns.add(rsRow.name) }
def ivrResults = []
db.eachRow(mssqlQuery) {rows ->
//print rows
ivrResults.add(rows)
}
one has all column names & other has all row data.as below-
return render(view:'xref',model:[ivrcolumns:ivrColumns,ivrresults:ivrResults] )
getting data in below format-
Columns
[ClientKey, Abbr, ConfigKey, Federal, State, DMA, Internal, Wireless, CRssing, CurfewExemption, CampaignID]
Data
[groovy.sql.GroovyResultSetExtension#12f8d75, groovy.sql.GroovyResultSetE
oovy.sql.GroovyResultSetExtension#12f8d75, groovy.sql.GroovyResultSetExtension#1
roovyResultSetExtension#12f8d75, groovy.sql.GroovyResultSetExtension#12f8d75, gr
tSetExtension#12f8d75, groovy.sql.GroovyResultSetExtension#12f8d75, groovy.sql.G
ion#12f8d75, groovy.sql.GroovyResultSetExtension#12f8d75]
view code is---
<g:if test="${ivrcolumns != null }">
<center>Database Location - WLDB1 <br>DB Name - IVR_GUARDIAN </center><br><br>
<table class="table loadTable" >
<thead>
<tr bgcolor="#f0f0f0" >
<g:each in="${ivrcolumns}" status="ii" var="columnivr">
<td nowrap>${columnivr}</td>
</g:each>
</tr>
</thead>
<tbody>
<g:each in="${ivrresults}" status="jj" var="hed">
<tr>
<g:each in="${ivrcolumns}" status="kk" var="col">
<td nowrap>${hed.col}</td> ///please suggest how to do it.
</g:each>
</tr>
</g:each>
</tbody>
</table>
now want to show in GSP page .i am able to display the column but having hard time to display data.not getting how the dot will be used to get correct data to each column.
Will appreciate any help.
thanks
Assuming that's just a sql result, you can just call ${ hed[ col ] } or ${ hed."$col" }
I have a a view that lists several contractor objects. The last column is from addresses which is a sorted set in the contractor object. I am trying to sort those columns and I get an exception that the property addresses is not defined in class Contractors. Do I need to handle the sorted set differently in the controller?
<table width="100%">
<thead>
<tr>
<th></th>
<g:sortableColumn property="status" title="${message(code: 'contractor.status.label', default: 'Status')}" params="[statuses:statuses,name:name,nameOperator:nameOperator ?: 'CONTAINS']"/>
<g:sortableColumn property="contractorName" title="${message(code: 'contractor.contractorName.label', default: 'Name')}" params="[statuses:statuses,name:name,nameOperator:nameOperator ?: 'CONTAINS']"/>
<g:sortableColumn property="addresses.address" title='Address' params="[statuses:statuses,name:name,nameOperator:nameOperator ?: 'CONTAINS']"/>
</tr>
</thead>
<tbody>
<g:each in="${contractorList}" status="i" var="contractor">
<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
<td>
<g:link action="show" id="${contractor.id}">show</g:link>
|
<g:link action="edit" id="${contractor.id}">edit</g:link>
</td>
<td>${contractor.status}</td>
<td>${contractor.contractorName}</td>
<td>${contractor?.addresses?.address}</td>
</tr>
</g:each>
</tbody>
</table>
The logic in the controller:
order(params.sort, params.order)
I am very new to groovy/grails any help would be appreciated, thanks!
I am not sure if there is anything availabel out of the box for this but here is how I solved my similar issue. Basically I had to interecept sort param and do my own sorting.:
if (params.sort && params.sort == "state") {
tasks = Task.activeOnly.open.unassigned.list() //<--my query
tasks = sortByState(tasks,params) //<--custom sort
}
--- where sortByState is this:
private List sortByState(tasks, params){
tasks.sort{Task.STATES.indexOf(it.state)} //<-- this is my custom sorter
if (params.order == "DESC" ) {
tasks = tasks.reverse()
}
def begin = params.int('offset') //<-- next two lines are for paging offsets
def end = Math.min(begin+params.int('max'),tasks.size()-1)
return tasks[begin..end]
}
On index.gsp I have this to redirect it to list.gsp so I'm imagining is should be something like this:
${response.sendRedirect("entry/list")}
My filter is just one textField and two datePickers with drop down boxes (DD-MMM-YYYY)
and they should be by default filtered from today's date to infinity. So it should show me only the events that have not happened yet, but the old ones should still be in the database.
Here are the links to my last 3 questions for background information
I have a bunch of data and I need a data filter using Grails
Grails: filter data in a Grails table dynamically
Grails: Edit and Delete links not working
I think the second one is the one with most of my code.
Any help would be greatly appreciated and well rated. Thanks! :)
And a again special thanks to proflux for all the help!
UPDATE
Here is the in the list.gsp
<g:each in="${entryInstanceList}" status="i" var="entryInstance">
<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
<td><g:formatDate format="dd-MMMM-yyyy" date="${entryInstance.fechaCambio}" /></td>
<td><b>${fieldValue(bean: entryInstance, field: 'proyectoRuta')}</b></td>
<td>${fieldValue(bean: entryInstance, field: 'summary')}</td>
<td><g:formatDate format="dd-MMM-yyyy HH:mm z" date="${entryInstance.lastUpdated}" /></td>
<td>
<g:form>
<g:hiddenField name="id" value="${entryInstance?.id}" />
<span class="simple"><g:actionSubmit class="editar" action="edit" value="${message(code: 'default.button.editar.label', default: ' ')}" /></span>
<span class="simple"><g:actionSubmit class="eliminar" action="delete" value="${message(code: 'default.button.eliminar.label', default: ' ')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Esta seguro que desea Eliminar?')}');" /></span>
</g:form>
</td>
</tr>
</g:each>
UPDATE
Here is the searchResults code,
def searchResults = {
def entryCriteria = Entry.createCriteria()
def results = entryCriteria.list {
and{if(params?.fechaCambioD && params?.fechaCambioH) {
between("fechaCambio", params.fechaCambioD, params.fechaCambioH)
}
if(params?.lastUpdatedD && params?.lastUpdatedH) {
between("lastUpdated", params.lastUpdatedD, params.lastUpdatedH)
}
if(params?.proyectoRutaN) {
ilike("proyectoRuta","%${params.proyectoRutaN}%")
}
}
}
render(view:'searchResults', model:['results':results, 'proyectoRutaN':params?.proyectoRutaN, 'fechaCambioD':params?.fechaCambioD, 'fechaCambioH':params?.fechaCambioH, 'lastUpdatedD':'', 'lastUpdatedH':params?.lastUpdatedH])
}
And since I am already using results and is a list, what should I do, give it another name and at the end put it to model after results?? like:
render(view:'searchResults', model:['results':results, 'otherResults':otherResults, 'proyectoRutaN':params?.proyectoRutaN, 'fechaCambioD':params?.fechaCambioD, 'fechaCambioH':params?.fechaCambioH, 'lastUpdatedD':'', 'lastUpdatedH':params?.lastUpdatedH])
Or should I just define it inside results??
UPDATE
<table><tbody class="yui-skin-sam">
<tr class="prop">
<td valign="top" class="name"><b>Proyecto/Ruta: </b> <g:textField name="proyectoRutaN" value="${proyectoRutaN}" /></td>
<td></td>
<td valign="top" class="name"><b>Fecha de Implementación: </b></td>
<td></td>
<td valign="top" class="name"><b>Fecha de Última Modificación: </b></td>
</tr>
<tr class="prop">
<td></td>
<td valign="top">Desde:</td>
<td valign="top"><gui:datePicker name="fechaCambioD" value="${params?.fechaCambioD}" formatString="dd/MMM/yyyy"/></td>
<td valign="top">Desde:</td>
<td valign="top"><gui:datePicker name="lastUpdatedD" value="${params?.lastUpdatedD}" default="none"/></td>
</tr>
<tr class="prop">
<td></td>
<td valign="top">Hasta:</td>
<td valign="top"><gui:datePicker name="fechaCambioH" value="${params?.fechaCambioH}" formatString="dd/MMM/yyyy"/></td>
<td valign="top">Hasta:</td>
<td valign="top"><gui:datePicker name="lastUpdatedH" value="${params?.lastUpdatedH}" default="none"/></td>
</tr>
For some reason when I apply the filters then it shows me everything on the list (it does not filter) and the textFields in the filter do not save the date from when the filter was applied.
Any ideas on how I can fix this?
For the fechaCambio in the criteria, what you can do is something like this:
def searchResults = {
def fromCal
if(params?.fechaCambioD) {
fromCal = Calendar.getInstance()
fromCal.setTime(param?.fechaCambioD)
fromCal.set(Calendar.HOUR_OF_DAY,0)
fromCal.set(Calendar.MINUTE,0)
fromCal.set(Calendar.SECOND,0)
fromCal.set(Calendar.MILLISECOND,0)
}
def toCal
if(params?.fechaCambioH) {
toCal = Calendar.getInstance()
toCal.setTime(param?.fechaCambioH)
toCal.set(Calendar.HOUR_OF_DAY,23)
toCal.set(Calendar.MINUTE,59)
toCal.set(Calendar.SECOND,59)
toCal.set(Calendar.MILLISECOND,999)
}
def entryCriteria = Entry.createCriteria()
def results = entryCriteria.list {
and{if(params?.fechaCambioD && params?.fechaCambioH) {
between("fechaCambio", fromCal.getTime(), toCal.getTime())
}
if(params?.lastUpdatedD && params?.lastUpdatedH) {
between("lastUpdated", params.lastUpdatedD, params.lastUpdatedH)
}
if(params?.proyectoRutaN) {
ilike("proyectoRuta","%${params.proyectoRutaN}%")
}
}
}
render(view:'searchResults', model:['results':results, 'proyectoRutaN':params?.proyectoRutaN, 'fechaCambioD':params?.fechaCambioD, 'fechaCambioH':params?.fechaCambioH, 'lastUpdatedD':'', 'lastUpdatedH':params?.lastUpdatedH])
}
Again, this is the quick and dirty approach; not very elegant but if I understand your problem it should do what you want.
Instead of doing the redirect on the GSP, it would probably be better to update your index controller method to do something like:
def index {
redirect(action:'list')
}
This way your controller logic is all in the controller and not in both the GSP and controller.
Your second requirement is a little trickier, but not too bad. Basically, you need up update your criteria logic. I'll assume that you only care about two cases:
you have both fechaCambioD and fechaCambioH
you have fechaCambioD and no fechaCambioH (your default - from today until the end of time)
And you don't care about finding all the entries from the beginning of time up until fechaCambioH
If that's the case, change your controller logic to:
def fechaCambioD = params?.fechaCambioD ?: new Date()
def results = entryCriteria.list {
if(params?.fechaCambioD && params?.fechaCambioH) {
between("fechaCambio", params.fechaCambioD, params.fechaCambioH)
}
else {
gte("fechaCambio", fechaComabioD)
}
if(params?.lastUpdatedD && params?.lastUpdatedH) {
between("lastUpdated", params.lastUpdatedD, params.lastUpdatedH)
}
if(params?.proyectoRutaN) {
ilike("proyectoRuta","%${params.proyectoRutaN}%")
}
}
So we essentially:
If you didn't get a fechaCambioD parameter, we're going to default fechaCambioD to the current time.
If we don't have both fechaCambioD and fechaCambioH we're going to find all entries that have a fechaCambio greater than the current time.
I believe that should work, but if not let me know...
UPDATE:
Okay, my mistake, there is no gte comaparator, it's just ge for greater than or equal and gt for greater than. Since your search criteria is simpler for list, you can probably just change the method to:
def list = {
def today = Calendar.getInstance()
today.set(Calendar.HOUR, 0)
today.set(Calendar.MINUTE, 0)
today.set(Calendar.SECOND, 0)
today.set(Calendar.MILLISECOND, 0)
def results = Entry.findAllByFechaCambioGreaterThanEquals(today.getTime())
render(view:'list', model:['entryInstanceList':results])
}
If this doesn't work let me know...