Hey all, I'm building a GRAILS application with a m:m db relation. As I try to show the entries the well known "failed to lazily initialize a collection of role ... no session or session was closed" error is shown.
One class is:
class Hazzard{
static hasMany = [warning:Warning]
static constraints = {
text(size:1..5000)
}
String name
String text
String toxicity
}
the other:
class Warning{
static hasMany = [hazzard:Hazzard]
static belongsTo = Hazzard
static constraints = {
text(size:1..5000)
}
String code
String text
}
In Hazzard/show the following code works fine
<g:each in="${hazzardInstance.warning}" var="p">
<li><g:link controller="Warning" action="show" id="${p.id}">${p?.encodeAsHTML()}</g:link></li>
</g:each>
but on other pages the following code will provide the error:
<g:set var="haz" value="${Hazzard.get(params.id)}" />
<h1>${haz.name}</h1>
<p>${haz.text}</p>
<h1>Toxiciteit</h1>
<p>${haz.toxicity}</p>
<br/>
<h1>Gevaren(H) en voorzorgen(P)</h1>
<g:each in="${haz.warning}" var="p"> --> This is where the error pops-up
${p.text}
</g:each>
Any clues on where this fails?
A more favorable approach to what you're trying to do would be to perform the get in the controller and pass the found domain object to the view for rendering. Something like:
// MyController.groovy
class MyController {
def myAction = {
def haz = Hazzard.get(params.id)
render(view: 'myview', model: [hazzardInstance: haz])
}
}
// my/myview.gsp (the view from your second GSP code block)
<h1>${hazzardInstance?.name.encodeAsHTML()}</h1>
...
<h1>Gevaren(H) en voorzorgen(P)</h1>
<g:each in="${hazzardInstance?.warning}" var="p">...</g:each>
Doing GORM lookups in the view can sometimes lead to the exception you're getting, although I thought many problems like that had been fixed in more recent versions of Grails. Nonetheless, using a more correct idiom for querying and rendering views will help you to avoid this error.
Related
In my domain model, some classes have a collection of enums as a field. I have modeled it in two differente ways, as an embedded collection:
class A {
String name
Set<Enumeration> enumerations
static embedded = ['enumerations']
}
enum Enumeration {
ENUM_VALUE_1,
ENUM_VALUE_2,
ENUM_VALUE_3
}
And also as a hasMany collection:
class A {
String name
static hasMany = [enumerations:Enumeration]
}
enum Enumeration {
ENUM_VALUE_1,
ENUM_VALUE_2,
ENUM_VALUE_3
}
In both cases, I can add enum values to the collection in BootStrap.groovy in the following way:
A.withTransaction { status ->
def a1 = new A( name:"a1" )
a1.addToEnumerations( Enumeration.ENUM_VALUE_1 )
a1.addToEnumerations( Enumeration.ENUM_VALUE_2 )
}
Using scaffolding, I can see the content of the enumeration collection in the index and show pages, but in the edit and create pages, only the label is shown, no widget is displayed.
Which is the simplest way to show a widget, e.g. a multiple select, for this kind of fields in Grails 4 (I am using Grails 4.0.3)?
Thanks in advance.
Which is the simplest way to show a widget, e.g. a multiple select,
for this kind of fields in Grails 4 (I am using Grails 4.0.3)?
You can use the select tag.
<g:select multiple="true" name="someName" from="${somePackage.SomeEnumClassName}"/>
This is the process I followed after #jeff-scott-brown answer:
I generated the views for class A:
generate-views A
Then in grails-app/views/a/edit.gsp and grails-app/views/a/create.gsp I changed this element:
<f:all bean="a"/>
into these other elements:
<f:with bean="a">
<f:field property="name"/>
<f:field property="enumerations">
<g:select
multiple="true"
name="${property}"
from="${Enumeration}"
value="${a?.enumerations}"
/>
</f:field>
</f:with>
And it works for both the embedded and the hasMany approaches.
Should not this be the default scaffolding for collections of enumerations in Grails?
Well if this is for validating data for your database, you can use the 'inList' constraint on your domain as the same constraint by extracting that as a 'List' for you to use:
def class = grailsApplication.getDomainClass(className)
def props = class.getConstrainedProperties()
props.each(){ k,v ->
if(k=='theColumnName-you-are-trying-to-get'){
return v.getInList()
}
}
This is a simpler solution without having to generate the views, i.e. no need to modify edit.gsp and create.gsp. Just using the fields plugin as suggested by #jeff-scott-brown.
Assuming this is your domain class:
package mypackage
class MyClass {
// ...
// properties
// ...
Set<MyEnumeration> enumerations
// no need to declare it as embedded
// static embedded = ['enumerations']
}
enum MyEnumeration {
ENUM_VALUE_1,
ENUM_VALUE_2,
ENUM_VALUE_3
}
And that you have generated a scaffold controller for it, i.e.
package mypackage
class MyClassController {
static scaffold = MyClass
}
The only thing you have to do is creating the file grails-app/views/_fields/myClass/enumerations/_widget.gsp with the following content:
<g:select
multiple="true"
name="${property}"
from="${mypackage.MyEnumeration}"
value="${myClass?.enumerations}"
/>
It also works if you declare the collection of enumerations as a hasMany association, i.e.
package mypackage
class MyClass {
// ...
// properties
// ...
static hasMany = [enumerations:MyEnumeration]
}
enum MyEnumeration {
ENUM_VALUE_1,
ENUM_VALUE_2,
ENUM_VALUE_3
}
I've two domain classes
1.CustomerInterest.groovy
static hasMany = [activities:Activity]
static belongsTo=[customer:Customer,projectProperty:ProjectProperty]
static mapping={
activities sort:'dateCreated',order:'desc'
}
2.Activity.groovy
Date dateCreated
static belongsTo = [customerInterest:CustomerInterest, employee:Employee]
In a controller i am doing this..
def customerDetails(Customer customer)
{
def customerInterest=customer.customerInterests
render view:"customerDetails",model:[customerInterest:customerInterest]
}
customerDetails.gsp
<g:each in="${customerInterest}" var="ci">
${ci}
</g:each>
**
Question: I want to sort CustomerInterest on property dateCreated of Activity domain.
**
Any help as soon as possible would be appreciated.
Try this
def customerInterest=customer.customerInterests.sort { it.activities.dateCreated }
Instead of using the sort order in the mappings definition, you can use the old java Comparable-interface:
class Foo implements Comparable {
int compareTo(anotherObject) {
// complex logic returning -1 or 0 or 1
}
}
Then you can call listOfFoos.sort() and it will sort it with the help of the compareTo-method.
Beware that (equals) == will also use this method, so ensure that (only) identical objects will return 0.
I am trying to implement a "bulk" insert for hasMany. And got it 90% there, but hit a hiccup with my polyclass design. I have a model like
class Parent {
static hasMany = [references: Reference]
}
class Reference {
static belongsTo = [parent: Parent]
String name
}
class ConcreteReference extends Reference{
String childName
}
I was able to get the create working(defined by creating records) by having markup like
<form action='reference/1/edit>
<input name='references[0].name' value='name1' />
<input name='references[0].childName' value='childName1' />
<input name='references[1].name' value='name2' />
<input name='references[1].childName' value='childName2' />
</form>
But the issue is it always creates the concrete type(class='domain.Reference'), so "child name is always null.
I used:
def edit(def id){
def parent = Parent.get(id)
//i actually clear all the old references first (didn't show that code)
parent.properties = params
parent.save(flush:true)
}
How can I either specify the "concrete type"( I tried a hidden value of class it didn't work) or is there an easy way to loop through params.reference[idx] create it and then add it to Parent?
hopefully that is clear, let me know if you need any clarification.
My manual way of parsing params(not I hard code ConcreteReferences, but i was able to group the params):
def refRegex = /^([a-zA-Z]+\[\d+\])/
for (ref in params.groupBy { it -> it.key.find(refRegex)}.findAll{it.key}){
//this will be like references[0]: [references[0].name : 'test', etc]
//for each one create a FreeFormReference
def _ref = new ConcreteReference(ref.value.collectEntries{it -> [it.key.replace(ref.key + '.', ''), it.value]})
log.info('created reference ${_ref}')
parent.addToReferences(_ref)
}
I have domain classes as:
package mnm.schedule
class Project {
static hasMany = [ tasks : Tasks , users : User ]
String name
static constraints = {
name(nullable:false)
tasks(nullable:true)
}
}
User.groovy
package mnm.schedule
import org.example.*;
class User extends SecUser {
//relationships. . . .
static belongsTo = [ company : Company, role : Role, resource : Resource]
static hasMany = [ holidays : Holiday, tasks : Tasks, pt:String ]
Profile profile
Project project
String username
String password
boolean enabled
List pt
boolean ptTaken
}
I have a view file, inside which i have this code snippet :
<g:each in="${ans}">
<li>${it.username.toUpperCase()}<g:checkBox name="checkedUsers" value="${ans}" checked="false" /></li>
</g:each>
The variable ans is the arraylist, that has user objects. I use g:checkBox, so that end user can "check" the required users name.When the user submits this form, I do this action in my controller:
def users = params.checkedUsers
users.each { index ->
new Project(name:"testing",users:index).save()
}
The idea is that I need to add the choose user(via checkbox) to the project.
But this throws the error as :
2012-02-03 10:13:08,173 ["http-bio-8080"-exec-4] ERROR errors.GrailsExceptionResolver - TypeMismatchException occurred when processing request: [POST] /scheduleNew/project/project - parameters:
_checkedUsers:
_checkedUsers:
Add: Add
checkedUsers: anto2
Provided id of the wrong type for class mnm.schedule.User. Expected: class java.lang.Long, got class java.lang.String. Stacktrace follows:
Message: Provided id of the wrong type for class mnm.schedule.User. Expected: class java.lang.Long, got class java.lang.String
Whats going on? Where I went wrong?
Seems a few things a bit odd to me (but maybe I am not fully understanding what you are trying to achieve).
View:
<g:each in="${ans}">
<li>${it.username.toUpperCase()}<g:checkBox name="checkedUsers"
value="${it.id}" checked="false" /></li>
</g:each>
I would pass the id rather than the entire list (you had value="${ans})
In the controller, the passed params are of type String, that's why you are getting the type mismatch. There are several ways to do this, one solution is below:
Controller:
def actionCalled = {
def project = new Project(name:"testing")
def users = params.checkedUsers
users.each { index ->
def user = User.findById(index.toLong())
project.addToUsers(user)
}
project.save()
}
I'm sure you can simplify this further.
The class GrailsDomainClass has two methods: getProperties and getPersistentProperties
I have a domain class (MyDomainClass) which includes this:
static transients = {computeStuff}
float computeStuff(){
def thisCouldChange = 3.1
return thisCouldChange //it actually does things but not of consequence to my question
}
Ok, I took the default index page and modified it to list all the properties of MyDomainClass as such:
<g:each var="d" in="${grailsApplication.domainClasses.sort { it.fullName } }">
<h2>${d.fullName}</h2>
<g:each var="f" in="${d.properties.sort { it.fieldName } }">
<br>${f.fieldName }
</g:each>
</g:each>
Ok. That works, but it doesn't get any of the transient properties. I have tried d.properties and d.persistantProperties and they seem to give me back the same results. Thanks in advance for your help!!
Do I need to call it getComputeStuff or something?
I have now changed my domain class to contain this and still do not get back the transient computeStuff
static transients = ['computeStuff']
float getComputeStuff(){
def thisCouldChange = 3.1
return thisCouldChange //it actually does things but not of consequence to my question
}
This seems to have made no difference.
remove the static transient decleration. define your method as follows:
def getComputeStuff(){
def thisCouldChange = 3.1
return 3.1 //it actually does things but not of consequence to my question
}
after that a property call "computeStuff" should appear in your properties list calling getProperties() on the domain class. its very important that you define the return value as def.
I think that if you want your computeStuff method be treated like a property, call it getComputeStuff - name convention as JavaBeans. I'm not sure id it will work, but it's worth a try I think ;)