I have the following code that grabs all controllers, sorts it, and outputs in li tags:
<g:each var="c" in="${grailsApplication.controllerClasses.sort { it.fullName } }">
<li<%= c.logicalPropertyName == controllerName ? ' class="active"' : '' %>>
<g:link controller="${c.logicalPropertyName}">${c.naturalName}</g:link>
</li>
</g:each>
I have a need to filter out controllers by package i.e. grab controller from a certain package.
For example:
com.app.module.mars.controller.HelloController
com.app.module.venus.controller.PrintController
As you can see I'm packaging controllers by modules, so mars will have its own set of controllers and venus will have its own. Then in the UI I want to use the above code (with some filter) which will show modules as main-menus and their controllers as dropdowns.
How can I apply such a filter? Or if you could guide me in the right direction would be great. Thanks.
You can use GrailsClassUtils.isClassBelowPackage() which takes a class and a list of packages as the arguments. So this should do the trick:
GrailsClassUtils.isClassBelowPackage(c.class, ['com.app.module.mars'])
Edit: grailsApplication.controllerClasses probably gives you a list of GrailsClass objects, so you'd want to use c.clazz instead of c.class like
grailsApplication.controllerClasses.each { c ->
GrailsClassUtils.isClassBelowPackage(c.clazz, ['com.app.module.mars'])
}
You can use Collection#groupBy to group the controller classes by package name.
I don't have a Grails system to make a quick test right now, but this would be a little example of grouping classes by package name:
def classes = [Integer, List, String]
def classesByPackage = classes.groupBy { it.package.name }
assert classesByPackage == ['java.lang': [Integer, String], 'java.util': [List]]
You can then iterate through each packageName to make each menu and through each class under that package name to make each menu item. Something like...
classesByPackage.each { packageName, packageClasses ->
println "Menu $packageName"
packageClasses.each { println " Item $it.simpleName" }
}
... but with GSP-style loops :)
Related
In grails I have a menu. I'm trying to highlight the menu item for the active page:
link1
link2 (show as bold if the controller action partially matches this gsp name)
link3
I have working code but it seems like there would be a better way.
Taglib works fine:
def active = { attrs, body ->
for (page in attrs.check) {
if (params.action.startsWith(page)) {
out << "current"
break
}
}
}
This option works fine, but seems wordy:
<li>Contact Info</li>
<li>About You</li>
This blows up:
<g:link action='myProfile' class="${<xyz:active check='${['myControllerAction']}'/>}">My Profile</g:link>
I don't believe you can pass a taglib as a parameter to g:link
I also have the requirement that multiple gsps/actions would cause a link to be active because of how they are named:
aboutYouLocation
aboutYouBackground
aboutYouEducation
all make this link the active one:
About You
I can do a partial match, but I've also got some actions/gsps that begin with aboutYour (extra R) resulting in my use of the array being passed into my taglib.
There's a standard way of doing that with the Platform Core plugin. It will provide you a Navigation API:
grails-app/conf/AppNavigation.groovy
navigation = {
// Declare the "app" scope, used by default in tags
app {
contact(action: 'contactInfo')
about(action: 'aboutYouFamily')
}
}
*grails-app/view/_menu.gsp* (template that you can use in your layout or GSP's)
<nav:menu scope="app" id="navigation" />
You can also customize the html generated for your menu, check the custom item rendering.
i'm working in Multilingual grails application (English and arbaic) , i want when the user chooses Arabic language the view's labels will be on the right side of the page and in English on the left side , how this can be achieved ?
thanks
You can use internationalization in grails through messages.properties file, you can define message signature in files and and they can be accessed through ?lang=es on the URL, you may need to have two files one for english and another for Arabic.
for example define in the messages.properties:
vendor.link.dashboardLink = Vendor Dashboard
and on the GSP page you can access it like:
<g:message code="vendor.link.dashboardLink" />
you can find more about internalization at grails doc have a look at http://grails.org/doc/2.2.1/guide/i18n.html
If the views have differences beyond simple string substitution, I would recommend using a different set of views based on locale:
Example controller code:
import org.springframework.web.servlet.support.RequestContextUtils as RCU
class ExampleController {
final static String englishLanguageCode = new Locale('en').getLanguage()
final static String arabicLanguageCode = new Locale('ar').getLanguage()
def differentViews() {
def currentLocale = RCU.getLocale(request)
switch(currentLocale.language) {
case englishLanguageCode:
render view: 'englishView'
break
case arabicLanguageCode:
render view: 'arabicView'
break
default:
// pick a default view or error page, etc.
}
}
}
I've decided to step into the Backbone arena & after searching through BB's & Marionette's doc, I'm having a little trouble with something seemingly simple.
I'm simply trying to customize what's being display on the third record.
Here's how I would do it just using Rails
<% #posts.each_with_index do |post, i| %>
<% if i == 1 || i == 7 || i == 14 %><!-- 1st, 7th, & 14th record -->
display title, description & something else
<% else %><!-- other records -->
display only title
<% end %>
<% end %>
Using Backbone + Marionette + Underscore, here's how my records are displayed:
controller
postsRegion: (posts) ->
postsView = #getPostsView posts
postsView.on "childview:posts:post:clicked", (child, post) ->
App.vent.trigger "posts:post:clicked", post
#layout.postsRegion.show postsView
getPostsView: (posts) ->
new List.Posts
collection: posts
view
class List.Post extends App.Views.ItemView
template: "posts/list/_post"
tagName: "li"
className: "span4 item"
events:
"click" : -> #trigger "posts:post:clicked", #model
How do I make the the 1st, 7th, & the 14th (or just the third) record display something different? Also, being more of a designer, could anyone suggest any further readings on views using this js library?
In your collection view, you should be able to pass options to item views based on index like follows:
MyCollectionView = Backbone.Marionette.CollectionView.extend({
itemView : MyItemView,
itemViewOptions : function (model, index) {
if (index % 3 == 0) {
return { specialValue : "foo" };
} else {
return {};
}
}
};
MyItemView = Backbone.Marionette.ItemView.extend({
onRender : function () {
if (this.options.specialValue) {
// DO SOMETHING SPECIAL
}
}
};
The Marionette docs are actually really awesome. The function in question can be found here.
You're likely going to run into issues with this type of behavior: Marionette collection views render an item view for each model in the collection. These item views, in turn, only adapt what is displayed (i.e. the rendered template) according to data in the model.
If you want to highlight every 7th item, I'd see the following options:
Add jQuery/javascript code to an onRender method to style those rows (easiest, but might have performance implications with large collections)
Rewrite the collection/composite view's appendHtml to style the element if the number of rendered views (obtained via jQuery selector) is divisible by 7 (see https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.collectionview.md#collectionviews-appendhtml), or by using an external counter (e.g. defined on the view)
Define your own view to receive a collection, render a child view for each item and attach it to the DOM (basically using plain Backbone, and reimplementing Marionette concepts)
All in all, you'll be facing an uphill battle, and your life will be MUCH easier with Marionette if you could instead use a model attribute for the styling purposes. Note that this model attribute wouldn't have to be persisted on the server...
I have two domain classes
class Contract {
String number
static hasMany = [statements:Statement]
}
class Statement {
String code
static hasMany = [contracts:Contract]
}
I would like to show all statements available in my gsp with a checkbox next to each, allowing the user to choose which statements are applicable to the contract. So something like:
[ ] Statement Code 1
[ ] Statement Code 2
[ ] Statement Code 3
I started off with this:
<g:each in="${Statement.list()}" var="statement" status="i">
<g:checkBox name="statements[${i}].id" value="${statement.id}" checked="${contractInstance.statements.contains(statement.id)}" />
<label for="statements[${i}]">${statement.code}</label>
</g:each>
But i just cannot get a list of checked statements to the controller (there are null elements in the list, there are repeated statements...).
Any idea how to achieve this?
This is possible, but it does require a bit of a hack. First off, every checkbox must have the same name, "statements":
<g:each in="${org.example.Statement.list(sort: 'id', order: 'asc')}" var="statement" status="i">
<g:checkBox name="statements" value="${statement.id}" checked="${contract.statements.contains(statement)}" />
<label for="statements">${statement.content}</label>
</g:each>
Second, in the controller you have to remove the "_statements" property before binding:
def contract = Contract.get(params.id)
params.remove "_statements"
bindData contract, params
contract.save(failOnError: true)
The check box support hasn't been designed for this use case, hence the need for a hack. The multi-select list box is the one typically used for this type of scenario.
I personally prefer to get the list of Id's in this case.
<g:each var="book" in="${books}">
<g:checkBox name="bookIds" value="${book.id}" ...
</g:each>
Command Object:
class BookCommand {
List<Serializable> bookIds
}
In controller action:
BookCommand bc ->
author.books = Book.getAll(bc.bookIds)
Change the checkbox to something like this.
<g:checkBox name="statements.${statement.id}" value="true" checked="${contractInstance.statements.contains(statement)?:''}" />
and then in the controller, in params.statements you will get a list with the IDs of the checked statements.
Also notice the ?:'' in the checked property, it's a good idea to add it because any value(even 'false') in the checked property is interpreted as checked.
Are you mapping request directly to Contract? It's much more secure to map incoming request into an Command object.
As about mapping a list - values are mapped only to existing elements. I mean it cannot create new list elements. You need to prepare it before mapping. If you know that there is always 3 elements, you can make:
class ContractCommand {
List statements = [
new Statement(),
new Statement(),
new Statement(),
]
}
and map request to this object
Grails newbie - I'm trying to create URIs for the value attributes in my select markup (iterating over each object of a domain class). I tried using the createLink tag in my tag like so:
<g:select name="project.id" from="${Project.list(sort:'start', order:'desc')}" optionValue="${createLink(controller:'project',action:'show')}/${it.id}" noSelection="['null': 'select project']/>
Obviously I get a GSP exception, which explains that "/[mycontroller]/[myaction]/null" is not a property of the object.
Is there any other way of constructing these URI's inside a <g:select> (e.g. <option value="/my/uri/"> without resorting to a loop and constructing the values "manually"?
It can be easily done by adding an extra method to Project domain:
class Project {
static transients = ['optionValue']
String getOptionValue() {
def g = ApplicationHolder.application.mainContext.getBean(
'org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
return g.createLink(controller:'project',action:'show', id: id)
}
}
and using it as:
<g:select name="project.id" from="${Project.list(sort:'start', order:'desc')}" optionValue="optionValue" noSelection="['null': 'select project']" />