Grails : Updating Index.gsp from another controller - grails

My Grails app is using version 2.3.6
In this app there's a Controller class:
class UserOrderController {
def index(Integer max) {
params.max = Math.min(max ?: 20, 100)
respond UserOrder.list(params), model:[userOrderInstanceCount: WorkOrder.count()]
}
}
Now in another Controller i am accessing the UserOrder objects and filtering based on product ID. Product ID is a string property in UserOrder domain class.
The other controller is:
class UserOrderFilterController {
def getUOBasedOnID () {
// Here i get a new list for **UserOrder**
// Now i want to draw the UserOrderController page with the new list
}
}
I am pretty new to Grails and not sure how to do this.
Should i be creating a new INDEX function in UserOrderController class and pass the new list?
Like shown below:
class UserOrderController {
def index(Integer max) {
params.max = Math.min(max ?: 20, 100)
respond UserOrder.list(params), model:[userOrderInstanceCount: userOrder.count()]
}
def index(List UserOrder) {
// create page???
}
}
UPDATE:
The UserOrderFilterController has it's own index.gsp file.
What am doing is: Accessing all the objects for UserOrder domain class and filter them based on a property.
Now in the index.gsp of UserOrderFilterController i will show the total number of objects/orders found. This number will be shown with a hyperlink using href and when the user clicks on it, it will go to index.gsp page of UserOrderController with only the filtered UserOder's displayed.
So what am expecting is:
<a href='${createLink(controller:'UserOrder', action:'index')}'>%s</a>
A href like shown above with a params field that will have the filtered list of UserOrder's.
I have no idea how to add the params/list to href. Is this possible?

Each action in Grails has a matching view. Duplicating actions is asking for trouble. If you have some new functionality that deserves a page of its own, then you should create a new action and a new view.
If it belongs on the home page, then put it through the same index method.
Note that in grails, you simply pass the values to the .gsp page and the .gsp page is what handles the formatting of the data. Your action should have absolutely 0 knowledge of the structure of the view. This is a core concept in MVC.
In this case, you can redirect to a controller, as per the docs. The one that should interest you the most is this:
redirect(controller: "yourController", action:"index", params=[filteredList: filteredList]);
This will redirect to the existing index action in your UserOrderController, and pass in the filtered list. From there, you can have something like..
if(params.filteredList) {
// You know this came from the filtered controller, so display this and don't
// make a DB call.
}
Point to Note
The structure of your grails application worries me. Why have you got an entirely separate controller for simply filtering data?
Edit
Taking the createLink approach can be improved a bit. Instead of..
<a href='${createLink(controller:'UserOrder', action:'index')}'>%s</a>
You can use the g:link functionality:
<g:link controller="UserOrder" action="index" params=['filteredList':filteredList]>%s</g:link>

If you are trying to create new controller for filter UserOrderFilterController, you can do
class UserOrderFilterController {
def getUOBasedOnID () {
//get filtered list
respond filteredList, [view:'userOrder/index',model:[userOrderInstanceCount: filteredListCount]]
}
}
You can see more about respond here.

Related

Grails disallow normal create

Grails noob here. I am build a simple grails app where I have the default create/ edit / list / show pages for a Product domain object. I also have the ProductController. The create method by default does this:
def create() {
[productInstance: new Product(params)]
}
My understanding is this will take me to the create page. And there will be nothing in the params object so everything will be blank.
I want to change the behaviour so that the when the create() method is invoked, a pop up is returned to the User: Sorry you are not allowed to create new data.
The user will stay on the same page and the only thing that will happen is the pop up.
How do I do this without using JavaScript?
Thanks
If the create method is not allowed, why not just remove the create() method from the controller, delete the create.gsp page and remove the 'New' button from the list page?
If you do want to offer a 'New' button and display a popup message that says you can't use the button, that is probably easiest done in javascript.
If you can just display the message in the default grails message panel rather than in a popup, then change the controller method to
def create() {
flash.message = "Sorry you are not allowed to create new data."
redirect(action: "list")
}
Prior to sending the user to the GSP view, you should make a decision in the controller, if the user is allowed to execute the create action. In your controller this could be something simple like:
params.allowed = false
The result of this decision is then passed to you GSP where you can evaluate it inside the GSP using something like:
<g:if test="${params.allowed == false}">
Alert: you are not allowed...
</g:if>
For the alerts, there are good looking alternatives to javascript like: http://getbootstrap.com/components/#alerts

