2. About Ixor
• Since 2002
• 40 experienced ICT professionals
• Software development for the JVM
• Consultancy and product development
• Loyal customers in Finance, Telecom and Government
• www.ixor.be
13. Action state: returning a model
saveProject {
action {
def project= projectService.save(flow.projectInstance)
return [project: project]
}
on("success").to("end")
on("ValidationException").to("retry")
on("Exception").to("fatalError")
}
• Merged into flow scope
• Used in next state or view
14. Start and end states
• Start state: first state in flow definition
• End state
• Empty
cancel()
• Redirect
end {
redirect(action: "show", id: flow.projectInstance.id)
}
15. Subflow states
• Like method calls
• Reusable (parts of) flows
• Definition
lead {
subflow(controller: "developer", action: "getDeveloper",
input: [experience: Experience.SENIOR])
on("selected“).to("team")
on("cancel").to("lead")
}
• Events
• Last state of subflow
17. Subflow input (grails 1.4.0)
• Required arguments
• Exception if not provided
• Not possible to use flow standalone
• Default values
def mySubFlow = {
input {
foo() // optional input with no default value
bazz(required: false, value: someConstantValue)
dynamic { flow.someProperty }
dynamicBis (value: someNamedClosure)
}
…
18. Subflow output (grails 1.4.0)
• Define output in end state
selected {
output {
developer {flow.developer}
}
}…
• Retrieve output in calling flow
on("selected") {
flow.projectInstance.lead = currentEvent.attributes.developer
}.to(…)
19. Subflow input and output values
• Constant values
• Defined at flow definition time (application startup)
• Refer to (controller) variables or literal expressions
• Dynamic values
• Evaluated at flow execution time
• Defined with closures
• Applicable in:
• Default input values
• Input values in subflow call
• Output values
20. Scopes
• Standard scopes
• Webflow scopes
• flow
• flow execution, only visible in one flow
• serialized
• conversation
• flow execution, visible for all flows and subflows
• Cf. class members ↔ method arguments (subflow input)
• serialized
• flash
• semantics like grails flash, but different instance
• merged with request scope → omit ‘flash.’ prefix
21. Ajax
• Define transition to current state
• Render template in transition action
stories {
on("remove") {
…
render(template: "newProjectWizard/editStories",
model: [projectInstance: flow.projectInstance])
}.to("stories")
…
• Use remoteLink or formRemote
<g:remoteLink update="editStories" controller="project“
action="newProjectWizard" event="remove"
params="${[name: userStory.name,
ajaxSource: true, execution: params.execution]}">
Remove</g:remoteLink>
22. Testing
• Integration test flows
class ProjectControllerTests extends WebFlowTestCase {
@Override Object getFlow() {
return controller.newProjectWizardFlow
}
protected void setUp() {
//register all subflows
registerFlow("developer/getDeveloper",
new DeveloperController().getDeveloperFlow)
}
void testHappyFlow() {
startFlow()
assert "projectInfo" == flowExecution.activeSession.state.id
controller.params.name = "project name“
signalEvent("next")
assert "x" == flowScope.projectInstance.lead.name
…
23. Tip 1
• Returning flow output to other controller actions
• Use standard grails flash scope
RequestContextHolder.currentRequestAttributes().flashScope.message =
"Project was successfully created"
26. Tip 4
• Kick start flow views
• grails generate-views
• Change
• action attribute in forms and links
• explicitly fill in the controller attribute in forms and links when
using subflows
• add an event attribute to links
• remove the flash prefix in flash.message
• use submitButton in stead of actionSubmit
27. Tip 5
• Bread crumbs
• <flow:breadCrumbs/>
• Tag source code: see demo app
28. Pitfalls
• Implement java.io.Serializable
• Objects in flow and conversation scope
• Events don’t get fired
• Missing methods are not missing (f.e. scaffold actions)
• Limited execution history
• Back navigation
• Ajax calls increase state count
• Last state is subflow state
• Back navigation after flow completes redirects to subflow
29. Alternatives
• Ad hoc flows
• Where to store state?
• What if user quits in the middle and starts again?
• One-page wizards with ajax
• One page
• One controller action for each step
• Where to store state?
• Subflows with overlays