views:

118

answers:

2

Hi, it's my first time using webflows in Grails and I can't seem to solve this.

I have 3 domain classes with associations that look something like this:

class A {
  ...
  static hasMany = [ b : B ]
  ...
}

class B {
  ...
  static belongsTo = [ a : A ]
  static hasMany = [ c : C ]
  ...
}

class C {
  ...
  static belongsTo = [ b : B ]
  ...
}

Now, the GSP communicates with the controller via Javascript (due to my use of Dojo). When I try to remoteFunction a normal action, I can do something like this:

def action1 = {
   def anId = params.id
   def currA = A.get(anId)
   def sample = currA.b?.c // I can get all the way to 'c' without any problems
   ...
}

However, I have a webflow and the contents of that action is in the webflow... It looks something like this:

def someFlow = {
   ...
   someState {
      on("next") {
         def anId = params.id // this does NOT return a null value
         def currA = A.get(anId) // this does NOT return a null value
         def sample = currA.b // error already occurs here and I need to get 'c'!
      }.to("somePage")
      ...
   }
   ...
}

In this case, it tells me that b doesn't exist... so I can't even get to 'c'. Any suggestions on what to do??? Thanks... getting real desperate...

A: 

Hmmm, not quite sure what the problem is, but I do find something curious. In your first block, you use the following:

 def sample = currA.b?.c

According to the classes you've created, currA.b is a Collection of Bs, not a single one. Therefore currA.b?.c would be a Collection of Collections of C class instances, one Collection for each of the Bs in currA.b

I'm not sure what would happen if currA.b was empty... given the safety operator, which equates empty with null (via GroovyTruth), I'd say sample would be null.

None of this helps determine why the line you specify generates an error, though. Perhaps you can show us what the error is? Is it an NPE or some other?

Looking at this more, It also looks like you're missing the "to" function call:

on("event") { intraEvent code }.**to** "eventHandlerAction"

Perhaps the error you're seeing is the result of this?

Seems not.

Ok, only other thing I can think of is that some time ago (v1.1 I think), they made it so you have to use "this." a lot more inside flow actions. Essentially, in order to access controller class-level objects and methods, you need to put "this." in front of the access to them. Say you wanted to put that intraEvent code you've got into a method:

def goGetC() {      
   def anId = params.id 
   def currA = A.get(anId) 
   def sample = currA.b 
}

In order to call that method from your event code, you'd have to use:

   someState {
      on("next") {
         this.goGetC()
      }.to ("wherever")
   }

If you tried to call goGetC() without the "this.", you'd instead be ending your eventhandler with the event "goGetC". I'm not sure if your simplification of your real code is hiding a similar case or not, and I'm still not sure what error exactly you're getting, but this was something that bit me a while ago and it's Flow specific. Hope this helps.

Bill James
Oops, sorry, this is what happens when I try to generalize my codes from the actual ones :D Thanks for pointing that out.Um, for now (just so that it gets simplified), let's just say that there's just 1 B (I'll worry about the collection of B's later...) but the definitions still hold.Most of the time, the error's generated by the flow (about empty maps) so I think it's a separate issue. However, I think the more appropriate thing to say is that at that line, B returns as null INSIDE the webflow... if I put this function OUTSIDE the webflow (like the sample 'action1', it isn't null
callie16
It makes me wonder about bindings... is it correct to assume that domain bindings inside and outside a webflow are different? I mean, calling a normal action (action1 in this case) doesn't result in B being null... but inside a webflow, it is null. If that's the case, any ideas on how to solve that? Thanks!
callie16
I have noticed that hasMany relationships do implement a bit differently while in a flow (for some reason)... Specifically, when taking objects on and off the flow scope in between requests. I'll notice that a hasMany collection that I've been sure to fully load (not lazy) before placing it on the scope will revert to a special implementation list, not just ArrayList when I pull it off the scope. Check to see what actual implementation type currA.b is and try to convert to a an actual B before trying to reach c?
Bill James
I added some more to my answer above... are you missing the "to" part of the event descriptor?
Bill James
Hi Bill, thanks again for the response. Actually, yes, I did notice that the collection that comes back is of type PersistentSet (or something like that). I'll try to convert it and get back to you. Yup, the actual codes has the "to" part :D Missed this one here... again hehe thanks again for pointing that out!
callie16
If it's inside the webflow, I can't see what type it is... even if I did an eager fetch, B still returns as null. On the other hand, if it's in action1, it's of type B...
callie16
Changed my answer again with kind of a last-ditch try.
Bill James
Hi, thanks again for the tip, will try it out =) For now, I'm beginning to suspect the saving of the data itself. Here's why: Table A's id is generated by us so let's call it OID. The foreign key that corresponds to this in table B is A_ID. Now there seems to be an intermediate table between A and B called A_B that has these fields: A_B_ID and B_ID (in B and C, there's table B_C with B_C_ID and C_ID but B's id is autogen in grails so no foreign key in C). Now, when I save (e.g. A.addToB(b)), OID and A_ID are filled up but A_B_ID and B_ID are not. Not with B and C though. They save normally.
callie16
Normally in that when I do a B.addToC(c), B_C_ID and C_ID in table B_C is filled up. I'm beginning to think this has something to do with my problem...
callie16
A: 

I've also put up a related question here.

Thanks again

callie16