Grails Asset Pipeline: URLs within static assets - angularjs

In Grails Asset Pipeline, I am serving HTML, js, CSS, and image files.
In an HTML asset, if I need to supply a src for an image, how should I write that URL to take into account:
that assets might be served from a different base URL, as specified by grails.assets.url
that assets might be served from the Grails server (in dev mode), but under a potentially variable app context
I can think of the following solutions:
always use relative paths between assets.
The problem with this is that if I ever move an asset, then all of its relative links must change.
Another possible issue would be that, if I somehow made Asset Pipeline route proprietary assets to my own static server, and thrid-party assets to public CDNs (e.g., https://ajax.googleapis.com/ajax/libs for angularjs, jquery, etc.), then any static relative URLs wouldn't work.
I assume that I shouldn't ever directly reference js and/or css files outside of Asset Pipeline manifests, so, unless that's wrong, then this problem shouldn't occur for those file types, but, if there's a CDN for common images (does such a thing exist?), then static relative URLs in html files wouldn't work in img src attributes.
angularjs & a javascript variable set via a gsp
Use angularjs in the html file to read a javascript variable that contains the base URL. Set the base URL in a gsp that is referenced by every page.
The problems with this are:
that a separate request for the gsp is necessary (though it should be able to be cached for a long time). Is it possible to compile a gsp at build-time into a js file, and copy the result as an asset, so that it could be included in the Asset Pipeline static bundle rather than served from my Grails server?
that browser cycles are used processing the angularjs code
that certain third-party javascript libraries don't play well with angularjs, so it might be complex to get them working with this setup
from what I know, url() calls in css would still have to be relative, since angularjs wouldn't be able to influence them. Maybe I could use one of the css wrapper languages, like less, but this option is getting much more complicated than option 1...
Are there any other viable options?
Are there any other cons and/or gotchas to either option that I've mentioned above?
I'm using the current version of Asset Pipeline (2.2.5), and the latest version of Grails 2.x (2.5.0).

Related

Using image urls vs importing images locally

Is it generally better performing to use an image by referencing it with an url or by downloading it, saving it to some directory, and then importing it in a component?
Eg
<img src="images.google.com/example.png" />
vs
import Example from './static/example.png';
<img src={Example} />
Since React doesn't have any means of loading or serving image data, this question is inherently about other tooling, like Webpack.
Storing images locally and loading them via webpack has the benefit of being able to process them with your Webpack loaders, which enables things like inlining them (via base64 resource URLs) when they're small enough and fingerprinting them. You'll still want to deploy those final assets somewhere that gets wrapped in a CDN, though.
Referencing remote images via string URL alone has the advantage of not having sizeable binary images bloating your SCM repository, not needing to import each image explicitly (remember, there are no glob imports in Webpack), and reducing your webpack build times.
I suspect most React apps are going to end up using both, usually in different use cases. I always use local images for logos and app assets for example, whereas I'll reference external files for any user-uploaded content or larger JPG/PNG assets.
CDNs are usually preferred for delivering static files such as images. Due to the fact that the Google Cloud CDN caches your files through its edge servers, your visitors will have their static content delivered to them from the closest point of presence instead of originating from the same server your application is running from.
Having your static content served and cached in multiple geographical locations around the world ensures an optimal content delivery time.
Developing locally however, you may notice that having your static content locally is faster. This is normal, because you're fetching an image over the internet vs from your local hard drive. However, once your app will be deployed and running (say from Canada), visitor A who is from Eastern Europe may be experiencing slower delivery times than visitor B who is from the States. Because you are serving a global customer base from one geographical location, your visitors from the a completely different timezone will receive an unfair treatment in the time it takes for their content to be delivered.
In another perspective, why not both? Have your application serve static content from a CDN but also have your static content on the same application folder and gracefully fall to the local one if the CDN fails.
TL;DR
Serve your images from a CDN ( url ) if you have a large customer base for optimization.

Loading an arbitrary file in the Play Framework 2 (Scala)

I'm trying to serve an AngularJS application using the Play 2 Framework for Scala and I think I understand, in general, how the routes and the templates work.
In order to serve the angularJS files (which should NOT be available for users publicly), I'm currently placing them under /public and then creating routes for them.
I would like to have a little more flexibility over where my angular js files are. I'm aware of the assets.at() method that creates an action for this purpose but I cannot serve files that live anywhere other than /public, no matter what I do. I will need to intercept the call and only serve the javascript file if the user has the correct permissions.
So I think my question is whether this is the right approach for what I have in mind (selective serving of angular JS files - depending upon permissions and so on ) and whether I'm stuck with having to place my angularJS app under /public - is it not possible to serve files from anywhere else?
You can wrap the built-in Assets controller. Instead of using the router to invoke it directly, as is the default, invoke your own Action, and use Action composition to wrap it with your authorization logic.
I'd like to see what is not working for you. You should be allowed to have assets served from multiple paths
routes.conf
GET /assets/*file controllers.Assets.at(path="/public", file)
GET /secure/*file controllers.Assets.at(path="/secure", file)
Then in your templates.
<script src="#routes.Assets.at("/public", "test.js")"></script>
<script src="#routes.Assets.at("/secure", "test.js")"></script>

Change Angularjs existing backend to Play 2

I have a fully developed Angularjs frontend app (with the routes and everything set up) and would like to change the current backend to a Play 2 Java. What is the best approach to display the existing html files from Angular in Play? I have found a few examples of how to connect the routes, but I would rater not create an index.scala.html file as I would like to have the two frameworks separated as much as possible and having Play only working as backend.
If you don't want to dynamically generate views from Play using Twirl and you just want to serve your HTML static files publishing them as assets is the way to go. By default assets are designed to provide resource like CSS or JS files but nothing prevents you from serving ordinary HTML views as well.
Simply put your index.html in the public directory and modify the conf/routes files so it can handle all requests:
GET /*file controllers.Assets.at(path="/public", file)
This way your index.html will be accessible at www.yourdomain.com/index.html. Remember to put this line as the last mapping in the file as it matches all possible URLs. Other services should be declared above it.

Deploying ASP.Net MVC, Angular application download file when there is a change

I have an ASP.net application which uses AngularJs, Javascript, HTML5.
The problem is everytime I deploy the browser may cache some files which results in errors because the user is not getting the latest Html and javascript files.
I understand HTML5 has a manifest file which can force files to download but is there a more efficient way to download the file when the file has actually changed?
I understand bundles only do this for Javascript files not HTML files?
I am not sure if it is angularjs related as a number of these HTML files are swapped out using ng-views and templates.
Any clues would be appreciated.
I am trying to make use of bundles but not sure if this applies to html files using AngularJs
Thanks
in general, to control included web resources js/css/... cache behavior, i would suggest to manage some type of version id and append it to your including element.
for example:
<script src=".../.../app.js?<version id>" />
or
<script src=".../.../app/<version id>.js" />
or even
<script src=".../<version id>/.../app.js" />
once you deployed your new application, the browser will ignore the previews cached files and use the newer files
in case are using a source control (git/svn/...), you can use the latest commit id as the version id. otherwise, manage this version id manually (incremental number).
in case are using a build server, you can add a rule to your build script that automates this procedure for you.
Updated answer
I am not sure if it is angularjs related as a number of these HTML
files are swapped out using ng-views and templates.
Bundling reduces the number of requests to the server. For AngularJS you could use a technique at build time to inline the templates with javascript. This is a plugin for a Grunt based build system.
Grunt AngularJS Template Inlining
Now you can use bundles for JS.
Other options are around browser caching and not even worry about bundling the templates.
Grunt based builds have a good cache busting mechanism. Described further down.
I would advise you to look at caching options and some important points below. The caching options are set at the web server level.
Rule of thumb
Never cache the first page (index.html) or cache with if-modified headers.
Cache every resource that is resolved from the main page.
For the referenced resources
Use Grunt based build and a plugin https://www.npmjs.org/package/grunt-cache-bust
The way this plugins works is that it will calculate the HASH of each resources referenced in the HTML and renames the file with the file.HASH making sure its references are also updated.
It is also smart enough to ignore CDN based remote URLs.
You can't do anything from the server if the client does not even send you the request.
HTML5 Cache Manifest does not work on all browsers (IE<=9 :()
you can configure web.config to disable caching in asp.net application
<system.webServer>
<staticContent>
<clientCache cacheControlMode="DisableCache" />
</staticContent>
</system.webServer>
No, html files are not bundled in ASP.NET MVC. This can be done with js or css, but html files generally need to be directly addressable via URL.
A simple way to get Angular to load in a new version of the template is to just append a unique query string parameter onto the template URL wherever you use it in Angular:
module.directive('myDirective', function() {
return {
// add querystring with deploy date, or whatever
templateUrl: '/Path/Template.html' + '?noCache=08102014',
controller: controller
};
});
Of course, a cleaner, more maintainable solution would be to compile your templates to javascript strings, which could then be bundled like any other part of your Angular app. grunt-angular-templates would be one candidate for this approach.

Handling Image Caching with CakePHP (and Asset Compress)

From Yahoo!'s Best Practices for Speeding Up Your Web Site document:
Expires headers are most often used with images, but they should be
used on all components including scripts, stylesheets, and Flash
components.
I follow the above advice using the "mod_expires" Apache module. My implementation is much like HTML5 Boilerplate's. See this .htaccess code.
Here is another quote from the same Yahoo! document:
Keep in mind, if you use a far future Expires header you have to
change the component's filename whenever the component changes. At
Yahoo! we often make this step part of the build process: a version
number is embedded in the component's filename, for example,
yahoo_2.0.6.js.
I have taken care of this with my CSS and JavaScript files using Mark Story's Asset Compress plugin. It's just a matter of making Asset Compress' shell part of the build process.
Now for the two issues I've run into, both related to images:
I have regular <img> tags throughout my websites and I also have CSS background-images. I currently don't have an elegant way of handling cache busting for either of those two types of images. For <img> tags, I have this line in my "core.php" file:
Configure::write('Asset.timestamp', 'force');
Although this does provide a way of automatically handling cache busting for <img> tags (provided the tags are generated using $this->Html->image(...)), I don't consider this to be elegant for two reasons:
It uses a query string, which is not recommended.
The image's timestamp is checked every time that particular view is accessed. Yes, you could cache the view, but you may want the image(s) in that view to be updated before the cached version of the view expires, so you would have to do whatever is needed to trigger that view to be re-cached, which I don't consider to be elegant.
As for handling the cache busting of CSS background-images, I have to manually update the LESS file. Definitely not elegant.
How is image caching supposed to be handled with CakePHP and/or Asset Compress?
Cache Invalidation, performance and the web
It's commonly held that one of the hardest things to do in programming is cache invalidation. However with assets (js files, css files, images etc.) that's not really true optimal logic for serving web assets is:
Serve with long cache expiry (1 year)
Don't use etags
If the asset changes change the url
There is however one complication when applied to the web.
Consider a request for /css/main.css, containing:
body {
background-image:url('../img/bar.gif');
}
This will, obviously, trigger a request for /img/bar.gif when the css file is loaded. Assuming the image is served with appropriate headers, there are only two ways to load an updated version of bar.gif:
change the contents of the css file
change the folder where the css file is
The first is problematic if it's not automated (and even if automated, could go wrong) the second is easy.
Version-prefix asset urls -> never have problems again
One simple way to solve the css/js/files problem is to make your build number part of the url:
/v123/css/foo.css
^
You can do this by modifying your app helper webroot function for example:
public function webroot($file) {
$file = parent::webroot($file);
if (Configure::read('debug')) {
return $file;
}
return '/' . Configure::read('App.version') . $file;
}
Incidentally it's the same technique to use a cdn - probably the best thing you can do to improve frontend performance.
In this way when you bump your site version - all assets get new urls. Note that using this technique, all referenced assets need to use relative urls, not absolute:
.foo {
/* background-image:url('/img/bar.gif'); // won't work */
background-image:url('../img/bar.gif');
}
Otherwise the request for the css file is application-version specific but the referenced image is not and would be read from browser cache (if relevant) even with a new application version.
Achieving the same result, no changes to file system
You can use a rewrite rule similar to the one in h5bp's for filename cache-busting if you don't want to change your folder structure:
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/v\d+/(css|files|js)/(.+)$ /$1/$2 [L]
</IfModule>
This will mean that the url /v123/css/main.css serves the contents of /css/main.css at the time of the request.
Parsing css files is complex
You mention in a comment
I think the fact that changing one asset causes all assets to be re-downloaded is a deal-breaker
Unless you are releasing a new production version every minute - it's not going to be a problem (unless you have GB of cached content in which case .. you have different problems). The only way to have performant cache logic that is file specific is to e.g. store each file in your site as the sha1 of the file's contents - applied to css files that means replacing ../img/foo.gif with ../img/<hash of foo.gif's contents>.gif.
There's nothing to stop using multiple techniques, for example with the following structure:
app
webroot
css
img <- css assets only
fonts
img
js
You can version-prefix your css, fonts and js requests; indirectly do the same for css-images (assuming they use relative urls of the form background-image:url('img/bar.gif');) without applying the same logic to other assets (user avatars, their uploaded cat videos, whatever).
Or use data uris for all images
It's what google does =).
At the end of the day it becomes a choice between how complex you want your build process to be, and for what real benefit. Many users have empty browser caches, so it's quite likely that for a random user the cache logic of an application will only apply to their current visit - one of the main reasons why expiring your whole asset cache in one go isn't such a bad thing.

Resources