I want to create a report for my domain class instances but not all, just the ones user selected in the GSP table (selecting checkboxes). On my report I will also have some logic - few conditionals, some calculations etc. I would also have to get some additional data from database. How do I do this? Should I get values of selected checkboxes in cotroller and pass it to jasper cotroller? Something like this:
GSP:
<g:each in="${books}" var="bookInstance">
<td> <g:checkBox name="book_${bookInstance.id}"/> </td>
</g:each>
Action:
def bookReport = {
def bookList = []
params.each {
if(it.key.startsWith("book_")){
bookList.add((it.key - "book_") as Long)
}
}
def bookCriteria = Book.createCriteria()
def books = bookCriteria.list {
'in'('id',bookList)
}
chain(controller:'jasper', data:books, action:'index', params:params)
}
I used iReport for report creation. I've tried creating report without SQL query and parameters. My logic was that if I pass a map of domain instances to jasper controller as I did in the example above I dont need to specify data source in report. But I get an empty report.
I also tried seting the report datasource to my database and report query to this: select * from book where $X{IN,id,books}. In that case, no matter what I select report is created for all book instances.
Have you tried...
chain(controller:'jasper', model:[data:books], action:'index', params:params)
...while leaving the SQL string empty in your .jrxml?
Related
When I try to show linked data in index action - I can't avoid N+1 problem using standart includes. Example:
Model 1 - Pet (let it will be animal: title:string. Pet can has many PetAttributeElems, they show attributes this Pet have and what values they have)
Model 2 - PetAttribute (this model contains only titles of attributes, like weight, age and 1000+ more attributes. PetAttribute has many PetAttributeElems - one attribute, such as weight, can be described for many Pets)
Model 3 - PetAttributeElem (this model belongs to pet and to petAttribute, also it has value field, that show value of attribute for pet.
When I make show action - I use in HAML:
-#pet.pet_attribute_elems.includes(:pet_attribute).each do |elem|
="#{elem.pet_attribtute.title}: #{elem.value}"
But when I make index action I want to use:
-#pets.each do |pet|
-pet.pet_attribute_elems.includes(:pet_attribute).each do |elem|
="#{elem.pet_attribtute.title}: #{elem.value}"
That includes method will call many SQL queries, for every pet
Now I solve it by manually creating additional object like this:
#pet_elems = {}
PetAtributeElems.includes(:pet_attribute)
.where(pet_id:#pets.map(&:id)).each do |elem|
pet_id = elem.pet_id
if #pet_elems.include?(pet_id)
#pet_elems[pet_id] << elem
else
#pet_elems[pet_id] = [elem]
end
end
Than I can use:
-#pets.each do |pet|
-if #pet_elems.include?(pet.id)
-#pet_elems[pet.id].each do |elem|
="#{elem.pet_attribtute.title}: #{elem.value}"
How could I solve that task more easy?
You're going down a non rails-convention path.
Move code out of the views so it's simply
= render #pet_attribute_elems
Make a partial to handle display
# _pet_attribute_elems.html.haml
="#{pet_attribute_elem.pet_attribtute.title}: #{pet_attribute_elem.value}"
In the controller, do the queries
def show
#pet = Pet.find(...)
#pet_attribute_elems = #pet.pet_attribute_elems.includes(:pet_attribute)
end
def index
#pet_attribute_elems = PetAttributeElem.includes(:pet_attribute)
end
I want to display all games under a certain category in pagination (10/page)
This is my controller
def listGame(){
def category = GameCategory.list()
def platform = Platform.list()
def currentCategory = params.categoryName
def myCategory=GameCategory.findByCategoryName(currentCategory)
def games = myCategory.games
[currentCategory:currentCategory, category:category, games:games, platforms:platform, gameCount:Game.count(), chosenPlatform:params.platform]
}
and this is my pagination code in my view
<g:each in="${games}" status="i" var="game">
${game.gameTitle}
</g:each>
<div class="pagination">
<g:paginate action="listGame" total="${gameCount}" />
</div>
When I have 9 games in the current category the page 12 showed up wherein it shouldn't
When I have 11 games in the current category all 11 games are displayed in page 1 and clicking page 2 will result in this error
There are a couple of things to address.
Make sure categoryName is not coming into the controller as null.
Make use of the max and offset parameters that the paginator is providing to select the appropriate Games.
The category name
Because the paginator is creating the URL that calls into the controller, and your controller needs the category name, the paginator must add the category name to the params it calls the controller action with. You can achieve this with the tag's params attribute:
<g:paginate action="listGame" total="${gameCount}" params="${[categoryName: currentCategory]}"/>
Max & offset
The controller needs to use max and offset parameters to know what subset of the Games to retrieve from the database and render in the view. However, in some cases, such as when visiting the URL http://blah/someController/listGame, there would be no max and offset params. This is something your controller action must be able to handle. It goes something like this:
def max = params.max ?: 10
def offset = params.offset ?: 0
So the max and offset params will be used if they are available. Otherwise they'll use default values.
Finally, you need to use these parameters to select the Games. For that you can use a where query:
def games = Game.where {
categories.categoryName == currentCategory
}.list(max: max, offset: offset)
Something interesting about the returned games object: It acts like a list, but it's actually a grails.gorm.PagedResultList. That's a good thing because it contains the total number of records in the result (ignoring max and offset). In other words, it's your new gameCount.
You can read more about where queries in the Grails documentation. I also have a series of articles which cover where, criteria and HQL queries in more detail.
Putting it all together, listGame() would look like this:
def listGame(){
def category = GameCategory.list()
def platform = Platform.list()
def currentCategory = params.categoryName
def max = params.max ?: 10
def offset = params.offset ?: 0
def games = Game.where {
categories.categoryName == currentCategory
}.list(max: max, offset: offset)
[currentCategory:currentCategory, category:category, games:games, platforms:platform, gameCount:games.totalCount, chosenPlatform:params.platform]
}
Looks like you haven't done your homework. Please go through grails doc and other tutorials, to understand how pagination works.
Just to give you a hint. You are doing two things wrong here.
You are not getting a paginated list at the first place from controller
You are not sending back all the required data back to the controller, from the pagination link. And that's the reason you are getting the error!
From the Grails controller function if i want to retrieve a value from another object, i retrieve it as follows:
def person = Person.get(10)
println person.name
The above code will return a person object where the ID is 10, and also it will return the name of that particular user.
Likewise, how can i do such a computation in the view.
View
<body>
<table>
<g:each in="${personInstanceList}" status="i" var="personInstance">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<td><g:link action="classesoffered"
url="${fieldValue(bean: personInstance, field: "id")}"
id="${personInstance.id}" >
${personInstance.id}
</g:link></td>
.....
... </body>
The above code will display the ID of the person Object. Is it possible for me to use this ID to retrieve a value of another object. For example.
def school = School.get(${personInstance.id})
Can i use the ID (${personInstance.id}) in order to retrieve the school from the View ?
Note: Hope i have explained the question properly. In a nutshell I want to do a computation at the view. To retrieve schoolinstance from ${personInstance.id} from the view.
UPDATE
Person MODEL
String name
int school
School MODEL
String nameOfSchool
You can import a domain in your view with: (first line of the gsp)
<%# page import="com.yourPackage.School" %>
And then, you can use the tag set to create a new variable inside you view.
For example:
<g:set var="school" value="${ School.get(personInstance.id) }" />
If you want to print the value in your GSP (for example the name of the school), you can use:
${ school.nameOfSchool }
(if school is not null of course)
Hope that helps
Rather than trying to do this kind of thing within the view, you should redesign your domain model to fit the task. If you want each Person to be linked to their School then you should do it with a proper association rather than storing an ID (for which, incidentally, you're using the wrong type - by default the ID of a Grails domain class is a Long, not an int):
class Person {
String name
School school
}
class School {
String name
}
and create instances like this:
// create a new school
def school = new School(name:'Example school')
// or fetch an existing one from the DB
// def school = School.get(1)
def person = new Person(name:'Illep', school:school)
With this model, the GSP can access the school name simply as
${personInstance.school?.name}
In my controller I have an instant variable with a where method, its part of a filter in the Search Controller.
#items = #items.where([ 'borrowable = ?', true]) if params[:top_filter] == 'borrow'
The borrowable = ?, true bit is completely wrong. What I want is to filter the #items that are borrowable by using a method in the Item's model called borrowable?.
How do I do this?
The method in the Item model is below.
def borrowable?(current_user, user, item, controller)
false
if user.invited?(current_user, user)
item.pieces.each do |piece|
if piece.available?(current_user, piece)
true
end
end
if controller == "pages"
true
end
end
end
If it's relying on model level code, then you won't be able to put it in a where clause. The where clause is trying to grab a list of items, but on each item you want to call a method, but you don't have the method until your grab the items. You could potentially use a stored procedure on the DB, but just in Rails code you could do this:
#items = #items.keep_if { |item| item.borrowable? } if params[:top_filter] == 'borrow'
You can't! ActiveRecord makes queries on the database, then builds object with the returned data. So until the data is returned, no ruby object exists, and you can't use their method.
If you are able to represent the same information using a "calculation" (in the SQL sense) then you can use it by
#items.where("some sql condition/calculation")...
I have a domain class called Application as follows:
class Application {
static hasOne = [resumption:Resumption, employee:Employee]
//Employee employee
Date startDate
Date endDate
Integer amountOfDays
String leaveRecommended
String leaveNotRecommended
Date supervisorDate
String toString(){
return "Application for ${employee.lastName}, ${employee.firstName}"
}
}
In the ApplicationController I'm trying to write a query that is going to find all applications that match a particular employee id. I do so as follows:
def applicationlist(){
if(!params.max){
params.max = 10
}
def query
def criteria = Application.createCriteria()
def results
query = { eq("employee_id", Long.parseLong("1")) }
results = criteria.list(params, query)
render(view:"employeeapplicationlist", model:[applicationlist:results])
}
Now I keep getting the error: "could not resolve property: employee_id"
I've checked the generated Application table in MySql, there is a column called employee_id with a value. The weird thing is I can access any other property (like amountOfDays), so what's the deal with employee_id? Why is it complaining that it cannot resolve the property? What am I missing? Thanks in advance.
Associations in the criteria DSL are of the form
Application.withCriteria{
employee{
eq 'id', 1
}
}
http://grails.org/doc/latest/guide/GORM.html#criteria
But you could probably just do:
def employee = Employee.proxy(1)
Application.findAllByEmployee( employee )
This appears a few times in the Grails User Guide as 'querying associations'
Oh well it looks like I'm still not fully adjusted to interfacing with the database on an Object level. For anyone else with this or a similar problem, here's the fix:
query = { eq("employee.id", Long.parseLong("1")) }
Sine the Application Domain class has one Employee, then we just need to access the id field of that employee. Remember we're in the ApplicationController.