212

I have problem with caching partials in AngularJS.

In my HTML page I have:

<body>
 <div ng-view></div>
<body>

where my partials are loaded.

When I change HTML code in my partial, browser still load old data.

Is there any workaround?

Chris Martin
  • 28,558
  • 6
  • 66
  • 126
Mennion
  • 2,823
  • 3
  • 18
  • 34
  • 4
    Just a quick note: I had a problem with this that was more related to the cache control headers my Flask application was sending back. I got around the issue by adding `app.config.update(SEND_FILE_MAX_AGE_DEFAULT=0)` to my `flask_app.py`. (I imagine similar things exist for other web servers). – gatoatigrado Aug 13 '13 at 03:38
  • 5
    If you're using chrome just do a `Ctrl+Shift+R` (i.e. Hard Reload) and no matter what caching mechanism is used chrome will ignore it and re-fetch all the scripts, stylesheets etc. – snajahi Sep 18 '14 at 15:15
  • 4
    ctrl+shift+R doesn't work for me in Chrome, but on the developer tools "network" tab, clicking on "disable cache" works perfectly. For me, this is a client side problem which shouldn't be solved using hacks on the server like many of the suggestions below; it should be fixed on the client where the "problem" exists. If you fix it on the server, and forget to un-fix it, production could be adversely affected. – Ant Kutschera Apr 07 '15 at 19:53
  • 8
    ctrl+shift+R bypasses cache for normal requests. ajax requests made from angular for `ng-include`|`ng-view`|`templateUrl` are not handled by this shortcut – André Werlang May 14 '15 at 22:36
  • 2
    You can't ask all end users to Ctrl+Shift+R when visiting the site, so what's the answer to this question for the non development case? "For me, this is a client side problem which shouldn't be solved using hacks on the server like many of the suggestions below" - I don't agree, you can't control clients in a web environment so the fix for production must be application driven. For that reason I accepted: $rootScope.$on('$viewContentLoaded', function() { $templateCache.removeAll(); }); – Robert Christian Sep 30 '15 at 23:19

13 Answers13

201

For Development you can also deactivate the browser cache - In Chrome Dev Tools on the bottom right click on the gear and tick the option

Disable cache (while DevTools is open)

Update: In Firefox there is the same option in Debugger -> Settings -> Advanced Section (checked for Version 33)

Update 2: Although this option appears in Firefox some report it doesn't work. I suggest using firebug and following hadaytullah answer.

LukeSolar
  • 3,567
  • 3
  • 23
  • 36
  • 7
    This ought to be the accepted answer because it doesn't require a code change, and is more to the point of the OP's request. Of course you would want a production app to cache requests, so doing what the above folks suggested, while well meaning, could prove problematic if the code is left in a production app. – Aaron Wagner Apr 07 '14 at 13:43
  • 1
    Any suggestions for the other browsers like Firefox? – Lereveme Oct 24 '14 at 18:17
  • In Firefox: Debugger > Settings (the gear) there is the same option. – LukeSolar Oct 28 '14 at 19:53
  • 5
    This does not work in Firefox. Even when cache is disabled, and the toolbox is open, the templates are still cached. – Patrick J Collins Nov 21 '14 at 11:16
  • 4
    Does caching affect production too? What if I push new web files to the server, what prevents subsequent requests from production clients from loading a pre-published cached versions? – Northstrider Jun 30 '15 at 22:39
  • `ctrl + shift + r` for chrome – CENT1PEDE Jul 16 '15 at 07:05
  • @PatrickJCollins Had the same problem in firefox. I solved by going to Menu: History > Clear Recent History and select 1 hour and only check "Cache". Not a nice solution of course. – AceRymond Sep 03 '15 at 13:11
112

Building on @Valentyn's answer a bit, here's one way to always automatically clear the cache whenever the ng-view content changes:

