How can I have multiple sets of views per controller in Grails? - grails

I'm rewriting the views for my site but I'd still like to have the originals running because the rewrite is missing features. Is there a way for me to have both and depending upon the URL pick one or the other? They'll be sharing the same domain and controller classes.
For example, http://localhost/app/* goes to the original views and http://localhost/app/test/* goes to the new views.
Thanks!

The context path when using the render function uses the name of the controller by default.
What you could do is add a parameter in your url mapping, is this satisfying ? :
class UrlMappings {
static mappings = {
"/test/$controller/$action?" {
indent = "newviewfolder/"
}
}
}
And, in a controller method
class RandomController {
render(${params.indent}"viewname", model [:], params)
}

Related

Grails createCriteria() used inside Domain classes

Just got to know about the capability of createCriteria() method. Just wanna know that other than applying it on the Controller, is there a way to apply onto the domain classes as well? Probably on its own mapping to a property like:
static mapping = {
additionalInfo: Page.createCriteria().list()
}
Perhaps you may want to simply create a new field based the target field like the below example:
class myInfo {
String additionalInfo
String[] moreInfo // a transient field
getMoreInfo(){
def myresultmap = createCriteria.list{
// insert any other criteria shenanigans
}
return myresultmap
}
static transients = ['moreInfo']
}
In a controller return a view like normal with the Domain instance of class MyInfo
Then use in view like:
<g:each in="${domaininstancefromcontroller}">
${it.moreInfo[0]
</g:each>
see docs.
Hope this helps.
Just wanna know that other than applying it on the Controller, is
there a way to apply onto the domain classes as well?
Criteria queries are not limited to controllers, you can apply them elsewhere using the same syntax that you would in a controller. However, the particular example you show there is probably going to be problematic because you are trying to use GORM inside of the mapping block which is used to configure GORM.

Reusable Grails controller helper methods

How to create reusable Grails controller helper methods which can be used in many controllers?
Right not I have few private methods in one controller. I want to share them with other controllers.
I would like have access to params, redirect etc.
The correct way to share code between controllers is to abstract the logic into a service. See
http://grails.org/doc/latest/guide/services.html
Note that if the service is not required to be transactional you should mark it as such.
If however you have web related logic (such as writing templates or markup to the output stream) then you can also use tag libraries to share logic, as tags can be invoked from controllers. See:
http://grails.org/doc/latest/guide/theWebLayer.html#tagsAsMethodCalls
You can use Mixins to you put all your common code:
// File: src/groovy/com/example/MyMixin.groovy
class MyMixin {
private render401Error() {
response.status = 401
def map = [:]
map.message = "Authentication failed"
render map as JSON
}
}
Now in a controller you can do something like this:
// File: grails-app/controller/com/example/OneController.groovy
#Mixin(MyMixin)
class OneController {
public someAction() {
if (!user.isAuthenticated) {
// Here we're using the method from the mixin
return render401Error()
}
}
}
Just one final advice: Mixins are applied during runtime so there is a little overhead.
The simplest answer is to create a class in src with a bunch of static methods and pass everything around as parameters, see: http://grails.org/doc/2.3.8/guide/single.html#conventionOverConfiguration
...or else create a controller base class that all other controllers extend from?
That said, I wonder if you are actually looking for scoped services? See http://ldaley.com/post/436635056/scoped-services-proxies-in-grails.

Grails 2.3.3 namespaced controller render view

For Grails 2.3.3, it allows same name controller in different package with namespaced controller according to http://grails.org/doc/latest/guide/theWebLayer.html#namespacedControllers
So we have package like this:
/admin/FooController
/public/FooController
To keep consistent we want the view package like this:
/views/admin/foo/...
/views/public/foo/...
However, in the FooController action, if you don't hard code the render method. It will find the view in the
/views/foo/index....
It seems it can't take advantage of namespace. We have to hard code.
Anyone has good idea for that?
You can certainly do this. Take a look at this post by Sérgio Michels shows how to render views from different directories using afterInterceptor. The idea is to substitute the default view before its getting rendered.
So your controller will be something like this:
package test.xpublic
class FooController {
static namespace = 'public'
def afterInterceptor = { model, modelAndView ->
if (modelAndView.viewName.contains('index')) {
modelAndView.viewName = "/public/index"
}
}
def index() { }
}
You can be creative and make it smart to pick the right view, since afterInterceptor will be called for each action.
This will help you to render the view from your directory (views/admin or views/public). However, you also need to take care of the UrlMappings
class UrlMappings {
static mappings = {
"/foo/admin" {
controller="foo"
namespace = 'admin'
}
"/foo/public" {
controller="foo"
namespace = 'public'
}
...
}
and finally on the links you need to pass the namespace.
<g:link controller='foo' namespace="admin" >Admin</g:link>
<g:link controller='foo' namespace="xpublic">Public</g:link>
A sample application is provided here
At least with Grails 3.2.0 this is working out of the box. See http://docs.grails.org/latest/guide/theWebLayer.html#_selecting_views_for_namespaced_controllers for more details

Grails: How can I change default view location?

I've got controller AdminTagController. By default view will be located in /adminTag folder. Is it possible to change default folder for this controller to /admin/view? I can specify view for each method but it's not cool
Thank you
It's possible to change it with the afterInterceptor of your controller. Check the example:
def afterInterceptor = { model, modelAndView ->
println "Current view is ${modelAndView.viewName}"
if (model.someVar) {
modelAndView.viewName = "/mycontroller/someotherview"
}
println "View is now ${modelAndView.viewName}"
}
This is applied to all actions of your controller.
In your case, I would do the heavy work in the controller, as only one class is impacted.
However, here is another way, using a custom GroovyPageResourceLoader.
That approach is typically used when your views are in a folder structure that doesn't follow Grails conventions. In your case, that would be overkill in my opinion.
However, here's the general idea:
1. Create a class that extends the default groovyPageResourceLoader.
Below is a very raw example.
class AdminGroovyPageResourceLoader extends GroovyPageResourceLoader {
#Override Resource getResource(java.lang.String location) {
if (location.contains ("/admin")) {
return new FileSystemResource("PATH_TO_GSP_LOCATION_WITHOUT_FILE_EXTENSION")
}
return super.getResource(location)
}
}
2. Override the default groovyPageResourceLoader bean
In resources.groovy or your plugin descriptor, override the groovyPageResourceLoader bean with your custom class.
A shorter path, might be some metaclass kung fu, if you don't want to override the default Spring Bean.

Override Grails scaffolding URLs entirely or with prefix

I have a controller that has static scaffold = true defined, as well as some custom actions.
I would like to ensure that only logged-in users and those of type ADMIN (some Enum value in our domain) can access it. To achieve this I've implemented a grails filter that is mapped to the /admin/** URI space, but of course the URIs for the Domain/Controller in question when scaffolding are not under there. I have added custom, named, URL mappings for the show/edit/create actions (which work and delegate straight to the scaffold layer), but I end up with two URI contexts dedicated to this purpose.
I would prefer being able to say to the scaffolded controller "Use this URI as a prefix for all your URIs" and be done with it, but searching the docs and web in general have not proven very helpful.
Any ideas?
Scaffolded controllers generate all their URLs using the standard taglib calls, which are sensitive to URL mappings. So if you have the URL mappings right then it should just work. If you have a MyDomainController that you want to be mapped under /admin then you need something like
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?" {
constraints {
controller(validator:{
// make sure this mapping doesn't apply to the MyDomainController,
// so it will only be accessible via the protected /admin URL
return it != "myDomain"
})
}
}
// special rule for the MyDomainController
"/admin/myDomain/$action?/$id?"(controller:"myDomain")
}
}
You can use any of the usual Grails constraint types on a URL mapping, so you could restrict by whitelist
controller(inList:['foo', 'bar'])
or by regular expression
// exclude all controllers whose name starts "sec", e.g. secUser, secRole, ...
controller(matches:/(?!sec).*/)

Resources