Scope Basics
You'll notice from previous examples that we used a special object called
flow
to store objects within "flow scope". Grails flows have 5 different scopes you can utilize:
request
- Stores an object for the scope of the current request
flash
- Stores the object for the current and next request only
flow
- Stores objects for the scope of the flow, removing them when the flow reaches an end state
conversation
- Stores objects for the scope of the conversation including the root flow and nested subflows
session
- Stores objects inside the users session
Grails service classes can be automatically scoped to a web flow scope. See the documentation on Services for more information.
Also returning a model map from an action will automatically result in the model being placed in flow scope. For example, using a transition action, you can place objects within
flow
scope as follows:
enterPersonalDetails {
on("submit") {
[person:new Person(params)]
}.to "enterShipping"
on("return").to "showCart"
}
Be aware that a new request is always created for each state, so an object placed in request scope in an action state (for example) will not be available in a subsequent view state. Use one of the other scopes to pass objects from one state to another. Also note that Web Flow:
- Moves objects from flash scope to request scope upon transition between states;
- Merges objects from the flow and conversation scopes into the view model before rendering (so you shouldn't include a scope prefix when referencing these objects within a view, e.g. GSP pages).
Flow Scopes and Serialization
When placing objects in
flash
,
flow
or
conversation
scope they must implement
java.io.Serializable
otherwise you will get an error. This has an impact on
domain classes in that domain classes are typically placed within a scope so that they can be rendered in a view. For example consider the following domain class:
class Book {
String title
}
In order to place an instance of the
Book
class in a flow scope you will need to modify it as follows:
class Book implements Serializable {
String title
}
This also impacts associations and closures you declare within a domain class. For example consider this:
class Book implements Serializable {
String title
Author author
}
Here if the
Author
association is not
Serializable
you will also get an error. This also impacts closures used in
GORM events such as
onLoad
,
onSave
and so on. The following domain class will cause an error if an instance is placed in a flow scope:
class Book implements Serializable {
String title
def onLoad = {
println "I'm loading"
}
}
The reason is that the assigned block on the
onLoad
event cannot be serialized. To get around this you should declare all events as
transient
:
class Book implements Serializable {
String title
transient onLoad = {
println "I'm loading"
}
}