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.
Related
I am trying to send an ID of an iterated item but I think the whole list is being sent. How can I send just one ID?
I have STUDENT and COURSE domain class.
Domain Model
class Student {
String fullName
String toString() {
"$fullName"
}
static belongsTo = [school: School]
static hasMany = [courses:Course, studentCourses:StudentCourse]
}
class Course {
String course
String toString() {
"$course"
}
static hasMany = [studentCourses:StudentCourse]
static belongsTo = Student
}
class StudentCourse {
Student student
Course course
//Some methods...
}
And this is my edit view.
<g:if test="${studentInstance.studentCourses?.course}">
<g:each class="courseList" in="${studentInstance.studentCourses?.course}" var="courses">
<li class="courseList">
<span class="courseList" aria-labelledby="courses-label">${courses.id} ${courses}
<g:actionSubmitImage class="deleteIcon" action="deleteCourse" value="delete"
src="${resource(dir: 'images', file: 'delete.png')}" height="17"
onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');"
params="${courses.id}"/></span>
</li>
</g:each>
</g:if>
I'd like to be able to delete one course from the list when user clicks delete.png image. But when I println params.course the parameter is being sent as a whole list, not as an individual item of the list even though it is within g:each tag. How can I just send one corresponding item to the controller?
My edit page has a list of courses.
Course 23 English (delete icon here)
42 Science (delete icon here)
67 Math (delete icon here)
In my println params.course this is what I see.
[ English, Science, Math ]
How can I just have [English] when user clicks the delete button next to English line?
Thank you in advance.
As far as I have understand your problem, The problem should be in your taglib
I am giving you the example of similar code and this is working.
<g:each in="${student?.studentCourses?.course}" var="course">
<div>
<span>${course.id} ${course.course}</span>
<g:link controller="login" action="delete" params="[courseId:course.id]">
Delete
</g:link>
</div>
In this block of code, by clicking delete link with params having params="${[id:course.id]}, it will redirect to delete action inside login controller. Print params in delete action and you will get output as
params: [courseId:2, action:delete, format:null, controller:login]
Here you can see your params.courseId is only a Long value not a list.
Hope it will help you understand your problem.
I'm using Grails 2.2.3 and Fields plugin 1.3.
I want to customize fields to manage one-to-many relationships using select tag.
In views/_fields/oneToMany/_input.gsp I have:
<g:select name="${property}.id" from="${type.list()}" optionKey="id" value="${value}" class="form-control one-to-many" noSelection="['null': "${label}"]" />
But type is a set, so I can't use list function.
How can I retrieve target domain class?
As long as you use a Map to declare the relationship, for example:
static hasMany = [ books: Book ]
You can get the list of the referenced domain, which is the key from the hasMany property of the bean, so the from attribute should change to
from="${bean.hasMany[property].list()}"
Alternatively you can pass the list to the _input.gsp template prefixing the variable name with input-, e.g.
<f:field property="books" input-domainList="${bookInstaceList}" />
In the _input.gsp template you can use the variable as follows:
from="${domainList}"
or mixing both methods:
from"${domainList ?: bean.hasMany[property].list()}"
I found this solution for the problem, this code work fine with one to many and one to one
<div class="input-group">
<g:select name="${persistentProperty.toString().contains('one-to-many')?property:property+'.id'}" value="${value?.id}" required="${required}"
noSelection="${['null':'Escriba...']}" class="select2 col-md-6"
multiple="${persistentProperty.toString().contains('one-to-many')?true:false}" from="${grailsApplication.getDomainClass(persistentProperty.properties['associatedEntity'].toString()).clazz.list()}"
optionKey="id"></g:select>
</div>
This solution can be helpful. I found in the documentacion of Fields Plugin, the persistentProperty is a class type DomainProperty but their methods doesn't works and I found the correct class is org.grails.datastore.mapping.model.MappingFactory
you can use .toArray() method on your Set instead of .list().
ale
You can use the code below to identify the Element of your collection
def className = bean.hasMany[property].properties.typeName
See my "views/_fields/oneToMany/_input.gsp" implementation:
<%# page defaultCodec="html" %>
<%
def className = bean.hasMany[property].properties.typeName
def simpleName = Class.forName(className).simpleName
def beanSimpleName = bean.class.simpleName
def createUri = "/${simpleName.toLowerCase()}/create?${beanSimpleName.toLowerCase()}.id=${bean.id}"
%>
<a id="add${simpleName}" name="add${simpleName}" href="${createLink(uri: "${createUri}")}">
Add ${simpleName}
</a>
<ul>
<g:each var="item" in="${value}">
<li>
${item}
</li>
</g:each>
</ul>
I've scoured the web for almost a day and can't seem to find a solution to my problem.
I have a lookup table called Hobby which has a bunch of hobbies in it (camping, hiking, biking, etc). The hobbies table is populated during the bootstrap. Grails creates a hobby table with an id, and description field.
I have a domain object called Applicant. An applicant can have zero or more hobbies. I've declared the domain like this:
class Applicant {
static hasMany = [hobbies:Hobby]
List <Hobby> hobbies = LazyList.decorate(new ArrayList(), FactoryUtils.instantiateFactory(Hobby.class));
}
In my controller I'm using a command object for the page that will allow the applicant to select their hobbies. It is defined as:
class LifestyleCommand {
List <Hobby> hobbies = LazyList.decorate(new ArrayList(), FactoryUtils.instantiateFactory(Hobby.class));
}
My gsp looks like this:
<g:each var="item" in="${Hobby.list()}" status="i">
<g:set var="newline" value="${(i % 3) == 0 ? 'newline' : ''}" />
<div class="formcheckbox columns3 ${newline}">
<g:checkBox name="hobbies_${item.id}" optionKey="id" value="${item.id}" />
<label>${item.description}</label>
</div>
</g:each>
The page will display all of the hobbies in the page correctly. However when I try to submit the form back to the controller the list in the LifeStyleCommand object is null. I'm not sure if my gsp has the g:checkBox variables set correctly, and I'm not sure if I declared the List in the command object correctly either. Once I get the data to be sent back to the controller, my next problem to overcome will be copying the data from the command object to the Applicant. Any help would be appreciated.
I've tried using just a plain List in the command but grails complains about type conversions when the form is submitted.
EDIT:
Here is what I got to work:
class Applicant {
static hasMany = [hobbies:Hobby]
//I removed the List <hobby>... code
...
}
My command object:
class LifestyleCommand {
Set <Hobby> hobbies;
}
gsp:
<g:each in="${Hobby.list()}" var="hobby">
<g:set var="checked" value="${ command?.hobbies.find{h->h.id == hobby.id } != null }" />
<g:checkBox value="${checked}" name="${ 'hobby' +'[' + hobby.id + ']'}"/>${hobby.description}</td>
</g:each>
And my new controller code:
Hobby.list().each{hobby->
if (params["hobby[${hobby.id}]"] == 'on') {
applicant.addToHobbies(hobby)
}
else if (applicant.getHobbies().contains(hobby)) {
applicant.removeFromHobbies(hobby)
}
}
Everything is working. There may be better ways, and I haven't done any refactoring yet but having it work is a starting point.
The problem is with your GSP. When you need a list of objects, you have to maintain a standard name for your checkbox:
<g:checkBox name="hobbies[$i].id" ... />
This will be mapped correctly to your command list.
Im developing a webapp that is feeded via a server. Its index presents some shops deals, having all of these deals ann id. In the .gsp, i have this code:
<div id="content">
<g:each in='${promos}' var='promo'>
<div id="up_box"></div>
<div class="deal_container">
<div class="images_container"><img src="${createLink(action:'showImage',controller:'image',id:promo.fotoPrincipal.id)}" width="120" height="105" />
</div>
<g:link controller='promotion' action='openPromo' params="[promoid:promo.id]">
<div class="oferta">${promo.name}</div>
</g:link>
<g:link controller='promotion' action='openPromo' params="[promoid:promo.id]">
<div class="flecha">
</div>
</g:link>
</div>
That means, when I click in one of the links, it will open an url like this:
app/promotion/openPromo/3
In case the deal with the id "3" is clicked. I have a "general" deal details page. I want that page to load always, and fulfill its details dinamically, depending the deal the user clicked. How can I do that?
I don't know if I have been clear in my explanation, if i haven't please ask.
Thank you!
Both links will be handled by the openPromo action in the PromotionController and will be passed the id of the promo as promoid.
You can then load the appropriate promo along with any other related offers you want the user to see and give these to the view. Eg:
class PromotionController {
def openPromo() {
Promo promo = Promo.get(params.promoid)
// Load any other relevant data or offers
render (view:'openPromo', model:[promo:promo, <any other model data here>]
}
}
You can then use the model data in your gsp.
You can do it adding that general info to a Template and then render it from the Controller like this
// render a template to the response for the specified model
def theShining = new Book(title: 'The Shining', author: 'Stephen King')
render(template: "book", model: [book: theShining])
You´ve got more info about the render in this link from the Grails documentation.
http://grails.org/doc/latest/ref/Controllers/render.html
It´s easy to do it, you can add a createLink like the first ones whith calls to an action who will render that template.
Hope it helps ;) and good luck!
I did it this way, after asking my workmate:
def openPromo() {
Promotion promo = Promotion.get(params.promoid)
[promo:promo]
}
Almost what you said David, but without rendering, if I name openPromo my gsp.
Thank you!
I have the following domain classes:
class User = {
String username
...
Company company
}
class Company {
String name
...
}
That is, there is a n:1 relationship between user and company.
These classes are so and I cannot change them.
At the show.gsp I want to have the details of the company together with links to
the users who belongs to this company.
I know that I can achieve this writing an own tag, but I am sure that this would be possible using the each tag or the the findAll tag.
If I do the following
<g:each in="${User.findAll('from User order by username')}" var="userInstance">
<li><g:link controller="role" action="show"
id="${userInstance.id}">${userInstance.encodeAsHTML()}</g:link>
</li>
</g:each>
I've tried to pass the ${companyInstance} as a parameter but either I got an exception or it didn't work.
I've also tried using User.findAllByCompany.
When using:
<g:findAll in="${user}" expr="it.company == ${companyInstance} ">
I am getting an empty set.
Is there an easy way to achieve this without writing a taglib?
Thanks in advance.
Luis
The <g:findAll> tag iterates over an object list but it does not query the DB as done with User.findAll(..)
The correct code is:
<g:each in="${User.findAll('from User as u where u.company=:company order by username', [company: companyInstance])}" var="userInstance">
<li><g:link controller="role" action="show"
id="${userInstance.id}">${userInstance.encodeAsHTML()}</g:link>
</li>
</g:each>
If you absolutely want to use g:findAll, then you first need to build the list of users as following:
<% def users = User.listOrderByUsername()%>
<g:findAll in="${users}" expr="it.company == ${companyInstance} ">
I hope it helps,
Regards,
Fabien.
It's more simple that you think: ${userInstance.company.encodeAsHTML()}