In Grails, how do I get a property for a relationship on a Spring Security user?

I'm using Spring Security. Let's say I have a User that belongs to a Store. The Store has a property that determines if it has an online shop or not:
class Store {
...
boolean isOnline
...
}
class User {
...
def belongsTo = [ store: Store ]
...
}
In a global navbar template that's displayed on every single page, I'd like to display a link if the currently-logged-in user's Store is online shoppable. I'm currently using this code in the toolbar template:
<g:set var="userId" value="${sec.loggedInUserInfo(field: 'id') as Long}"/>
<g:set var="user" value="${User.get(userId)}"/>
<g:if test="${user.store.isOnline}">
<li>
<g:link controller="store" action="redirect">Shop Online</g:link>
</li>
</g:if>
Although this code works, is there a better way of handling this logic? I don't particularly like that I'm executing this code for every page render, and I especially don't like that I'm using User.get() directly in the view.
In other words, is there anything equivalent to springSecurityService.currentUser for a view that allows me to grab the actual User object and travel down its relationships?
You can move this logic to a filter:
class UserFilters {
SpringSecurityService springSecurityService
def filters = {
loadUser(controller: '*', action: '*') {
before = {
def user = springSecurityService.currentUser
request.setAttribute('currentUser', user)
}
}
}
}
So you'll be able to use currentUser from a view:
<g:if test="${currentUser.store.isOnline}">
...
</g:if>
More about filters: http://grails.org/doc/latest/guide/theWebLayer.html#filters
I would advice to move this logic to controller and prepare view for that controller (small chunk that you're using for navbar). You can then use <g:include/> tag to reuse controller action with that view.
.get() will generally pull from 2nd level cache before hitting the database, so that isn't what I'd be concerned with. The location of your logic is just misplaced. I'd put your view logic like that in a custom taglib.

Grails: How do I map a URL to a controller inside a custom plugin?

I have a controller inside a plugin called TextController with an action called index
I then have a view inside the calling application called test-plugin.gsp with the following include tag:
<g:include controller="text" />
This works great. The problem is that the plugin's controller actually needs to identified by a package-like name, such as: com.foxbomb.fxportal3.components.text, and I need to reference it as such. So my thinking us to try and have a URL mapping for the plugin, such as:
"/components/com.foxbomb.fxportal3.components.text/" {
controller = "text"
action = "index"
}
I also don't know how to create an include in the calling application to try and call that URL, something like (I know you don't get a URL attribute):
<g:include url="/components/com.foxbomb.fxportal3.components.text"/>
In other words, I want to include a controller / action by its mapping as opposed to its controller / action name.
<g:include> doesn't allow a url attribute, which surprises me, but looking at the source of the <g:include> tag it should be possible to implement this in your own taglib fairly easily (untested):
import org.codehaus.groovy.grails.web.util.WebUtils
class IncludeTagLib {
def grailsUrlMappingsHolder
def includeUri = { attrs, body ->
def mapping = grailsUrlMappingsHolder.match(attrs.uri)
out << WebUtils.includeForUrlMappingInfo(request, response, mapping,
attrs.model ?: [:])?.content
}
}
You'd say <g:includeUri uri="/components/com.foxbomb.fxportal3.components.text"/> in your GSP.

Grails Web Flow: Redirect to the webflow from another controller action?

