Grails web flow - grails

Is there any way to pass model data to a view state? Consider the following example view state:
class BookController {
def shoppingCartFlow = {
showProducts {
on("checkout").to "enterPersonalDetails"
on("continueShopping").to "displayCatalogue"
}
}
}
If I want to pass the data model [products: Product.list()] to showProducts.gsp, is there any way to do this apart from preceding the view state with an action state that stores the model in flow scope?
Thanks,
Don

Hmm, it's been a bit since I did a flow, and your example is simplistic (just for being an example's sake, I hope).
What your missing is the initial action in the flow. Keep in mind that a "view" flow action as your showProducts is just says what to do when your showProducts gsp POSTS. It's the action that SENT you to showProducts that should create the model to be used in showProducts.gsp
def ShoppingCartFlow = {
initialize {
action { // note this is an ACTION flow task
// perform some code
[ model: modelInstance ] // this model will be used in showProducts.gsp
}
on ("success").to "showProducts"
// it's the above line that sends you to showProducts.gsp
}
showProducts {
// note lack of action{} means this is a VIEW flow task
// you'll get here when you click an action button from showProducts.gsp
on("checkout").to "enterPersonalDetails"
on("continueShopping").to "displayCatalogue"
}
// etc. (you'll need an enterPersonalDetails task,
// displayCatalogue task, and they
// should both be ACTION tasks)
}
Make sense?

Maybe I don't understand the question, but can't you do
render (view:"showProducts",
model:[products: Product.list()]
inside your controller?

You can try this (assuming you want go to checkout):
showProducts {
on("checkout"){
// do somethings here too if you like
// then pass your data as below:
[products: Product.list()]
} .to "enterPersonalDetails"
on("continueShopping").to "displayCatalogue"
}

Related

How can you use session variable to determine view?

I have my global.asax setup with the following Session_Start:
protected void Session_Start()
{
HttpContext.Current.Session.Add("sourceCode", "default");
}
On my controller I have the following:
public ActionResult Index(string sourceCode)
{
if (sourceCode != null && sourceCode != "default")
{
Session["sourceCode"] = sourceCode;
return View();
}
else
{
return View();
}
}
I want to be able to display different partial layouts based on this session variable. What is the proper way to do this? Can I load a partial view from the controller or do I need to handle that on the view?
This is a variable that I want to use site wide to determine special pricing and landing page creatives. Do I have to set this same structure up on every single controller or is there a more global way of doing this?
Thanks,
Brian
If you want to show the layout in all the pages, you might want to add the logic in the layout file. There, you will add something like that (assuming razor)
#if(HttpContext.Current.Session["someValue"]){
#*render some partial*#
}else{
#*render some other partial*#
}
By the convention of MVC, controller should decide which view it should open. For this in controller you have code like this:
public ActionResult Index(string sourceCode)
{
if (sourceCode != null && sourceCode != "default")
{
Session["sourceCode"] = sourceCode;
ViewData["PartialView"] = "partialviewname1";
}
else
{
ViewData["PartialView"] = "partialviewname2";
}
return View();
}
and in view you can write code something like this:
<div>
#Html.Partial(Convert.ToString(ViewData["PartialView"]))
</div>
and if you have decide which partial view you have to load on each and every request then you can write above logic in global action filter. Global action filter get executed before any requested action method. To know more about global action filter you can explore this link.
http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs

is it possible to access the view state from a webflow controller action?

