2

I have built a largely directive-based AngularJS HTML5 application. Each directive will load it's template from another file, using the templateUrl syntax.

The application is a single-page application, let's pretend it's accessible via http://prod/index.html, and when that page is loaded a directive will then load http://prod/partials/directive-a.html


I'm having problems when pushing out new releases of the website to my production enviroment. The browser is continuing to use cached assets from the previous version.


For example, if I navigate to http://prod, all the assets are shown from the previous version. If I force-refresh the page (CTRL+F5), the index.html page will be re-loaded from the server and reflect any changes.

This does not cause the browser to refresh the partials/directive-a.html. Therefore, the new index.html page still shows the old directive-a.html.

If I navigate to the full partial HTML file (partials/directive-a.html) in the browser address bar, the old version of the partial is presented to me. By hard-refreshing the partial page the correct (new) version of the partial will be shown, and also be updated when the directive on index.html is reloaded.


What mechanisms are available to me to prevent the caching of partials?

I've seen commentary about appending a timestamp to the query arguments, but this seems overly clunky to put on all of my directives and routes.

rzelek
  • 3,807
  • 1
  • 30
  • 34
jwa
  • 2,960
  • 2
  • 19
  • 48
  • I think do disable caching by adding no-cache header, in application config.. by the way which platform software server side is built? . – Pankaj Parkar Jan 30 '16 at 13:08
  • @PankajParkar Certainly possible, the HTML servers are being vended by apache httpd. This will clearly have an implication on performance (particularly on mobile), but I might be able to live with that... – jwa Jan 30 '16 at 13:09
  • yes.. this might affect performance for mobile device. but I think this could help http://stackoverflow.com/a/11724596/2435473 – Pankaj Parkar Jan 30 '16 at 13:12

2 Answers2

2

@Duncan gave you awesome way to deal with templates, but I think it is not the only problem. Please remember about caching js, css and index.html.

Simple solution If you want simple (almost ideal) solution for cache problem, you can force server to set session cookie at first request (for index.html). Browser will cache html and js for session's length. You will still have partial cache support and no problems.

# First response header will set session id:
Set-Cookie:JSESSIONID=0E9AAA7D661A3E100C8EE9F421541B91; Expires=Sun, 29-Jan-2017 14:25:25 GMT; Path=/; HttpOnly

# Next browser request will contain session id from first response.
Cookie:JSESSIONID=0E9AAA7D661A3E100C8EE9F421541B91

Complicated solution

You should consider using custom headers for serving Angular assets. You can use mod_expires inside of apache.

ExpiresByType text/html                             "access plus 0 seconds"
ExpiresByType application/javascript                "access plus 0 seconds" 
ExpiresByType text/css                              "access plus 0 seconds"
...

The problem of this solution is losing cache mechanism. Returning users will not load your site faster and your server will experience more usage. You can run in performance problems.

You can improve this solution by building mechanism:

  1. concat your app js into application.js
  2. minify/uglify application.js
  3. change application.js to application.[unique-id].js
  4. change occurences in index.html

The same applies to css and 3rd party libraries.

After that you will be able to change

ExpiresByType text/html                             "access plus 0 seconds"
ExpiresByType application/javascript                "access plus 1 year" 
ExpiresByType text/css                              "access plus 1 year"
...

Obviously, there are automated solutions based on Grunt or Gulp. Personally, I can recommend https://github.com/yeoman/generator-angular

Both solutions are working in my current apps.

rzelek
  • 3,807
  • 1
  • 30
  • 34
  • The `ExpiresByType text/html "access plus 0 seconds"` was a huge help for us, thank you! We protect certain views on our Angular app with Shibboleth, and if the browser loads a cached version of the page, the Shibboleth directives never fire. This seemed to fix that issue. – jlewkovich Aug 10 '16 at 19:39
1

I haven't tested to see if this works (found with a quick Google search), though I think it should . It is decorating the $http service so that any requests have a cache-buster added to the url. You probably want to change the logic to only decorate urls in your 'partials/' folder.

anglar.module('myApp',['ui']).config(["$provide", function($provide) {
    return $provide.decorator("$http", ["$delegate", function($delegate) {
        var get = $delegate.get;
        $delegate.get = function(url, config) {
            // Check is to avoid breaking AngularUI ui-bootstrap-tpls.js: "template/accordion/accordion-group.html"
            if (!~url.indexOf('template/')) {
                // Append ?v=[cacheBustVersion] to url
                url += (url.indexOf("?") === -1 ? "?" : "&");
                url += "v=" + cacheBustVersion;
            }
            return get(url, config);
        };
        return $delegate;
    }]);
}]);

From https://gist.github.com/ProLoser/6181026

Alternatively compile all of the partials into a single templates.js file that populates the template cache and then you have no actual http requests for the templates at all. If you use gulp to build your application then there are various plugin modules to do this (e.g. gulp-angular-templatecache or gulp-angular-templates).

Duncan
  • 79,697
  • 10
  • 108
  • 148
  • Possible problem: `ui-bootstrap-tpls.js` is not the only one that you can break. – rzelek Jan 30 '16 at 13:58
  • 1
    @ArekŻelechowski that's why I suggest changing the code to only work on requests to the `partials/` folder. – Duncan Jan 30 '16 at 14:00
  • You're right, but you can't guarantee that 3rd party library will not use `partials/` on pushing to `$templateCache` service. Anyway, this solution is awesome because you don't loose caching mechanism. – rzelek Jan 30 '16 at 14:03