Here's what I think I want to do:
class MyController {
def goToWizard = {
if (params.option1)
redirect actionName:'wizard1', params:params
if (params.option2)
redirect actionName:'wizard2', params:params
}
def wizard1Flow = {
start {
action {
// put some values from the params into flow scope
[thingsThatGotPassedIn:params.thingsThatGotPassedIn]
}
on('success').to 'nextThing...'
}
// wizard 1 implementation...
//...
done {
redirect view:'somewhereElse'
}
}
def wizard2Flow = {
start {
action {
// put some values from the params into flow scope
[thingsThatGotPassedIn:params.thingsThatGotPassedIn]
}
on('success').to 'nextThing...'
}
// wizard 2 implementation...
//...
done {
redirect view:'somewhereElse'
}
}
}
I've tried something like this, but I don't seem to ever get into the webflow. Is this a valid approach?
The reason for all this is that I have a gsp that looks like so (a form with 2 submit buttons inside, each one should trigger a different webflow)
<g:form action="goToWizard">
...
<g:submitButton name="wiz1" value="Goto Wizard1"/>
<g:submitButton name="wiz2" value="Goto Wizard2"/>
</g:form>
There are some input elements inside the form that I want to pass the values of into whichever webflow gets called. I'd rather just have the form submit call the appropriate webflow directly (the way all the examples that I've seen work), but there are two webflows, and only one form. How can I accomplish this?
I'm also interested in alternative implementations, if you think this is the wrong way. I'm new to webflows in grails.
Take a look at actionSubmit tag in grails documentation. I think, you should use actionSubmit instead of submitButton
actionSubmit creates a submit button that maps to a specific action, which allows you to have multiple submit buttons in a single form. Javascript event handlers can be added using the same parameter names as in HTML.
By this approach, You no need to mention action in form tag, i.e. No need to make a check in goToWizard. You can send contents directly to your particular action.
Is this the solution to your problem?

Grails: Unable to get model data from controller to view

I have been using Grails for last 3 weeks (learning and working). I have been working on porting a JSP/Servlet application to Grails and it has been absolute "fun" porting the application.
I have facing an issue and have been searching, reading but could not solve it yet.
In the GSP page I have a textfield and search button where user enters ProductID. I have a controller which is called from jQuery Ajax method when a search button is clicked.
// ----- Part of jQuery Ajax call ---
$.ajax({
type : "post",
url : '/${grailsApplication.metadata['app.name']}/product/checkProductAjax',
data : "pid="+proID,
// ----- Contoller code-----
class ProductController {
def scaffold = true
def checkProductAjax = {
def product= Product.findByProductId(params.pid)
if(product) {
[product: product] // model to get data in GSP page.
render(product.toString() + ":" + product.id)
} else {
render("none")
}
}
}
Ajax call and everything works fine. The problem I am facing is how to get the model (i.e. the Product data back to the GSP page i.e. [product: product] and display in GSP as for e.g. Product Name: ${product}
How can I get it working? I have read examples where it is mentioned that just setting the model [product: product] will help to get the data in GSP.
Product Name: ${product} always shows blank in the GSP page Product Name:
Please tell me what I am doing wrong.
Cheers!
Jay Chandran
[product: product] and render(product.toString() + ":" + product.id) are incompatible. When you see a controller action whose last line is a map like [product: product] this is the implicit return value since it's the last statement of the action closure - it's the equivalent of return [product: product]. But if you have a map in the middle of a method it's just created and discarded. It's pretty much equivalent to
def ignoreThisMap = [product: product]
// other code
Since you're making an Ajax call though, putting the product in the model doesn't make sense since you aren't going to re-render the GSP. You're going to render text, JSON, XML, or some other content that the client-side JavaScript will use to update some subset of the html. So you probably want something closer to
if (product) {
render product as JSON
}
else {
render "none"
}
and then you can use jQuery or Prototype to evaluate the JSON and extract the data in your JavaScript.
you probably want to use the grails tags that are created for this type of work; it wraps the AJAX code all up for you
http://www.grails.org/Ajax

Resources