pass data from controller to filter - grails

In a Grails application I'm looking for some way to pass data from a controller action to a filter that runs after the action. I was thinking of something like:
class MyController {
def myAction = {
render(view:"myView", model:[key: "value"])
passData {
// Do some processing here
name = "paolo"
age = 38
}
}
}
public class MyFilters {
def name
def age
def filters = {
myFilter(controller: "*", action: "*") {
after = { model ->
// Do something with name and age
return true
}
}
}
}
The original plan was to do the following in the init() closure of BootStrap.config:
Use meta-programming to add a "passData(Closure pdClosure)" method to all the controllers
Set the delegate of pdClosure to MyFilters, so that when the name and age properties are set within this closure, they are set on the MyFilters instance.
However, I realised this won't work as there's no obvious way for me to access (from BootStrap.init) the MyFilters instance that will be called for a particular controller.
Equally, there doesn't appear to be any way to access the controller instance from within the filter. Of course, I could just stuff all the data into the model, but I'm wondering if there's a more elegant way to pass data between the two?
Thanks,
Don

Passing your data in the model seems pretty darn elegant and easy to me. You can even remove the data from the model in the filter if you don't want it to be available to the view for some reason.

If in your filter you want to modify the data that is a model, then your solution looks fine.
If however you just want to pass some parameters from controller to the filter then request attributes should be better (if you need that data only the for request scope). This way you don't mix the model with some request parameters.

Related

Grails createCriteria() used inside Domain classes

