I've switched from using the Resources plugin to the new Asset Pipeline plugin. However, I've come across an issue that I'm not sure how to fix.
I use several templates (ie: _template.gsp) that are included via the g:render tag from other GSP files.
_template.gsp:
<%# page contentType="text/html;charset=UTF-8" %>
<asset:stylesheet src="_template.css"/>
<asset:javascript src="_template.js"/>
<div>
...
</div>
other GSP files:
...
<g:render template="/template"/>
...
In my _template.gsp file I include several assets that are required for the code in the template to work and/or look right. When I used the resources plugin to accomplish this, things worked as expected. Any files included in templates were moved to the HEAD section of the resulting GSP file. However, with the Asset Pipeline plugin, they stay in the same location where the template was included in the calling GSP file. And to make things worse, they aren't processed correctly, so they aren't loaded correctly in the resulting HTML file.
For example, in debug the resulting HTML file looks like this
...
<link rel="stylesheet" href="/assets/_template.css?compile=false"/>
<script src="/assets/_template.js?compile=false" type="text/javascript"></script>
<div>
...
</div>
...
and everything works (although the file ideally should be loaded in the HEAD section like it used to when using the Resources plugin).
In production the resulting HTML file looks like:
...
<link rel="stylesheet" href="/assets/_template.css"/>
<script src="/assets/_template.js" type="text/javascript"></script>
<div>
...
</div>
...
however, in production all other included assets (files included in the actual GSP file) have longer filenames that look like styles-6a85c6fa983d13b6f58e12b475e9d35c.css. The _template.css and _template.js files from the template isn't being converted to one of these long filenames and if I try to access the /assets/styles.css path I simply get a blank page.
I was able to solve the first part of my problem (the assets not being in the HEAD) by creating the following tag library:
class TemplateAssetsTagLib
{
// Define the namespace and encoding
static namespace = 'tasset'
static defaultEncodeAs = 'raw'
// Tag called to move the content of this tag to where the assets tag is located (usually the HTML HEAD section)
def head = { attrs, body ->
// Get any existing asset blocks
def assetBlocks = request.getAttribute('templateAssetBlocks')
if(!assetBlocks)
assetBlocks = []
// Add the body of this tag to the asset blocks list
assetBlocks << body()
request.setAttribute('templateAssetBlocks', assetBlocks)
}
// Tag called to load any content that was saved using the head tag
def assets = { attrs ->
// Get all existing asset blocks
def assetBlocks = request.getAttribute('templateAssetBlocks')
if(!assetBlocks)
return
// Output the asset blocks
assetBlocks.each { assetBlock ->
out << assetBlock
}
}
}
It's mirrored after the Asset Pipeline deferred script functionality, but more generic.
And I've been able to solve the second problem by simply renaming the assets and removing the leading underscore. For some reason assets with a leading underscore don't get compiled during WAR creation and therefore aren't accessible in production mode.
Related
I don't really understand how the wrapper works. I understood the example of the catalyst tutorial but I don't know how to apply specific CSS file for a specific template.
Should I use [% IF %] statement in my wrapper.tt in order to select a specific template ? Do I call the CSS file with stash, like I do for a template in the controller ?
Some examples or hints would be great, thanks
You can certainly assign the CSS file to a stash variable in your controller.
sub frobnicate :Local {
my ($self, $c) = #_;
# ...
# this would probably be implied, i.e. in a properly configured Catalyst
# you don't have to actually set this, it will just load the right thing
$c->stash->{template} = 'frobnicate';
# this one is for loading the right CSS
$c->stash->{css_file} = 'frobnication.css';
}
And then in your TT wrapper, possibly wrapped in [% IF css_file %]...[% END %]:
<head>
<link rel="stylesheet" href="[% css_file %]">
</head>
That would work, but it is not good practice, because you are breaking the separation of concerns. The way the page looks should have nothing to do with your application controller.
You could also just load each CSS file whenever it's needed, but that is bad practice too, because it will impact page load times and the order in which things are loaded. Typically one puts CSS at the top in the <head>, and most javascript files at the end of the page, just before the </body> so that there is already content rendered before the browser goes off and fetches and runs javascript.
A more flexible, but also more complex solution is to write a method in your View that can be exposed as a TT vmethod to the Template Toolkit, and use it to add CSS files to the stash when needed. You can do that with expose_methods.
package MyApp::View::TT; # or whatever you have called this
# ...
expose_methods => [qw/add_css_file/],
# ...
sub add_css_file {
my ( $c, $self, $css_file ) = #_;
push #{ $c->stash->{_css_files} }, $css_file;
return;
}
Now you can use that in your template files. You can have a block at the very top or very bottom of each file to add CSS files to the list of files that should be loaded right where they belong to logically.
<h1>Order Confirmation</h1>
[% add_css_file('confirmation.css') %]
In your wrapper, you can iterate that list of files and load each of them. As you can see this approach comes with the benefit of allowing you to have more than one file.
<head>
[% FOREACH file IN _css_files %]
<link rel="stylesheet" href="[% file %]">
[% END %]
</head>
They'll be available in the stash because the wrapper gets rendered after the inner part of the page, but you can't do it from the template directly, because you cannot change the stash within Template Toolkit. If there are no files, this will not do anything because the loop has nothing to iterate over.
Note how I called the stash key _css_file. The underscore _ indicates that it's meant to be a private variable. Perl has no concept of private or public, but that's a convention to tell other developers not to mess with this.
It would be advisable to now add a second method to the View to read the list and return it. That would decouple the implementation detail of how the list of files is stored completely from the template. You could change it entirely without having to touch the template files at all.
If you have that exposed method, it would be smart to for example make sure that each file is only included once, e.g. with List::Util::uniq, or by using a hash and accessing the keys instead of going for an array.
I originally used this approach for javascript files rather than CSS. For CSS in your application I actually believe it would be smarter to condense all the styles into one file, and minify it. Your users will download most of them anyway, so why have the overhead of loading multiple files and making each initial page load a little bit slower, and blowing up their cache, if you can just have the very first page load be a tiny bit longer and then read everything from cache?
I'm building a Rails app, but I'm using a plugin in which I have to render my images using only html.
Since I haven't deployed yet, all my images are in RAILS_ROOT/app/assets/images/, so to render an image I have to write the following code:
<img src="/assets/image.jpg">
But when I'm ready to deploy to the web and I perform a precompile, all my images are supposedly going to be moved to my public folder. Will the html still work to link to the image, or will I have to change to link to a different path?
The plugin I'm using is Typeahead:
application.html.erb*
<script type="text/javascript">
//....
$('#typeahead').typeahead(null, {
maxLength: 5,
displayKey: function(thing) {
return "<div class='typeahead'><img src='" + thing.image_url + "'></div>";
},
source: bloodhound.ttAdapter(),
});
</script>
things_controller.rb
def typeahead
#render json: Thing.where(name: params[:query])
q = params[:query]
render json: Thing.where('name LIKE ?', "%#{q}%")
end
*Thing.image_tag is currently set to "/assets/[image.jpg]", except for each thing it's adjusted with the proper file name.
Not only are they going to be in the public folder, but they'll be renamed to include the fingerprint.
You must use the Rails helpers for all assets, see how to here and read the rest of the guide while you're at it :)
I think you should use non-stupid-digest-assets gem as it copies all your assets(mentioned in assets precompile list) in public/assets folder and then you need not to change your code before/after compiling.To install, you just need to add it into your Gemfile.
gem 'non-stupid-digest-assets'
I hope it might help you.
Joe, my suggestion would be to create a directory in your public folder to house your images, instead us using the app/assets directory. The public folder will allow the assets to not be altered by the rails pipeline, and you can link to them reliably using any external services that need the images.
As stated in RailsGuides:
Assets can still be placed in the public hierarchy. Any assets under
public will be served as static files by the application or web server
when config.serve_static_files is set to true. You should use
app/assets for files that must undergo some pre-processing before they
are served.
So you would need to add this line in config/application.rb
config.serve_static_files = true
As described in Rails general configuration.
It looks like you're storing your image_url in your model, and that's not working because assets don't have fixed URLs in Rails. I would override the getter in your model to use the asset_path helper, so it translates the path when that attribute is read (e.g., when the JSON is generated).
Something like:
# thing.rb
[...]
def image_url
ActionController::Base.helpers.asset_path(read_attribute(:image_url))
end
[...]
Short answer, no.
But it isn't that big a deal to remedy. Just move the images you need to reference with html into your Public folder. Then you can simply reference them with this code:
<img src="/image_name.image_type">
and the html will link to the correct path, both before and after precompile. So you don't have to change any code before you deploy.
BTW: I assume image_tag and image_url are the same column and you just made a mistake in one of the two times you mentioned it. If that's the case, then don't forget to change it to simply "/[image.jpg]".
I want to generate a pdf (an invoice as letter) out of a twig template. The template uses a css and contains a header with a logo (png-image) and a footer, which should appear at the bottom of the document.
I tried it with the KnpSnappyBundle, but this doesn't work (css only works inline, images are not rendered..., etc.). Are there any other tools to generate a pdf?
With Java I used jasper-reports (really cool), isn't there anything similar for php?
I have used KnpSnappyBundle to generate pdf before, and it worked with external css files, thought there's is some diffrence bettween regular tempaltes:
When linking asset you have to provide absolute path:
<link type="text/css" rel="stylesheet" href="{{ asset('css/css.css', null, true) }}" />
I didn't needed images files, but I think it should work the same, also you need to use "renderView" method instead of "render".
$pdf = $this->renderView('**:**:tempalte.html.twig', array());
After that you just simple use:
$file = $this->container->get('knp_snappy.pdf')->getOutputFromHtml(pdf);
The answer is: The server startet via
php app/console server:run
is single-threaded, so there is no chance, to get a response, when requesting an image or css-file...
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
script(src="/javascripts/jquery-2.1.1.js")
script(src="/javascripts/global.js")
body
block content
Apparently, src="/../public/javascripts/jquery-2.1.1.js" doesn't work, but src="javascripts/jquery-2.1.1.js" works
The file structure is like so:
nodetest
public
javascripts
jquery-2.1.1.js
views
index.jade
Unless Jade actually creates index.html within the public folder? Is this correct?
first jade doesn't actually save an index.html to disk, it generates it on the fly during the request response cycle
second, by default, express is set up to treat public/ as the root directory for static files, so your reference to /javascripts/jquery-2.1.1.js points to public/javascripts/jquery-2.1.1.js
if you tried to load /views/index.jade or /index.jade it would 404 because Express won't find any matching static file
and finally src="/javascripts/jquery-2.1.1.js" (i.e. with a leading slash) should probably be how you reference it, because otherwise it's gonna look for child folders according to your url. (like, if you have js/jquery.js on the page my.domain/parent/child.html, the request would go to my.domain/parent/js/jquery.js
Is there a way to include a plain html page inside a Play Framework's view template? I have a scenario wherein there is a common view template and in the body of the template, I would like to include certain static html pages. I know that I can include other templates inside a certain template, but I'm not sure if I could include a plain html page?
One option is to just make your static HTML a template, eg, create myStaticPage.scala.html:
<h1>Static page</h1>
<p>This page is static, though it is still a template.</p>
Then your view template, myView.scala.html:
#(staticPage: Html)
<html>
<head>...</head>
<body>#staticPage</body>
</html>
And then in your action that renders the template:
def renderMyStaticPage = Action {
Ok(views.html.myView(views.html.myStaticPage())
}
You just need to make sure that your HTML page escapes any # symbols with ##.
On the other hand, if which HTML page that's being included is more dynamic, then simply load the HTML from the file/database/classloader/whereever it's coming from, eg:
def renderMyStaticPage = Action {
val staticPage: String = // code to load static page here
Ok(views.html.myView(Html(staticPage))
}
You could.
Just put something like that in your routes file:
GET /file controllers.Assets.at(path="/public", file="html/file.html")
Here is a duplicated post: Route to static file in Play! 2.0