• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Devaka Cooray
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Jeanne Boyarsky
  • Tim Cooke
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Tim Moores
  • Mikalai Zaikin
  • Carey Brown
Bartenders:

Passing objects across controllers in Grails

 
Author
Posts: 6055
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Author
Posts: 6055
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Author
Posts: 6055
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
pie sneak
Posts: 4727
Mac VI Editor Ruby
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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.
 
Mark Herschberg
Author
Posts: 6055
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 4727
Mac VI Editor Ruby
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Author
Posts: 6055
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 4727
Mac VI Editor Ruby
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Author
Posts: 6055
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 4727
Mac VI Editor Ruby
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Author
Posts: 6055
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Why do you need a hidden field? Can't you just set it programmatically in the controller?

--Mark
 
Marc Peabody
pie sneak
Posts: 4727
Mac VI Editor Ruby
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Ok... use flash scope:

It requires a bit more dancing around but this works.
 
Mark Herschberg
Author
Posts: 6055
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 4727
Mac VI Editor Ruby
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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
Author
Posts: 6055
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Author
Posts: 6055
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
Wanderer
Posts: 18671
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
[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 ]
 
Marc Peabody
pie sneak
Posts: 4727
Mac VI Editor Ruby
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Author
Posts: 6055
8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
Weeds: because mother nature refuses to be your personal bitch. But this tiny ad is willing:
a bit of art, as a gift, the permaculture playing cards
https://gardener-gift.com
reply
    Bookmark Topic Watch Topic
  • New Topic