I have a JS file that is included in my GSP template as follows:
<r:script type="text/javascript">
<g:render template="/javascript/common"/>
</r:script>
The /javascript/common outputs some dynamic JS e.g. pre-populates lists client side. It's dynamic per server restart i.e. it doesn't need to be generated per request, but rather more commonly on redeployment.
I'm wondering how I can process it as a resource using the resource plugin and get the ability to minify it and compress it etc.
There may be times where it would need to be refreshed. Is it possible to support refreshing it in a similar fashion to other resources i.e. when the underlying file is modified the plugin reloads it.
thanks ...
The best way to ensure it cooperates with the resource plugin would be to create a custom ResourceMapper
Oversimplified version:
Create a file with ResourceMapper.groovy as the file suffix, in the grails-app/resourceMappers folder.
Decorate the class using def phase = MapperPhase.GENERATION
Implement def map(resource, config) {} to generate your resource when requested.
Your custom mapper will run once per deployment, then use the static generated file. All of the minify/compress, you're using will, of course, run after the GENERATION phase.
UPDATE: It does look like the Gsp Resources plugin #Ruben suggested would do what you're looking for. You can see the source for its custom mapper.
Related
Generated file of my dsl is made of all resources in the project. I made that generator is loading all resources and file is made successfully.
Speed of that operation is fine though there are moments while it completely isnt.
When Full Build or Recovery is triggered, XtextBuilder iterates all resources and runs generator for each of them. In my dsl, this causes exactly same file to be generated 300 times(thats how many resources I have).
How do I disable those types of file generation?
You should solve this differently. one option could be to register a custom org.eclipse.xtext.builder.IXtextBuilderParticipant and put your complete generation logic there. (alternatively have a look at the default impl org.eclipse.xtext.builder.BuilderParticipant)
override bindIXtextBuilderParticipant() {
MyBuilderParticipant
}
in your logic you would call the generator for the first delta only, not for all changed files
I need to access a local JSON file. Since Grails 2.4 implements the AssetPipeline plugin by default, I saved my local JSON file at:
/grails-app/assets/javascript/vendor/me/json/local.json
Now what I need is to generate a URL to this JSON file, to be used as a function parameter on my JavaScript's $.getJSON() . I've tried using:
var URL.local = ""${ raw(asset.assetPath(src: "local.json")) }";
but it generates an invalid link:
console.log(URL.local);
// prints /project/assets/local.json
// instead of /project/assets/vendor/me/json/local.json
I also encountered the same scenario with images that are handled by AssetPipeline1.9.9— that are supposed to be inserted dynamically on the page. How can I generate the URL pointing this resource? I know, I can always provide a static String for the URL, but it seems there would be a more proper solution.
EDIT
I was asked if I could move the local JSON file directly under the assets/javascript root directory instead of placing it under a subdirectory to for an easier solution. I prefer not to, for organization purposes.
Have you tried asset.assetPath(src: "/me/json/local.json")
The assets plugin looks in all of the immediate children of assets/. Your local.json file would need to be placed in /project/assets/foo/ for your current code to pick it up.
Check out the relevant documentation here which contains an example.
The first level deep within the assets folder is simply used for organization purposes and can contain folders of any name you wish. File types also don't need to be in any specific folder. These folders are omitted from the URL mappings and relative path calculations.
How to access ApplicationTagLib in a script meant for the grails command line run-script?
In case someone wonders what is the use case
This problem came up when I was trying to create a script for generating a json file (containing links to controllers and actions) for use in javascript files. More info in https://stackoverflow.com/questions/30763227/how-to-use-urls-rendered-as-javascript-variables-during-jasmine-unit-tests
I was considering using g.createLink in this script file but couldn't find how to use tag library in script file.
You can use below code to access your taglib
def grailsApplication
def g = grailsApplication.mainContext.getBean('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
I'm trying to create a link to an image resource in my bootsrap file using the underdocumented grailsLinkGenerator class in Grails 2.0.3
I have successfully injected the service using
...
LinkGenerator grailsLinkGenerator
...
But the url generated by the following code is not correct
grailsLinkGenerator.resource(dir:'images', file:'1.jpg')
There are no errorsm and the path generated almost looks correct, except it is missing 'static' in the url. Am I using this wrong? Is there an alternative way to access your static resources in bootstrap?
Looking at the ApplicationTagLib source, you can see that g:resource uses linkGenerator only when the resources plugin is not installed.
Closure resource = { attrs ->
if (!attrs.pluginContextPath && pageScope.pluginContextPath) {
attrs.pluginContextPath = pageScope.pluginContextPath
}
// Use resources plugin if present, but only if file is specified - resources require files
// But users often need to link to a folder just using dir
return ((hasResourceProcessor && attrs.file) ? r.resource(attrs) : linkGenerator.resource(attrs))
}
So this is probably the reason that your links don't have the static path.
I don't think that's a good option to store the full link to the thumbnail in the database. If you change something in the structure you will need to update all links. You can store the name of the thumbnail (or use a convention) and let the link be generated in a TagLib that will be responsible to show them.
In the TagLib you can just use g:resource that will create the correct url's.
In my Grails app, I have a dir web-app/images/carousel/slides that contains files such as:
foo.png
foo.thumbnail.png
bar.png
bar.thumbnail.png
My app is using the resources Grails plugin, and the main images are loaded in a GSP using one of it's tags:
<r:img file="carousel/slides/foo.png"/>
which generates:
<img src="/myapp/static/images/carousel/slides/foo.png">
I attempt to load the thumbnail images from JavaScript by constucting a path such as /myapp/static/images/carousel/slides/foo.thumbnail.png. But when I attempt to display one of these images, I get a 404.
Similarly, if enter the following path in the browser's address bar
http://localhost:8080/myapp/static/images/carousel/slides/foo.png
the image displays correctly, but if I enter
http://localhost:8080/myapp/static/images/carousel/slides/foo.thumbnail.png
I get a 404. Why are my thumbnail images not available at the same path at runtime, given that they're in the same source directory? I suspect the answer has something to do with the fact that the main images are loaded using the resources framework whereas I attempt to load the thumbnails from JavaScript.
You mostly answered your own question: if you don't reference the images using resources in some way, then they don't get processed.
Your best bet is to create a resources module that contains a list of all the images. The add this resource to the page.
// grails-app/conf/CarouselResource.groovy
modules = {
carousel {
resource url:'/images/carousel/foo.jpg'
resource url:'/images/carousel/foo.thumbnail.jpg'
...
}
}
then in your GSP
<r:require module="carousel"/>
Now, the module description is a DSL, so you might be able to use some sort of file loop to automatically add all the files, but I'm not 100% sure how. You also might try something like '/images/carousel/**', but the docs don't say if that would work or not.
Also, I should mention, if you use any of the caching-based resources plugins, this won't help. You will then need to manually call r.img() and set it within your JavaScript, something like this (if it works):
<r:script>
var images = [
'${r.img(...)}'
];
</r:script>
This is because the URLs generated using, for example, cached-resources, are often hashes of the file content to allow for long-term caching. They usually are only indirectly related to the original filename.
Update based on comment below:
To load a common JS remotely, but include the images, you could do something like this. Realize, I don't know your carousel code, and you will almost certainly have to modify the carousel library to handle these changes.
<r:script>
window.carouselImages = [
{
image: '${r.external(url:'images/carousel/image1.jpg'}.encodeAsJavaScript();}',
thumbnail: '${r.external(url:'images/carousel/image1.thumbnail.jpg'}.encodeAsJavaScript();}'
},
...
];
</r:script>
<r:resource url="js/carousel.js"/>
Then in carousel.js you reference window.carouselImages to get your array of images. It also should be possible it flip the order, and use some method within carousel.js to add images, like this:
<r:script>
carousel.addImage('${r.external(url:'images/carousel/image1.jpg'}.encodeAsJavaScript();}', '${r.external(url:'images/carousel/image1.thumbnail.jpg'}.encodeAsJavaScript();}');
...
</r:script>
You can improve this by looping over the file list instead of encoding each image explicitly (and example was given in the JIRA I posted below).
Finally, if you aren't going to use any of the caching or file manipulation plugins (so the files always end up at the same URL), you could just simply loop over the files from within the controller or a service method, calling r.img() on each one. This would ensure that they are copied to the static directory. The return value from r.img() can be ignored.
You can use HTML5's data-* Attribute
<element data-*="somevalue">
Here is an example if your image is located at /grails-app/assets/images:
In page (i.e. index.gsp) you have to add the following:
And from the javascript all you have to do is:
<script>
var calImg = document.getElementById('calendar-icon').getAttribute('data-calendaricon');
</script>
Pretty straightforward, moreover it is a clean approach :)