aspose file tools*
The moose likes Other Application Frameworks and the fly likes Passing objects across controllers in Grails Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Frameworks » Other Application Frameworks
Bookmark "Passing objects across controllers in Grails" Watch "Passing objects across controllers in Grails" New topic
Author

Passing objects across controllers in Grails

Mark Herschberg
Sheriff

Joined: Dec 04, 2000
Posts: 6037
I'm trying to figure out who to pass an object during a webflow. Specifically I want to pass the User object to the class Foo. After the User object is created, I redirect the flow to the FooController and I can pass the user ID using the call below.

redirect(controller:'Foo',action:'create',id:user.id);

I then look up the User using

User.get(params["id"])

Is this the right way to do it, or is there some way to pass the User object itself?

--Mark
[ March 13, 2008: Message edited by: Mark Herschberg ]
Mark Herschberg
Sheriff

Joined: Dec 04, 2000
Posts: 6037
I figured out the answer.

I can pass parameters through the params tag (when I remember to include the ":" in params:["myparam":"myavlue"]). However, I think the values are all treated as Strings. In that sense I can pass the id or some unique value (e.g. the username field since typically username will be constrained to be unique for all User instance).

However, doing a lookup on both sides is wrong. Rather, I want to pass the actual User object itself. To do that I user the model tag, which works just like params but can pass objects. However, this must be done not using a redirect but rather a chain.

--Mark
Mark Herschberg
Sheriff

Joined: Dec 04, 2000
Posts: 6037
I almost figured out the answer....

Yes, this does pass an object. However, at the end of the create method in the fooController I have

return ['foo':foo]

However I have a User object I've been able to pass into the create method using the model in chain. I now need that User object passed to save method where I can attach it to the Foo object. I'm not sure how to pass an object at this step.

--Mark
Marc Peabody
pie sneak
Sheriff

Joined: Feb 05, 2003
Posts: 4727

In your final save method, check to see if your User was automagically set to your Foo model that create returned.

I know it sounds silly, but I believe that's how she works.


A good workman is known by his tools.
Mark Herschberg
Sheriff

Joined: Dec 04, 2000
Posts: 6037
I checked, it didn't happen. Below is the code from UserInformationController. I create a class User and then have a second class UserInformation which contains additional information about a user. The automatically generated statements don't end in a ";" whereas the lines I added do.





This generates the following output...


in UserInformationController create
user = USER: bob
userInformation = USER INFORMATION: null null is linked to... USER: bob


in UserInformationController save
params = ["dob_day":"13", "lastName":"smith", "dob_year":"2008", "dob_month":"3", "email":"", "dob":"struct", "action":"save", "gender":"", "controller":"userInformation", "firstName":"bobby"]
userInformation = USER INFORMATION: bobby smith is linked to... null


So as you can see in each case I generate an instance of UserInformationController but in the latter I didn't have a User to give to it. I was able to pass one in to the create method using the chain, but dontt don't have to further pass it to save. I'm not quite sure who exactly called the create method and where the returned map wound up.

--Mark
Marc Peabody
pie sneak
Sheriff

Joined: Feb 05, 2003
Posts: 4727

Excerpt from Definitive Guide to Grails
I can't tell from this example/explanation whether or not your create method would have to call chain(..).

It doesn't sound like it's necessary to call setUser, as it says the model builds itself together automatically.

How about adding a println( "user in save: ${chainModel.userInformation.user}" )
to your save method?
Mark Herschberg
Sheriff

Joined: Dec 04, 2000
Posts: 6037
That's what I had thought to. I had replaced the return statement at the end of the create method with the following code:



I get a org.codehaus.groovy.grails.web.servlet.mvc.exceptions.ControllerExecutionException: Action not found in redirect for name [null] This is why I was trying to figure out exactly when and where how create and save get called.

I've also tried it without calling the setUser() method, but then my userInformation instance has no reference to the user instance. It looks like I should be using add or addTo* but I can't seem to get either of those to work. In any case, that instance of userInformation goes away at the end goes away at the end of the create method.

--Mark
Marc Peabody
pie sneak
Sheriff

Joined: Feb 05, 2003
Posts: 4727

Here's a simple example that works. Maybe you can tell me what I'm doing that isn't what you're trying to accomplish.

Flow:
1) Select "UserController"
2) Select "New User" (UserController.create is scaffoldedly called)
3) Enter fields, select "Create" (calls UserController.save)
4) (UserController.save chains to UserInformation.create; create view is displayed, the User previously created is selected in dropdown)
5) Enter favorite color, select "Create" (calls UserInformation.save)


Mark Herschberg
Sheriff

