Grails Web Flow - passing map to first step/state - grails

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 :)

Related

can't display model in gsp page

So I've just started using Grails/Groovy for my senior capstone project and I'm having a bit of a rocky start. The issue I'm having is when I set up a mapping of my model in the controller I can loop through it and print it to the console but when I try and access it from the view I get blank page.
The Domain class is simple:
package ot
class CrewDeskMapping {
String crewDesk
String desk
}
And the Bootstrap file that creates the test data:
...
new CrewDeskMapping(crewDesk:"North",desk:"MON1").save()
new CrewDeskMapping(crewDesk:"North",desk:"TWI1").save()
new CrewDeskMapping(crewDesk:"North",desk:"NWE1").save()
...
Here is my controller:
import ot.CrewDeskMapping;
class DashboardController {
def index() {
def desks = CrewDeskMapping.list()
[desks:desks]
for (d in desks) {
System.out.println(d.desk);
}
}
}
and the console output looks as it should:
MON1
TWI1
NWE1
CHI1
COL1
...
And the relevant part of my index.gsp
<body>
<g:each in="${desks}">
<p>Title: ${it.crewDesk}</p>
<p>Author: ${it.desk}</p>
</g:each>
</body>
The most perplexing part is if I try this same code but with a different Domain it works just fine. I've been at this problem for a couple days now with no avail so any help would be greatly appreciated!
def desks = CrewDeskMapping.list()
[desks:desks]
for (d in desks) { ...
does not return your [desks: desks]
Swap the for and the [desks: ...] or add a proper return statement at the end like this:
def index() {
def desks = CrewDeskMapping.list()
desks.each{ log.debug it.desk } // groovier debug print
// must be last in the method like a regular return in java,
// but groovy allows implicit return of the last statement
/*return*/ [desks:desks]
}

how to make Drop Down Lists in gsp from BootStrap.groovy

I want to make a drop down using the data given in the BootStrap.groovy.
My City domain class
package city.model
class City {
String cityName
static constraints = {
cityName(maxSize: 50) }
}
In my Service class
public class CityService {
def citySelect(String cityName) //this is just a sample
{
City city = new City()
city.cityName = city.findByCityName(cityName)
}
}
controller
public class CityController {
def cityService
def
def selCity() {
def selectCity = cityService.citySelect(params.cityName){
if(selectCity != null){
render view // my view
}
else{
render view // error select again view
}
}
}
BootStrap.groovy
import city.City;
class BootStrap {
def init = { servletContext ->
for(String cityName in ['Addis Ababa', 'Semera','Asosa','Gondar', 'Jijiga','Harar', 'Dire Dawa', 'Bahir Dar',
'Hawassa', 'Arba Minch', 'Adama', 'Mekelle']) {
City.findOrSaveByCityName(cityName).save()
}
}
def destroy = {
}
}
i used this
<g:select name="cities" from="${City.list()}" optionKey="id" optionValue="cityName"/>
in my view but showing error cannot envoke method list on null object
What is wrong within mu code and what should i do to make it work. please any suggestions
<g:select name="city" from="${city.model.City.list()}" optionValue="${cityName}"
noSelection="['':'-Please select the City-']" optionKey="id"/>
this could work fine.
Maybe you have misunderstood the point of Bootstrap.
Bootstrap as per the naming convention is the part that is triggered when your site is booting up.
You would typically use it to ensure required db table records are generated before it has booted up i.e. admin account or in your case generation of some cities.
You would not be using Bootstrap to interact with the records you generated in the way of editing or selecting.
Once this has all be done and saved - you would have also used the Controllers/Views to list/view/update/add cities.
You would create your g:select tags in these views and matching controllers that would query the records you have saved via bootstrap
E2A:
Ok Just read your comment
Either use an import on the top of your gsp
<%# page import="city.City" %>
or call full packaged path to City domainClass city.City.list
<g:select name="cities" from="${city.City.list()}" optionKey="id" optionValue="cityName"/>
To create a list of the Cities you inserted into the Database in the Bootstrap.groovy (in a view i.e [viewName].gsp)
Mark up is like so
<g:select name="city" from="${city.model.City.list()}" value="${city.name}"
noSelection="['':'-Please select the City-']" optionKey="id"/>
However, the Bootstrap should be for initialization of Database and Application defaults, Also to perform start (Startup in the init closure while shutdown in the destroy Closure).
This should work:
<g:select name="cities" from="${city.model.City.list()}"
optionKey="id" optionValue="cityName"/>
However, executing queries like city.model.City.list() in a GSP is not a recommended practice. Instead you should retrieve your data (the list of cities) in a controller action or a service and pass that via the model to the GSP.

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
}

How do I get Shiro's annotations to work in Grails?

I'm trying to apply annotation-based security to my Grails app.
Peter says here that we should be using Shiro's annotations instead of the quasi-deprecated grails-shiro plugin annotations.
How does one get that working?
I'm finding the grails-shiro plugin strays from how Shiro does things, what with the Realm-converting and all. Has anyone tried implementing Shiro directly rather than using the grails plugin? Any success?
Thanks,
Graham.
G'day I have taken over the Grails-Shiro plugin project. I'm currently re-writing the functional tests and can confirm that the shiro annotations do work, with a couple of caveats:
You can use them in controllers on method actions in grails 2.x
You can use them on Service methods
They don't currently work on service classes (I'm investigating this)
e.g. this works on a service
class SecuredMethodsService {
def methodOne() {
return 'one'
}
#RequiresGuest
def methodTwo() {
return 'two'
}
#RequiresUser
def methodThree() {
return 'three'
}
#RequiresAuthentication
def methodFour() {
return 'four'
}
#RequiresRoles('User')
def methodFive() {
return 'five'
}
#RequiresPermissions("book:view")
def methodSix() {
return 'six'
}
}
or in a controller on an action method like this:
#RequiresAuthentication
def unrestricted() {
render(view: 'simple', model: [msg: "secure action"])
}
When using annotations you may need to add an "afterView" filter to catch the AuthorizationException thrown by the annotation e.g.
class ShiroSecurityFilters {
def filters = {
all(uri: "/**") {
before = {
// Ignore direct views (e.g. the default main index page).
if (!controllerName) return true
// Access control by convention.
accessControl()
}
afterView = { e ->
while (e && !(e instanceof AuthorizationException)) {
e = e.cause
}
if (e instanceof AuthorizationException) {
if (e instanceof UnauthenticatedException) {
// User is not authenticated, so redirect to the login page.
flash.message = "You need to be logged in to continue."
redirect(
controller: 'auth',
action: 'login',
params: [targetUri: request.forwardURI - request.contextPath])
} else {
redirect(controller: 'auth', action: 'unauthorized')
}
}
}
}
}
}
I hope that helps. A new version of the plugin should be released RSN.
Cheers,
Peter.
Since nobody is anwering...
I don't know how you get the annotations to work, but I've used shiro in several Grails projects and didn't miss them... So why do you need them?
When you need to permission explicit roles, you can just create some ShiroRoles and assign star-permissions to them: 'book:*' allows a role to execute all actions on the book controller. 'book:list,show' allows a role to only list or show books.
When you need implicit permissions, use a filter. So if you want to give someone access if she is (for instance) the boss of someone, just fetch the object on which you want to decide in a filter and make a decision.
When you need switches in you gsp-code (e.g. show this only if it's an admin), use the shiro tags. Just unzip the shiro plugin and look for the taglibrary. It is well documented.
HTH

Grails web flow

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"
}

Resources