I want to display the results of a queries on several pages, so I use the tag pagination.
I have correct outputs (I want 5 result per page) in all the pages but the first one.
In the first page, all results are displayed.
Controller :
def questions(){
def questions = Question.list(params)
[questions: questions, total: Question.count()?:0, tags: Tags.list(), params: params]
}
View
<g:each in="${questions}" var="question">
<div class="row">
${question?.body }
</div>
</g:each>
<div class=pagination>
<g:paginate controller="Question" action="list" total="${total}" max="5" params="${params}"/>
</div>
Why do I have all results in the first page ?
This happens because params has no max value.
setting params.max = 10 and then invoking questions() would result in 10 items for the first page.
// will result in Question.list(max: 10)
def questions = Question.list(params)
But, make sure if same action is called again for subsequent pages then params.max has to be set to 5 or totally removed because max from <g:paginate> would take care of the rest.
You can add following line at the beginning of your controller action:
params.max = params.max ?: 5
If no max parameter is given the default value will be 5.
Related
I am working with Grails 2.4.2. I have a view page named distGameList where I am showing the distributed game list. So when the list is over than 10 it should place other values in next page. But It's not doing it. In-fact It is creating 2 pages. I have 12 rows in that table. Now in view page 2 page is showing that's OK. But 12 rows is showing each pages although it should show 10 rows at first page and 2 rows in second page. Here are my steps below ::
in my controller ::
def distGameList(){
def distributedGameList = AndroidGameDist.getAll()
[distributedGameListInstance : distributedGameList, androidDistGameInstanceCount: AndroidGameDist.count()]
}
and in my view where paginate tag after ending table tag ::
<div class="pagination">
<g:paginate total="${androidDistGameInstanceCount ?: 0}" />
</div>
Use list for pagination and pass params in it.
def distributedGameList = AndroidGameDist.list(params)
or
Use this for more readable code:
def distributedGameList = AndroidGameDist.list(max: params.max ?: 10, offset: params.offset ?: 0)
I am trying to create a search action in my grails app that accepts a number of criteria and displays a table (similar to a default index action) of instances that match the criteria.
For search criteria that have many results I would like to use the paginate tag:
<g:paginate total="${alarmInstanceCount ?: 0}" />
By default, this tag forgets the search criteria. I believe that the params attribute allows me to add the search parameters to the links that the paginate tag creates.
To encapsulate my search criteria, I've organized them into their own map called searchParams.
However, when I try to pass my searchParams to the paginate tag:
<g:paginate total="${alarmInstanceCount ?: 0}" params="${[searchParams:searchParams]}"/>
my searchParams are turned into a string.
Here is an example of what I mean.
When I first open the search page the controller reports the params as follows:
[action:index, format:null, controller:alarm, max:10]
null
class org.codehaus.groovy.runtime.NullObject
So, since the params map doesn't contain an entry for searchParams it comes up as null and has the NullObject class. My code handles this case gracefully.
When I enter some text into the channelName field the controller reports the params as follows:
[searchParams.channelName:SWR, searchParams:[channelName:SWR], _action_index:List, _method:PUT, action:index, format:null, controller:alarm, max:10]
[channelName:SWR]
class org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap
So, now the params map has a searchParams entry which refers to a GrailsParameterMap. This is the desired behaviour, and the controller interprets this correctly.
However, if I click on the first entry in the paginate bar, the controller reports the params as follows:
[max:10, searchParams:[channelName:SWR], offset:10, action:index, format:null, controller:alarm]
[channelName:SWR]
class java.lang.String
In this third case, the params map has a searchParams entry that looks correct, but is actually a String object. This causes my code to implode with a:
No such property: channelName for class: java.lang.String.
Is this the expected behaviour of the params attribute of the paginate tag? If so, what is the cleanest way for me to achieve the behaviour I am looking for (i.e. searchParams being passed as a map and not a string to my controller)?
Edit:
Here is the relevant gsp code for my search form:
<g:form url="[action:'index']" method="PUT" >
<fieldset class="form">
<div class="fieldcontain">
<label for="channelName">
<g:message code="alarm.channelName.label" default="Channel Name" />
</label>
<g:textField name="searchParams.channelName" value="${searchParams?.channelName}"/>
</div>
</fieldset>
<fieldset class="buttons">
<g:actionSubmit class="list" action="index" value="${message(code: 'default.button.list.label', default: 'List', args: [entityName])}" />
</fieldset>
</g:form>
and here is my index action:
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
def alarmCriteria = new DetachedCriteria(Alarm)
def channelInterfaceInstances = ChannelInterface.list();
if(params.searchParams?.channelName != null && params.searchParams?.channelName != "" && params.searchParams?.channelName != []) {
alarmCriteria = alarmCriteria.and {
like("channelName", "%${params.searchParams?.channelName}%")
}
}
def results = alarmCriteria.list(params) {}
respond results, model:[alarmInstanceCount: results.totalCount, searchParams: params.searchParams]
}
edit-
I am trying to return a list of portfolios, along with the last 5 publications attached to each portfolio from my 2 domain classes. I am getting the last total 5 publications back and each list displays all 5. The query is not returning that particular instances own publications. Kellys great ideas put back into another track.
I have created the method in the portfolio controller which is the hasMany side to the publications which belongsTo the portfolio domain class.
I just cant seem to get the portfolios to list their own publications. If I change the eq portfolios, it all works fine, except each portfolio list shows the same publications.
How do I load each portfolio and list the last 5 publications. This is rendered from the portfolio/list page as a partial.Is this the issue maybe. Should I just render it from a new view which is not associated with the portfolio list action??
Newbie to grails and have read and read the doc's, just cant seem to get the params query to return correctly. Help
def _webList (){
//def per = Portfolio.properties
def portfolios = Portfolio.list(params.id)
def results = Publication.withCriteria {
eq('published', 'Yes')
order('lastUpdated', 'desc')
maxResults(5)
}
def reportscount = Publication.count()
[ portfolios: portfolios, results: results, reportscount: reportscount]
}
I can show the the sql log if needed.
EDIT
The following code is the entire partial from file _webList.gsp. The top div -alert loads on the page, but the content within the div property-list portfolio fails to load. Using Kelly's hibernate criteria produces the query in the sql log but not results or styles or anything are return to the view??. weird.!
<div class="alert alert-info" xmlns="http://www.w3.org/1999/html">Permissions apply to <strong>editing</strong> publications.<br>
<div style="display: inline;"><p>Click portfolio name to read or edit publications. Total number of sites: <strong>${rsNumb}</strong> | Total number of publications: <strong>${reportscount}</strong> </p>
</div>
</div>
<div class="property-list portfolio">
<g:each in="${portfolios}" var="portfolioInstance">
<div class="site-listing">
<div><span class="label">Site Name:</span><g:link action="show" id="${portfolioInstance?.id }">${portfolioInstance?.portfolioName?.encodeAsHTML()}</g:link></div>
<div><span class="label">Site Description: </span>${portfolioInstance?.portdescrip?.encodeAsHTML() }</div> <br>
<div><span class="label">Site Administrator: </span>${portfolioInstance?.profile?.portfolioAdmin?.encodeAsHTML() }</div> <br>
<div><span class="label"> Total publications:</span><span class="badge badge-success"> ${portfolioInstance?.publications?.size()}</span> </div>
<!-- whatever else you need here -->
<!-- now iterate through the pubs -->
<g:if test="${portfolioInstance?.publications}">
<g:set var="publicationInstance" />
<ul class="site-publication">
<li class="fieldcontain">
<span id="publications-label" class="property-label"><g:message code="portfolio.publications.label" default="Last 5 published publications:" /></span>
<g:each in="${portfolioInstance.publications}" var="publicationInstance">
${publicationInstance?.id}
<span class="property-value" aria-labelledby="publications-label"><g:link controller="publication" action="show" id="${publicationInstance.id}">${publicationInstance?.encodeAsHTML()}</g:link></span>
<!-- and again whatever else you need here -->
</g:each>
</g:if>
</g:each>
</div>
EDIT - sql log below
Hibernate: select this_.id as id5_1_, this_.version as version5_1_, this_.date_created as date3_5_1_, this_.last_updated as last4_5_1_,
this_.portdescrip as portdesc5_5_1_, this_.portfolio_name as portfolio6_5_1_, this_.portpublished as portpubl7_5_1_, this_.profile_id as profile8_5_1_,
this_.status as status5_1_,
publicatio1_.portfolio_id as portfolio5_5_3_,
publicatio1_.id as id3_, publicatio1_.id as id2_0_,
publicatio1_.version as version2_0_,
publicatio1_.date_created as date3_2_0_,
publicatio1_.last_updated as last4_2_0_,
publicatio1_.portfolio_id as portfolio5_2_0_,
publicatio1_.publication_content as publicat6_2_0_,
publicatio1_.publication_name as publicat7_2_0_,
publicatio1_.published as published2_0_,
publicatio1_.publisheddate as publishe9_2_0_,
publicatio1_.publishedemail as publish10_2_0_,
publicatio1_.pubproduct_id as pubproduct11_2_0_
from portfolio this_ left outer join publication publicatio1_
on this_.id=publicatio1_.portfolio_id where (this_.status=?)
and (publicatio1_.published=?) order by publicatio1_.last_updated desc
You are getting the java.lang.ClassCastException because portfolios is a list and portfolio in the Publication class (probably) isn't, it's probably an id (long); can't cast a list in any meaningful way to compare to a long in eq ('portfolio', portfolios)
You should not need two separate queries since the domain classes are related.
--EDIT--
Editing to not use separate action and just use list action. I've not been able to get the include to work either, but below is pretty much what I've in dozens of cases. If there is some reason you can't do this then maybe a new question on just using the include mechanism might generate some attention.
I'm not sure what your current list action looks like. This is how I would code a list method to get ALL Portfolios and their last 5 Publications. No need for any parameters because I'm returning ALL portfolios.
//PortfolioController
def list (){
def portfolios = Portfolio.createCriteria().list {
//if you needed to filter the list by for example portfolio status or something you could add that here
or {
eq('status','ACTIVE')
eq('status','PENDING')
}
publications(org.hibernate.criterion.CriteriaSpecification.LEFT_JOIN) {
eq("published", "Yes")
order("lastUpdated", "desc")
firstResult(5)
}
}
[portfolios: portfolios, portfolioCount:portfolios.size()]
}
Now the Publications come pre-attached to their Portfolio.
The LEFT_JOIN part of the above insures you get back a list of portfolios with ONLY those Publications attached that meet the criteria; if you leave that out it defaults to an inner join and when you iterate you will get ALL of the Publications for that Portfolio (even if they don't meet the criteria).
Then in your gsp iterate through the portfolios - it can either be directly in the list.gsp or in a template rendered in list.gsp. If you put it in a template called _webList.gsp you would render it in the list.gsp as
<g:render template="weblist" model="['portfolios': portfolios]" />
This is either in list.gsp or _webList.gsp - I would start with it directly in list.gsp to make sure it's all working.
<g:each in="${portfolios}" var="portfolioInstance" status="i">
${portfolioInstance?.portfolioName?.encodeAsHTML()
<!-- whatever else you need here -->
<!-- now iterate through the pubs -->
<g:each in="${portfolioInstance.publications"} var="publicationInstance" status="j">
${publicationInstance.id}
<!-- and again whatever else you need here -->
</g:each>
</g:each>
--EDIT--
firstResult(5) seems to do the trick.
--EDIT--
You'll notice I have the maxResults(5) commented in there - I'm having trouble getting that to work correctly. It seems to control the number of portfolios that are returned even though it is in the association block. Maybe someone else will see this and add that piece of the puzzle - or tinker with it yourself. I'll keep trying and update if I figure it out.
How do I structure my pages and partial templates so that Ajax will play nice with <paginate> and column sorting?
I currently have a search.gsp page with a remoteField that calls a controller to update a template. This all works fine. However, the column sorting and paging actions cause my search.gsp to be completely replaced by the template view.
From my search.gsp:
<div id="searchBox">
Enter a key or phrase: <g:remoteField name="searchBox"
update="resourceSearchResultPanel" paramName="q"
url="[controller:'resourceEntry',action:'searchForResources']"
/>
</div>
<div id="resourceSearchResultPanel" />
My controller handles the search request like so:
def searchForResources = {
params.max = Math.min(params.max ? params.max.toInteger() : 10, 100)
params.offset = params.offset ? params.offset.toInteger() : 0
log.debug "Handling search post action"
def q = params.q ?: null
log.debug "Search phrase is $q"
def searchResults
if (q) {
searchResults = [
results: ResourceEntry.search(q,[offset: params.offset, max: params.max]),
resultCount: ResourceEntry.countHits(q),
q: q.encodeAsHTML()
]
}
render(template:"resourceSearchResultPanel",model:searchResults)
}
The _resourceSearchResultPanel.gsp is just a table with this <paginate> tag:
<g:paginate action="searchForResources" total="${resultCount}" params='["q":"${q}"]' />
The problem is that when the <paginate> tag calls the controller, the entire page is refreshed with the contents of the _resourceSearchResultPanel.gsp template, while I just want the _resourceSearchResultPanel.gsp itself to be refreshed inside search.gsp.
There's no update attribute like there is in the remoteField tag...
The paginate tag doesn't support generating ajax links so you'll have to write your own verison of the tag that calls remoteLink instead of link.
cheers
Lee
I would suggest to you remote pagination plugins for Grails. This would suffice your requirement. For more details please refer to following site:-
http://www.grails.org/plugin/remote-pagination
Please feel free to revert incase of any concern.
I have a Groovy application. I am rendering the view list using the following statement:
render (view: 'list', model:[reportingInstanceList: reportingInstanceList, reportingInstanceTotal: i, params: params])
The list.gsp is as follows:
The view is rendered but the default sorting is not working.
<g:sortableColumn class="tabtitle" property="id" title="Id" titleKey="reporting.id" />
<g:sortableColumn class="tabtitle" property="company" title="Company" titleKey="reporting.company" />
Unfortunately the default sorting (by id, by company, etc) are not working.
Any hint why?
Thanks a lot in advance.
Luis
If you are asking about the sorting/order links at the top of the columns on the list page, the links are hrefs back to the controller and method that was originally used to populate the list. Plus the URLs include parameters for sort and order. For instance:
/tracker/bug/searchCurrentUserProject?sort=name&order=asc
The controller method will then need to handle the sort and order values from the link:
params.sort = params.sort ?: "priority"
params.order = params.order ?: "asc"
And pass them to the database query:
def bugList = Bug.createCriteria().list(
sort:params.sort,
order:params.order,
max:params.max,
offset:params.offset) {
eq "projectId", new Integer (params.projectId)
}
You can add two hidden fields in your form, set them dynamically with Javascript and send them together with your form.