Joined: Dec 04, 2000
Posts: 6037
I haven't seen this explicitly documented, but from what I have come to understand, params passes only Strings whereas you need a model to pass objects. Because favoriteColor is a String, it does get passed through params. If you made it an object (and removed it from the view gsp), you should get the same error I do.

--Mark
Marc Peabody
pie sneak
Sheriff

Joined: Feb 05, 2003
Posts: 4727

If you remove its component from the view, you must replace the component with hidden fields.

So I removed my User dropdown from the UserInformation create page and replaced it with:

and it still works.
Mark Herschberg
Sheriff

Joined: Dec 04, 2000
Posts: 6037
Why do you need a hidden field? Can't you just set it programmatically in the controller?

--Mark
Marc Peabody
pie sneak
Sheriff

Joined: Feb 05, 2003
Posts: 4727

Ok... use flash scope:

It requires a bit more dancing around but this works.
Mark Herschberg
Sheriff

Joined: Dec 04, 2000
Posts: 6037
Good idea; but...

I had also figured that I can always pass the username (which is unique) through params, and then recreate the User instance from a lookup. I nixed that because while it would work, it just seemed like it wasn't the right way to do it.

While flash would work, wouldn't I want to use one the other other more persistent flow states starting from the create method in the UserController (as opposed to UserInformation)? I'm not quite sure which one persists across two controllers.

(I don't want to sound ungrateful, I appreciate the effort, I just want to learn the right way to do it. :-)

--Mark
Marc Peabody
pie sneak
Sheriff

Joined: Feb 05, 2003
Posts: 4727

Originally posted by Mark Herschberg:
While flash would work, wouldn't I want to use one the other other more persistent flow states starting from the create method in the UserController (as opposed to UserInformation)? I'm not quite sure which one persists across two controllers.

Flash scope just says "Make me available for one more request." Chaining actually uses flash scope under the covers.

You could put it in session scope (Controller Scopes), but then you have to worry about cleaning up after yourself (remove it from session at the right time). To me, session scope is a sloppy idea.

I'm used to the whole hidden field idea from other web frameworks, so I'd probably lean towards that solution.
Mark Herschberg
Sheriff

Joined: Dec 04, 2000
Posts: 6037
Can you explain the whole hidden field thing to me? Why do we need it? Is there a security risk if someone modified the HTML page? Is it even visible to the end user's browser?

--Mark
Mark Herschberg
Sheriff

Joined: Dec 04, 2000
Posts: 6037
So I was able to get this to work following your code but without the need for the hidden tag.

One interesting issue... I didn't originally implement Serializable in User or UserInformation. The documentation said I needed to, but I was going step-by-step to see where the boundaries and failure points would occur. Interestingly I was able to pass the User object through the chain without an issue. It was only when passing the UserInformation about through the flash scope that it didn't work. Unfortunately it did "work" in that it didn't cause any problems. The flash.userInformation was null, basically it fialed quietly; once I implemented Serializable it worked. I asked on the grails user's list if I should file a bug.

Thanks for all your help Marc, I really appreciate it. (Maybe as a thanks I'll name a son after you--but you know, spelled correctly with a "k". ;-)

--Mark
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
[Mark]: Thanks for all your help Marc, I really appreciate it. (Maybe as a thanks I'll name a son after you--but you know, spelled correctly with a "k". ;-)

Karc?
[ March 15, 2008: Message edited by: Jim Yingst ]

"I'm not back." - Bill Harding, Twister
Marc Peabody
pie sneak
Sheriff

Joined: Feb 05, 2003
Posts: 4727

The hidden field isn't terribly secure. If it's an internal app it's probably not a huge deal. Anyone crazy enough to tweak the HTML (with Firebug or something) would be within choking distance.

If a hidden field were just some normal field you didn't want to be an input field, no big deal. But in the scenario here we're passing the identifier for a very critical entity object. Probably not the best idea to use the hidden field for this, so I take back what I said about my preference... unless I wasn't worried at all about my users being malicious.

I need to get on the Grails lists.

Please keep posting here, Mark. This is helping me learn.

Jim, I think he meant Mkrc. The pronunciation is similar to taking an uppercut punch to the throat.
Mark Herschberg
Sheriff

Joined: Dec 04, 2000
Posts: 6037
From the discussion on the grails, I'm thinking I should use a webflow, so I've posted a new question here.

As for the spelling of the name, it would be "sjdfkwhj" but all the letters are silent, so it's pronounced "Bob."

--Mark
 
It is sorta covered in the JavaRanch Style Guide.
 
subject: Passing objects across controllers in Grails