Just got to know about the capability of createCriteria() method. Just wanna know that other than applying it on the Controller, is there a way to apply onto the domain classes as well? Probably on its own mapping to a property like:
static mapping = {
additionalInfo: Page.createCriteria().list()
}
Perhaps you may want to simply create a new field based the target field like the below example:
class myInfo {
String additionalInfo
String[] moreInfo // a transient field
getMoreInfo(){
def myresultmap = createCriteria.list{
// insert any other criteria shenanigans
}
return myresultmap
}
static transients = ['moreInfo']
}
In a controller return a view like normal with the Domain instance of class MyInfo
Then use in view like:
<g:each in="${domaininstancefromcontroller}">
${it.moreInfo[0]
</g:each>
see docs.
Hope this helps.
Just wanna know that other than applying it on the Controller, is
there a way to apply onto the domain classes as well?
Criteria queries are not limited to controllers, you can apply them elsewhere using the same syntax that you would in a controller. However, the particular example you show there is probably going to be problematic because you are trying to use GORM inside of the mapping block which is used to configure GORM.

What is best way to update domain class object with large number of variables

Suppose I have Employee domain class, I want to create object of domain class from params map coming from UI side.
I can create object in two ways as follows
Normal way
Employee employee = new Employee(name: params.name, rollNo:
params.rollNo)
and so on. If domain class has 20 variables, then we need to write all variables in above constructor.
Following is best way to create object
Employee employee = new Employee(params)
Above constructor will populate object with matching params. Right.
Now my question comes here.
If suppose I have existing domain class object fetched from DB, Now I want to update this object from params map coming from UI.
What is best way to do this (like we do in above second option).
I think it is best to use command objects and bind it to the Employee.
here is sample pseudo code:
class EmployeeMgmtController {
def editEmp(EmployeeCmd cmd){
Employee editEmp = Employee.get(1)
editEmp.properties = cmd
editEmp.save()
}
}
class EmployeeCmd{
String id
static constraints = {
id blank:false,nullable:false
}
}
or,
you if your on controller, and still want to use params (and exclude any fields that you don't want to bind):
bindData(editEmp, params, [exclude:['firstName', 'lastName']])
If you want to achieve that in a service class, make your service implement grails.web.databinding.DataBinder then use the bindData method as demonstrated below.
import grails.web.databinding.DataBinder
class MyAwesomeService implements DataBinder {
/**
* Updates the given instance of a domain class to have attribute values specified
* in "newData" map.
*/
MyDomain updateMyDomainAttributes(MyDomain myDomianInstance, Map newData) {
bindData(myDomianInstance, newData)
myDomianInstance.save(flush: true)
}
}

Grails binding one to one associations

When you generate grails views, grails looks at your relationships and generates the right html for your form data to be automatically binded to the back end domain. For one to one associations grails creates a drop down list.
However, you might not want to present that property as a drop down list but something more custom (for example a text field with autocomplete). As soon as you do that the value that comes to the controller from that field, comes in as a String and you have to first:
Clear errors
Perform a findBy based on a given param and assign it to the property of the domain
I really want to avoid doing findBys in the controller as much as possible because it seems like I am doing logic/things that should not go there. The controller should delegate to the Service layer. It is not clear to me from the grails documentation how would I do that by using bindData which seems to work really well with String, date, Integer properties etc.. but I do not see how bindData is used for properties that are other domains.
I also really want to avoid passing the params object to the Service layer as it seems less reusable (or maybe not, correct me if I am wrong). I guess that I do not like how it looks semantically. I would prefer the first over the second:
#Transactional
class WithdrawService {
def addWithdraw(Withdraw withdraw) {
//perform business logic here
}
def createWithdraw(Map params){
//perform business logic here
}
}
Let's take the following example:
class Withdraw {
Person person
Date withdrawDate
}
and the parent lookup table
class Person {
String name
String lastName
static constraints = {
}
#Override
public String toString() {
return "$name $lastName"
}
}
In order for the bind to happen automatically without any extra work grails passes in the following request params to automatically bind the one to one:
person.id
a person map with the id.
[person.id:2, person:[id:2], withdrawDate:date.struct, withdrawDate_month:11, create:Create, withdrawDate_year:2015, withdrawDate_day:10, action:save, format:null, controller:withdraw]
What is the best way to go about this?
Pass two hidden fields that look exactly like this: person.id:2, person:[id:2] that get populated as a result of the Ajax call that populates the autocomplete?
In the controller do a Person.findBySomeKnownProperty(params.someKnownValue)
Or any other approach?

Accessing redirect id from grails "after" filter

I have a standard grails (1.3.7) scaffolded controller save closure as follows:
def save = {
// ... instantiate and save "instance" ...
redirect(action: "show", id: instance.id)
}
... and an "after" filter, e.g.:
def filters =
{
save(controller:'*', action:'save')
{
after =
{
// How do I get the newly created entity's ID here?
}
}
Is there a way to access the new entity's ID within the filter for any controller, without having to add any extra/special code to every controller?
You can put it into request, like
request.setAttribute('CREATED_ID', instance.id)
and get at filter as
def id = request.getAttribute('CREATED_ID')
Update:
Or you also put it into request at beforeInsert event, or at your own event listener: http://grails.org/doc/latest/guide/GORM.html#eventsAutoTimestamping
Save it in the request as recommended by splix or consider using interceptors instead. In an 'afterInterceptor' you have direct access to the model. see Controller Interceptors
Updated
Based on your comments, you could use meta-programming techniques and intercept each 'afterInsert' call on domain classes. For example, if you add this in your Bootstrap:
grailsApplication.domainClasses.each { org.codehaus.groovy.grails.commons.GrailsDomainClass gc ->
gc.metaClass.afterInsert = {
println "$id"
}
}
you should be able to access all newly created domains objects and perform common operations.

Groovy metaprogramming

In a Service of a Grails project, I like to find, at run time, the arguments of Dynamic Methods in order to inform callers.
Also, I like to call the method and if doesn't exist to return an error,
I will appeciate any help.
You can configure URLMappings in grails to get the value of the dynamic method and call it against your object for example you can do the following
In your urlMappings.groovy define a mapping with two embedded variables object and method
"/$object/$method" (controller:"api",action:"invoke")
Define a 'api' controller with an invoke action. See code below with the logic on how to invoke the method on the object
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
class ApiController {
def invoke = {
def object = params.object
def method = params.method
def args
if(object) {
def domainClass = AH.application.domainClasses.find{it.name == method}?.clazz
if(domainClass.metaClass.getStaticMetaMethod(method,args)) {
domainClass.metaClass.invokeStaticMethod(target,input.method,args)
}
}
}
}
In my example, I assumed that you're calling a static dynamic finder on the domain class. You can generalize this to handle instance methods as well. You need however to provide more information such as the object id, in your request to load the object and call the method against it.
"/$object/$id/$method" (controller:"api",action:"invoke")
-Ken
Not sure I understand your question, but the last part about checking if you can call a method on an object, this can be done by checking the meta class of the object you are dealing with like this.
obj.metaClass.respondsTo(obj, 'theMethodYouWantToCall')
obj is the object you want to call the method on, and theMethodYouWantToCall is the name of the method you want to call.
respondsTo will return an empty list [] if the method you are trying to call is not found

Resources