Paginate in GSP with an array List - grails

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.

Related

How to display HQL result to view in grails.?

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

grails : how to pass params in remoteFunction

I want to a param using remoteFunction of grails.
HTML
<table class="table table-hover table-bordered" id="profittable">
<thead>
<tr>
<th>Date</th>
<th>Profit</th>
<th>Delete?</th>
</tr>
</thead>
<tbody>
<g:each in="${dailyProfit}" var="dp">
<tr onclick="<g:remoteFunction action='edit' params="[date:${dp.date}]"></g:remoteFunction>" >
<td><g:formatDate format="yyyy-MM-dd" date="${dp.date}"/></td>
<td>
<g:formatNumber number="${dp.profit}" type="currency" currencyCode="PHP" format="###.##" />
</td>
<td>
<g:form controller="dailyProfit" action="delete" >
<g:hiddenField name="date" value="${dp.date.format("yyyy-MM-dd")}" />
<g:actionSubmit class="delete" value="Delete" >
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
</g:actionSubmit>
</g:form>
</td>
</tr>
</g:each>
</tbody>
</table>
ERROR MESSAGE
URI /SampleGrailsApp/dailyProfit/index Class
org.codehaus.groovy.grails.web.taglib.exceptions.GrailsTagException
Message Attribute value quote wasn't closed (action='edit'
params="[date:${dp.date}]").
ACTIONS FOR EDIT
The remoteFunction tag is inside every tr of my table. The plan is, if the row is clicked, the edit page will appear
def edit() {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
Date date = format.parse(params.date);
def dailyProfit = DailyProfit.findByDate(date)
render view:"edit" , model:[dailyProfit : dailyProfit]
}
def update() {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
Date date = format.parse(params.date);
def dailyProfit = DailyProfit.findByDate(date)
if(dailyProfit){
dailyProfit.properties = params
dailyProfit.save(flush:true)
}
list()
}
What is the proper way of passing parameters using remoteFunction tag of grails?
You can do like this .
<tr onclick = "${remoteFunction(
controller: 'xyz',
action: 'edit',update:'divId',
params: [date: dp.date])}" >
This is also a valid syntax:
<tr onClick="<g:remoteFunction action='edit' params="${[param1: 'value', param2: 0]}"></g:remoteFunction>">.....</tr>
Note that the code will translate into an Ajax call with the parameters you indicate. Being an Ajax call, you wont see the page change.
If you want to send the user to the edit page when the row is clicked one option is the following:
<tr onclick='document.location = "<g:createLink action='edit' params="${[date: dp.date]}"/>" '> ... </tr>

How to pass data from one view to other

I Have a table, there i have a details hyperlink on click of which i m showing a new table on same page in other div. The details Hyperlink has 3 input parameters based on which a service method gets triggered and I get data in the data. Now i have this task to show the 2nd grid on a new page. I m not sure how to do it.
Please help.
<table class="table table-bordered table-condensed table-hover ">
<thead>
<tr style="white-space: nowrap;">
<th>Date</th>
<th>Organization No</th>
<th>Contract No</th>
<th>Company Name</th>
<th>Plan No</th>
<th>Status</th>
<th>View Detail</th>
</tr>
</thead>
<tr ng-repeat="mas in vm | startFrom:currentPage*pageSize | limitTo:pageSize" data-ng-class="{active1:$index==selectedRow}" data-ng-click="rowHighilited($index)">
<td>{{mas.startDate | amDateFormat:'YYYY-MM-DD'}} </td>
<td>{{mas.organizationNumber}}</td>
<td>{{mas.contractNumber}} </td>
<td>{{mas.name}}</td>
<td>{{mas.planNumber}} </td>
<td>{{mas.description}} </td>
<td><span>Details</span></td>
</tr>
</table>
//getErrorDetailBySearch(mas.productAccountOid,mas.planNumber,mas.migrationRunID)it calls the function to bring data an binds it to the below div.//
<div ng-show="IsVisible">
<div ng-show="vm1.length > 0 && !loading">
<h2>Error Detail</h2>
<br />
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12 table-responsive">
<table class="table table-bordered table-condensed table-hover table-striped">
<thead>
<tr style="white-space: nowrap;">
<th>Contract Number</th>
<th>Plan Number</th>
<th>Business Error Message</th>
<th>System Error Details</th>
</tr>
</thead>
<tr ng-repeat="mas in vm1">
<td>{{mas.contractNumber}} </td>
<td>{{mas.planNumber}} </td>
<td>{{mas.businessErrorMsg }} </td>
<td>{{mas.systemErrorMsg}} </td>
</tr>
</table>
</div>
</div>
I am able to call a new page Called as ErrorDeatils but i dont know how to pass data b/w the pages.
This is controller written for 1st page
$scope.getErrorDetailBySearch = function (productAccountOid, planNr, migrationrunid) {
var path = "/Utilities/Error";
$location.path(path.replace(/\s+/g, ""));
};
var onSuccess = function (response) {
$scope.vm1 = response;
if ($scope.vm1 < 1) {
messageService.noDataFound();
}
}
$scope.getErrorDetailBySearch = function (productAccountOid, planNr,migrationrunid, skipFormValidate) {
if (skipFormValidate || b360FormsService.validateForm($scope.ViewErrorDetail)) {
errorService.globalproductAccountOid = productAccountOid;
errorService.globalplanNr = planNr;
errorService.globalmigrationrunid = migrationrunid;
$scope.loading = true;
repositoryService.getErrorDetailBySearch(productAccountOid, planNr, migrationrunid).$promise.then(onSuccess, onError).finally(function () {
$scope.loading = false;
});
}
}
Hi you can pass data both as query parameters or route params
Query Parameters
Lets say you have path "/errorDetails" and you want to pass "param" with value "10", then you can use $location.path() as follows:
$location.path("/errorDetails").search({param: 10});
and you can access value of "param" on resultant page's controller as:
$location.search().param;
Route Parameters
For using route params you will need to change your route configurations and you path will be like:
$routeProvider
.when('/errorDetails/:param', {
title: 'Error Details',
templateUrl: 'path/to/errorDetails.html'
});
Now you can send param using $location.path as follow:
$location.path("/errorDetails/"+param);
And you can access route parameter on resultant page's controller as follows:
$routeParams.param

Grails Pagination

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>

Groovy table sorting when the column is a sorted set of a class

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]
}

Resources