myApp.run(function($rootScope, $templateCache) {
   $rootScope.$on('$viewContentLoaded', function() {
      $templateCache.removeAll();
   });
});
Mark Rajcok
  • 348,511
  • 112
  • 482
  • 482
  • @user252690 Probably also need to make sure html template was not sent with cache headers. See [here](http://stackoverflow.com/questions/14718826/angularjs-disable-partial-caching-on-dev-machine#answer-22359241) and [here](http://stackoverflow.com/questions/14718826/angularjs-disable-partial-caching-on-dev-machine#comments-14718826) for possible fixes – Chris Foster Mar 12 '14 at 17:29
  • 30
    **A bit of a warning:** Emptying the $templateCache might actually have unintended consequences. For instance, [UI Bootstrap](http://angular-ui.github.io/bootstrap/) adds default partials directly to the $templateCache during initialization and later expects them to be there. – Strille Aug 21 '14 at 07:24
  • @Strille I was trying to use angular-ui-bootstrap Modal. The pop up was not visible. because $templateCache.removeAll(); any fix for it ? – Mukun Oct 09 '14 at 09:43
  • 4
    @Mukun: There's no easy fix the way I see it, other than not using removeAll() but instead just use remove() to delete the keys you need to clear. You would require some kind of bookkeeping to know which keys to delete. – Strille Oct 09 '14 at 11:53
  • is there any ways to clear cache only for specific ui view cache. – Gayan May 07 '15 at 05:35
  • How would this look like for Angular 2? – Ondra Žižka Mar 16 '16 at 14:55
37

As mentioned in the other answers, here and here, the cache can be cleared by using:

$templateCache.removeAll();

However as suggested by gatoatigrado in the comment, this only appears to work if the html template was served without any cache headers.

So this works for me:

In angular:

app.run(['$templateCache', function ( $templateCache ) {
    $templateCache.removeAll(); }]);

You may be adding cache headers in a variety of ways but here are a couple of solutions that work for me.

If using IIS, add this to your web.config:

<location path="scripts/app/views">
  <system.webServer>
    <staticContent>
      <clientCache cacheControlMode="DisableCache" />
    </staticContent>
  </system.webServer>
</location>

If using Nginx, you can add this to your config:

location ^~ /scripts/app/views/ {
    expires -1;   
}

Edit

I just realised that the question mentioned dev machine but hopefully this may still help somebody...

Community
  • 1
  • 1
Chris Foster
  • 1,260
  • 9
  • 12
  • 2
    yes, even though this doesn't answer original question directly this did in fact help me solve the caching issue on a live website. – Andre Apr 21 '14 at 16:32
31

If you are talking about cache that is been used for caching of templates without reloading whole page, then you can empty it by something like:

.controller('mainCtrl', function($scope, $templateCache) {
  $scope.clearCache = function() { 
    $templateCache.removeAll();
  }
});

And in markup:

<button ng-click='clearCache()'>Clear cache</button>

And press this button to clear cache.

Valentyn Shybanov
  • 19,011
  • 7
  • 63
  • 58
22

Solution For Firefox (33.1.1) using Firebug (22.0.6)

  1. Tools > Web-Tools > Firebug > Open Firebug.
  2. In the Firebug views go to the "Net" view.
  3. A drop down menu symbol will appear next to "Net" (title of the view).
  4. Select "Disable Browser Cache" from the drop down menu.
hadaytullah
  • 1,095
  • 11
  • 14
19

This snippet helped me in getting rid of template caching

app.run(function($rootScope, $templateCache) {
    $rootScope.$on('$routeChangeStart', function(event, next, current) {
        if (typeof(current) !== 'undefined'){
            $templateCache.remove(current.templateUrl);
        }
    });
});

The details of following snippet can be found on this link: http://oncodesign.io/2014/02/19/safely-prevent-template-caching-in-angularjs/

Scudelletti
  • 1,230
  • 15
  • 19
Code Prank
  • 4,049
  • 5
  • 29
  • 45
16

I'm posting this just to cover all possibilities since neither of the other solutions worked for me (they threw errors due angular-bootstrap template dependencies, among others).

While you are developing/debugging a specific template, you can ensure it always refreshes by included a timestamp in the path, like this:

       $modal.open({
          // TODO: Only while dev/debug. Remove later.
          templateUrl: 'core/admin/organizations/modal-selector/modal-selector.html?nd=' + Date.now(),
          controller : function ($scope, $modalInstance) {
            $scope.ok = function () {
              $modalInstance.close();
            };
          }
        });

Note the final ?nd=' + Date.now() in the templateUrl variable.

Diosney
  • 10,087
  • 14
  • 62
  • 108
  • 1
    Later you can set a `.value('DEBUG', true)` to enable that line or not. – Diosney Feb 26 '15 at 15:37
  • 1
    My Solution was to use the following within the initialization phase of my main module under `.run(function($rootScope) { $rootScope.DEBUG = true; ...` and then within the the directive inject the $rootScope like `.directive('filter', ['$rootScope', function($rootScope)...` and in the returned object-property: `templateUrl: '/app/components/filter/filter-template.html' + ($rootScope.DEBUG ? '?n=' + Date.now() : '')`. Maybe you could elaborate your .value('DEBUG', true) approach? Upvoted! – JackLeEmmerdeur May 07 '15 at 14:25
  • The use of `.value('DEBUG', true` is the same as you did with `$rootScope`, but without cluttering it :) You could later inject `DEBUG` into the controller and query as a normal service. – Diosney May 07 '15 at 17:35
  • Could you maybe extend the sourcecode in your answer to include the `.value(...)` thingy, if it's not too sophisticated? I suppose the concept behind is an angular best-practice unknown to me. – JackLeEmmerdeur May 07 '15 at 17:41
  • 1
    This solution is very useful when working with Ionic. It's gonna save me so much time as it makes livereload useful again. Thanks a ton! – ajuser Aug 23 '15 at 16:19
  • Thanks that saved me. In controller I have `$scope.cacheKiller = '?nd=' + Date.now();` and in template ``. – Paflow Jun 11 '16 at 08:45
  • hm, i used this solution but it leaves me wondering, why such a pain? Why can't the browser tell that template was changed? – Toolkit Aug 26 '16 at 11:10
11

As others have said, defeating caching completely for dev purposes can be done easily without changing code: use a browser setting or a plugin. Outside of dev, to defeat Angular template caching of route-based templates, remove the template URL from the cache during $routeChangeStart (or $stateChangeStart, for UI Router) as Shayan showed. However, that does NOT affect the caching of templates loaded by ng-include, because those templates are not loaded through the router.

I wanted to be able to hotfix any template, including those loaded by ng-include, in production and have users receive the hotfix in their browser quickly, without having to reload the entire page. I'm also not concerned about defeating HTTP caching for templates. The solution is to intercept every HTTP request that the app makes, ignore those that are not for my app's .html templates, then add a param to the template's URL that changes every minute. Note that the path-checking is specific to the path of your app's templates. To get a different interval, change the math for the param, or remove the % completely to get no caching.

// this defeats Angular's $templateCache on a 1-minute interval
// as a side-effect it also defeats HTTP (browser) caching
angular.module('myApp').config(function($httpProvider, ...) {
    $httpProvider.interceptors.push(function() {
        return {
            'request': function(config) {
                config.url = getTimeVersionedUrl(config.url);
                return config;
            }
        };
    });

    function getTimeVersionedUrl(url) {
        // only do for html templates of this app
        // NOTE: the path to test for is app dependent!
        if (!url || url.indexOf('a/app/') < 0 || url.indexOf('.html') < 0) return url;
        // create a URL param that changes every minute
        // and add it intelligently to the template's previous url
        var param = 'v=' + ~~(Date.now() / 60000) % 10000; // 4 unique digits every minute
        if (url.indexOf('?') > 0) {
            if (url.indexOf('v=') > 0) return url.replace(/v=[0-9](4)/, param);
            return url + '&' + param;
        }
        return url + '?' + param;
    }
bradw2k
  • 693
  • 7
  • 11
  • I am interested in your solution - do you keep this code in for good - or a for a certain period of time after you make a change? Reason - would be concerned about performance. Also, you mention side-effect defeats HTTP. You mean that is a good side-effect way correct - meaning that is what you intended in order to have 'hotfix'?Thx – jamie Jun 07 '15 at 04:08
  • 1
    I do leave this code in for good, though we dialed back the length of cache-busting to 10 minutes. So in every 10 minutes of use, a user will be reloading fresh html templates. For my biz app that is an acceptable cost to gain the ability to hot-patch templates, but obviously it would impair performance too much for some kinds of apps. ... It is unfortunate that HTTP caching is defeated as well, but I don't see a way to intelligently defeat Angular template caching without defeating etag's etc as well. IMO Angular template caching is just not configurable enough. – bradw2k Jun 08 '15 at 18:57
  • 1
    +1 I had the same idea, but I only intercept for localhost. You can see my implementation of the interceptor here: http://overengineer.net/preventing-angularjs-from-caching-your-templates-and-other-files – joshcomley Feb 06 '16 at 12:27
  • @joshcomley If you only need to defeat caching on localhost, why not use a browser plugin that defeats all caching? – bradw2k Mar 02 '16 at 21:52
  • @bradw2k this way I am in complete control of what is and isn't cached, which can be useful for development. I also test in all browsers, and not all browsers have extensions that do what I need. I can also have an indicator on the site in development telling me whether or not caching is disabled, as sometimes I only want to disable and test across browsers for a while. – joshcomley Mar 02 '16 at 23:24
8

If you are using UI router then you can use a decorator and update $templateFactory service and append a query string parameter to templateUrl, and the browser will always load the new template from the server.

function configureTemplateFactory($provide) {
    // Set a suffix outside the decorator function 
    var cacheBust = Date.now().toString();

    function templateFactoryDecorator($delegate) {
        var fromUrl = angular.bind($delegate, $delegate.fromUrl);
        $delegate.fromUrl = function (url, params) {
            if (url !== null && angular.isDefined(url) && angular.isString(url)) {
                url += (url.indexOf("?") === -1 ? "?" : "&");
                url += "v=" + cacheBust;
            }

            return fromUrl(url, params);
        };

        return $delegate;
    }

    $provide.decorator('$templateFactory', ['$delegate', templateFactoryDecorator]);
}

app.config(['$provide', configureTemplateFactory]);

I am sure you can achieve the same result by decorating the "when" method in $routeProvider.

Aman Mahajan
  • 1,293
  • 14
  • 14
  • A much better alternative is to use a plugin like gulp-angular-templatecache to register angular js templates in the $templateCache. This should be used along with gulp-rev. Everytime a template changes, a new JavaScript file with a different revision number will be created and caching will never be an issue. – Aman Mahajan Aug 07 '15 at 21:29
3

I found that the HTTP interceptor method works pretty nicely, and allows additional flexibility & control. Additionally, you can cache-bust for each production release by using a release hash as the buster variable.

Here is what the dev cachebusting method looks like using Date.

app.factory('cachebustInjector', function(conf) {   
    var cachebustInjector = {
        request: function(config) {    
            // new timestamp will be appended to each new partial .html request to prevent caching in a dev environment               
            var buster = new Date().getTime();

            if (config.url.indexOf('static/angular_templates') > -1) {
                config.url += ['?v=', buster].join('');
            }
            return config;
        }
    };
    return cachebustInjector;
});

app.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('cachebustInjector');
}]);
cpreid
  • 511
  • 3
  • 11
1

Here is another option in Chrome.

Hit F12 to open developer tools. Then Resources > Cache Storage > Refresh Caches.

enter image description here

I like this option because I don't have to disable cache as in other answers.

Jess
  • 20,424
  • 18
  • 108
  • 130
0

There is no solution to prevent browser/proxy caching since you cannot have the control on it.

The other way to force fresh content to your users it to rename the HTML file! Exactly like https://www.npmjs.com/package/grunt-filerev does for assets.

Thomas Decaux
  • 18,451
  • 2
  • 83
  • 95
-6

Refresh document every 30 seconds:

<head>
  <meta http-equiv="refresh" content="30">
</head>

w3schools HTML http-equiv Attribute

Eran Or
  • 565
  • 8
  • 14