i have a controller that is not on webflow but need to redirect it to a weblflow. The problem is the view I need to access is inside an action of a webflow.
here is my webflow
class EditSpouseContactInfoController {
def index = { redirect(action:"editSpouseContact") }
def editSpouseContactFlow = {
start{
action {
//some codes here
}
on("success").to("editSpouseContact")
on(Exception).to("editSpouseContact")
}
editSpouseContact {
/************************************/
// Veteran Marital History Processing
/************************************/
on("addMaritalHistory"){
flow.contactInstance.properties = params
if(!flow.maritalHistoryLst){
flow.maritalHistoryLst = []
}
conversation.maritalHistoryInstance = new MaritalHistory()
conversation.maritalHistoryInstance.isVeteranMaritalHistory = false
}.to("editSpouseMaritalHistory")
}
}
here is my non weblow controller:
def addMaritalHistory={
MySession session = MySession.getMySession(request, params.id)
def caseInstance = CmCase.get(params.cmCaseIdCmCase.id as Long)
redirect(controller: "editSpouseContactInfo", action: "editSpouseContact ", id:caseInstance.id)
}
The line above works but is it possible for me to directly access addMaritalHistory inside editSpouseContact?like instead of using the above action it would be action: "addMaritalHistory"? Of course it is not working but is there a way to call that as action? thanks
The whole point of web flow is that you can't jump directly into the middle of a flow from outside it. You'll have to add some logic to the initial start state to check for certain parameters in the incoming params and jump to the appropriate state from there.

Detect redirect in Grails

At the end of my save action I redirect to the show action like this:
redirect(action: "show", id: exampleInstance.id)
In my show action I want to be able to detect if someone came directly to this action via a url, or if they were redirected here from another action. I tried request.isRedirected() but it always returns false.
How do I detect if I am in an action as the result of a redirect from another action?
I guess you want to display a confirmation message. Grails has a built-in feature for this kind of use-case:
http://www.grails.org/doc/2.1.0/ref/Controllers/flash.html
Have a look at the example:
class BookController {
def save() {
flash.message = "Welcome!"
redirect(action: 'home')
}
}
In the view you can print or check against flash.message.
In theory, isRedirect checks request attributes. It is equivalent to
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
if(request.getAttribute(GrailsApplicationAttributes.REDIRECT_ISSUED) != null){
println "request was redirect"
}
Try it directly and tell me what happens.
It looks like in the Grails source that isRedirected() is only applicable to the save action, as it gets set in the redirect() method logic, and so wouldn't be set in the show action.
Instead, here are some manual options. One is to add a flag to the flash object, which is then tested in the redirect action. Since it is in flash scope it will be cleared at the end of the show action:
def save() {
// Do stuff
flash.redirectFrom = "save"
redirect(action:"show")
}
def show() {
if (flash.redirectFrom) {
// Respond to redirect
}
// Do other stuff
}
Another option is to issue a chain() call instead of a redirect() and test for the implicit chainModel object. The chainModel won't exist when the show action is requested from an external URL:
def save() {
// Do stuff
chain(action:"show",model:[from:'show'])
}
def show() {
if (chainModel) {
// Respond to redirect
}
// Do other stuff
}

Grails Web Flow - passing map to first step/state

I'm learning Grails so forgive me if I'm missing something basic.
I'm trying to create a wizard/web flow using the Grails Web Flow plugin. I'd like for the first step of the flow to render some variables. From what I've read about normal controllers, typically this would be done by passing those variables from the controller to the view using a map. In the WebFlow model though, I don't know where to initialize these variables and how to pass them to the first step. I tried creating an initialize "action" and putting the variable into the flash scope, knowing that it should pass through one redirect, but it doesn't render on the gsp.
How is this done?
Here's a snip of the controller, which prints "4" in the console:
class ServicesController {
def index() {
redirect(action: "initialize")
}
def initialize() {
flash.assessmentTypes = AssessmentType.list()
println flash.assessmentTypes.size
redirect(action: "request")
}
def requestFlow = {
selectAssessments {
on("next") {
// capture assessments
}.to("productInfo")
on("cancel").to("finish")
}
...
And a snip of the gsp, which throws a nullpointer when rendering the size:
${flash.assessmentTypes.size}
<g:each var="assessmentType" in="${flash.assessmentTypes}">
<li><g:checkbox name="assessmentType" value="${assessmentType.id}" />${assessmentType.name}</li>
</g:each>
No problem...
Use a flow initializer to act as the first step in the flow and then move it to the first step on success of the initFlow.
def wizardFlow = {
initFlow {
flow.assessmentTypes = AssessmentType.list(); //<-- put them in the flow so you can access it in step1.gsp
}
on('success').to('step1')
on(Exception).to('handleFlowError')
step1{
on('next'){
flow.asessmentType = AssessmentType.get(params.assessmentType.id);
println("They picked ${flow.asessmentType}.");
}.to('step2')
on('exit').to('exit')
}
step2{
on('next'){ /* do stuff */ }.to('finish')
on('previous').to('step1')
on('exit').to('exit')
}
exit( /* exit flow before finish */ )
finish( /* finish up */ )
handleFlowError( */ not good :( */)
}
step1 GSP....
<g:select name="assessmentType.id" from="${assessmentTypes}" optionKey="id" value="${assessmentType?.id}" />
This is untested but it should work just fine. Enjoy :)

How to get Symfony session variable in model?

How can I pass session variable in symfony model without using sfContext::getInstance()?
The recommended way is called dependency injection, and works like this: you create a setUser() method in your model file, that saves the given parameter to a private property:
class Foo {
private $_user;
public function setUser(myUser $user) {
$this->_user = $user;
}
// ... later:
public function save(Doctrine_Connection $conn = null) {
// use $this->_user to whatever you need
}
}
This looks clumsy, because it is. But without you answering the question what are you trying to do? I cannot give an alternative.
Recommended articles:
What is Dependency Injection? - a post series on Fabien Potencier's blog
Dependency Injection - the design patter in detail on wikipedia
Session variables should be stored as user's attributes.
// in an action:
$this->getUser()->setAttribute('current_order_id', $order_id);
See how to get it back.
// later on, in another action, you can get it as:
$order_id = $this->getUser()->getAttribute('current_order_id', false);
if($order_id!==false)
{
// save to DB
} else {
$this->getUser()->setFlash('error', 'Please selected an order before you can do stuff.');
// redirect and warn the user to selected an order
$this->redirect('orders');
}

Resources