32

In Angular 2, how do you clear the template cache? There are tons of answers for Angular 1, but none for 2.

The issue I am having is that when I change the contents of the html pages referenced by templateUrl on any components, the html pages don't change in the browser until I manually navigate to the templateUrl in the browser and hit reload. I know you can disable the browser cache to solve this during development, but my concern is that users can see an outdated html page if they have it cached in their browser when I go to update a website with Angular 2.

Here is a link to the stack overflow questions for Angular 1 AngularJS disable partial caching on dev machine

Below is a snippet and I am having issues with app.html updating when its content is changed.

@Component({
    selector: 'photogallery-app',
    templateUrl: './app/app.html',
    directives: [ROUTER_DIRECTIVES, CORE_DIRECTIVES]
})
Keith Neuse
  • 723
  • 1
  • 6
  • 12
  • Is this what you are looking for? https://angular.io/docs/ts/latest/api/router/CanReuse-interface.html – Langley Jan 15 '16 at 09:32
  • This might work, but I would prefer that there be a global method for this rather than having to do this for each component in my website. – Keith Neuse Jan 15 '16 at 09:45
  • For what I understand you don't have to, they'll allways get rebuilt unless you especifically say it should be reused by implementing routerCanReuse and returning true right? – Langley Jan 15 '16 at 09:47
  • I am having this issue now and thats without implementing routerCanReuse and returning true. – Keith Neuse Jan 15 '16 at 09:54
  • After further reading routerCanReuse, and some of the behavior I am observing, I can see that the component is thrown away and re-used because my ajax call is made each time I navigate to the page, but the referenced html by the templateUrl decorator property is what is not updating correctly. – Keith Neuse Jan 15 '16 at 10:05
  • I am not sure what is it that you are expecting, pages getting cached is a browser feature not Angular. A user can disable their cache if they want to slow load the page everytime. – Langley Jan 15 '16 at 10:09
  • Since I am building a SPA app, it would be nice if the cached pages for the various templates are thrown away when the user hits refresh or restarts their browser. Maybe, I can have .Net serve up an http header to invalidate the cache for static content and make use of routerCanReuse. – Keith Neuse Jan 15 '16 at 10:18
  • Have you found solution to this problem. I am writing an Angular2 app, and the templates are caching by default. – Ram V Feb 26 '16 at 15:33
  • I created a custom Url versioning system that appends ?version=1.0 to the end of each Url. I will updated my answer later today or tomorrow with more information – Keith Neuse Feb 28 '16 at 19:58
  • Did you ever get the top voted answer working? The `RuntimeCompiler` way doesn't work for me and it's not even present in the docs https://angular.io/docs/ts/latest/api/ – Juicy Sep 02 '16 at 14:02

9 Answers9

24

First import the TemplateCompiler.

import { TemplateCompiler } from 'angular2/src/compiler/template_compiler';

Next inject the TemplateCompiler in your constructor.

constructor(private _templateCompiler: TemplateCompiler)

Finally use that to clear the cache. Note this clears all templates.

this._templateCompiler.clearCache();

UPDATE: Angular 2 Beta 17

First import the RuntimeCompiler.

import { RuntimeCompiler} from 'angular2/src/compiler/runtime_compiler';

Next inject the RuntimeCompiler in your constructor.

constructor(private _runtimeCompiler: RuntimeCompiler)

Finally use that to clear the cache. Note this clears all templates.

this._runtimeCompiler.clearCache();

UPDATE: Angular 2 RC 1

First import the RuntimeCompiler.

import { RuntimeCompiler} from '@angular/compiler/src/runtime_compiler';

Next inject the RuntimeCompiler in your constructor.

constructor(private _runtimeCompiler: RuntimeCompiler)

Finally use that to clear the cache. Note this clears all templates.

this._runtimeCompiler.clearCache();

UPDATE: Angular 2 RC 4

First import the RuntimeCompiler.

Notice the path change from RC1. The path listed for RC1 will throw errors when calling .ClearCache() if used with RC4

import { RuntimeCompiler} from '@angular/compiler';

Next inject the RuntimeCompiler in your constructor

constructor(private _runtimeCompiler: RuntimeCompiler)

Finally use that to clear the cache. Note this clears all templates.

this._runtimeCompiler.clearCache();

UPDATE: Angular 2.0.0 (RTM)

It cannot be done. I have an app that serves one set templates for logged in users, and another set for those not logged in. After upgrading to 2.0.0, I can see no way to accomplish the same task. While I try to figure out the best way to re-architect the application, I have resorted to this instead:

location.reload();

That works (but obviously reloads the entire page).

James
  • 2,823
  • 22
  • 17
  • 2
    great solution. I simply make this call on login so users are always using the latest for there session. – Honorable Chow Apr 19 '16 at 17:57
  • 1
    I am also using it for login / logout, so clearing all templates is fine. – James Apr 28 '16 at 22:50
  • I read this on github "@angular/compiler/src/runtime_compiler is a private / not-exported path and as such should never be used (in other words you should nave see /src/ in your import paths for Angular2 public APIs);" – Northstrider Jul 02 '16 at 08:09
  • 1
    Can you post a more complete sample please. – yodalr Aug 16 '16 at 12:05
  • Not use `location.reload();`. It is a bad idea, observe my solution in [this answer](http://stackoverflow.com/a/40156489/2290538) – Fernando Leal Oct 20 '16 at 13:57
  • I see '@angular/compiler/src/runtime_compiler' when I search RuntimeCompiler but when I try to import, it says 'Cannot find module'. Anyone has this problem? I haven't had this problem before. – JP. K. Apr 24 '17 at 15:52
14

In Angular 2.0.2 we can clear the template cache as follows:

Import:

import { Compiler } from '@angular/core';

Dependency Injector:

constructor(private _compiler: Compiler) {
}

Usage:

this._compiler.clearCache();

Note: I believe that this solution works from Angular 2.0.0 (RTM)

Update

In current versions (4.x) of Angular clearCache() for is no longer working. This has already been reported in the Angular Github (by @Olezt) and possibly will be corrected in the future.

Explanation of functional purpose of clearcache() method:

Another question that is unclear is the functional purpose of the clearCache() method, clearCache() has the responsibility of removing the templateUrl, stylesUrl, etc. specified in @Component. (I use it to discard the view loaded in templateUrl and request a updated view of the server, in my project the view resulting from this request is changed according to exchange user/permissions for example. So the need to discard the one previously loaded.)

clearCache() has no action on the browser cache, or anything beyond the Angular, it only acts on the @Component, @NgModule, etc. Angular caches, nothing more.

Fernando Leal
  • 9,875
  • 4
  • 24
  • 46
  • 1
    Will this solution require importing the Compiler into each Component individually? – Tyron Mar 16 '17 at 00:02
  • @Tyron, Yes on all the components you want to perform this routine. I do not see it as a problem, would it be a problem for you? – Fernando Leal Mar 16 '17 at 11:20
  • This is not working for me with v.4.2.3 even if clearCache seems to exists in official documentation. – Olezt Jun 30 '17 at 12:46
  • @Olezt, Can we provide a [Plunker](https://angular.io/generated/live-examples/toh-pt1/eplnkr.html) so we can analyze a possible updated solution? – Fernando Leal Jun 30 '17 at 12:59
  • @Fernando https://plnkr.co/edit/7dHyrG?p=info Using v.4.2.5 (released today). You may check console and network tabs of developer tools to see that function is called, clearCache() is undefined and the template is not reloaded. – Olezt Jun 30 '17 at 13:14
  • @Olezt, I analyzed your plunker and `.clearCache();` Not serve to refresh the page, but to remove the pages of the `templateUrl` of the components already loaded, for the next loading requirements of the server. Of an observed in the way you are using. Note: in plunker it is not easy to reproduce this behavior. – Fernando Leal Jun 30 '17 at 17:11
  • @Fernando You are right for the plunkr, but on my project I use uirouter and `state.reload()` after `.clearCache()` but I still get the old template. Since the function seems undefined, how does it work? – Olezt Jun 30 '17 at 18:54
  • 1
    @Olezt. The return of the function being undefined does not mean that it does not work, only that it has no set return. You may notice that the method is in the [current versions of the `Compiler` object of the Angular](https://github.com/angular/angular/blob/5af143e8e43cb1c0d0560c558271f9d3b7201da9/packages/compiler/src/jit/compiler.ts#L216-L221). Try to check if you have something other than the angular that might be caching the html. Currently I have no guarantees that this is working, as this implemented in a prototype project that stopped in an old version of the Angular. – Fernando Leal Jun 30 '17 at 19:20
  • @Fernando, thanks for your comments. Can you clarify: 1. Why you believe clearCache() is unsupported and 2. Why this needs to be in each component? This is no-go since we have an order of a hundred components. Ideally I would like to clear the *bundle.js cache in one swoop by clicking on a button at a top level and thus clearing all the *.bundle.js's. – MoMo Jul 11 '17 at 20:49
  • @MoMo, 1. Well first it seems that in current versions of angular `.clearCache()` is no longer working and already has even [a issue reported in github about it, by @Olezt](https://github.com/angular/angular/issues/17880). 2. It is not necessary to import `Compiler` into all components, you need to import them and inject it only where you want to call the cache cleanup method (`.clearCache()`). – Fernando Leal Jul 12 '17 at 12:42
  • `clearCache()` has no action on the browser cache, or anything beyond the Angular, it only acts on the @Component, @NgModule, etc. Angular caches, nothing more. So to discard the "* .bundle.js" files as you have quoted I believe it will not be useful, you may have to resort to something out of Angular. – Fernando Leal Jul 12 '17 at 12:43
  • @MoMo, I added more information about this in the answer. – Fernando Leal Jul 12 '17 at 12:58
6

In Angular 5.X we can use the compiler in the same way as 2.0.

Import the compiler:

import { Compiler } from '@angular/core';

Inject the Dependency in your component constructor :

constructor(private _compiler: Compiler) { }

Clear the cache:

this._compiler.clearCache();

Abhishek Gautam
  • 1,617
  • 3
  • 18
  • 29
3

The following stack over flow question provides a great strategy for solving this problem by appending a version parameter to the Url.

Force browser to clear cache

This in theory should work great for production releases. Then for development, I can simple disable the browser cache.

Community
  • 1
  • 1
Keith Neuse
  • 723
  • 1
  • 6
  • 12
  • I can confirm this strategy will work using fiddler; however, now I need to figure out how to rewrite urls in angular JS 2.0 for template content – Keith Neuse Jan 15 '16 at 16:01
3

gulpfile.js

var gulp = require('gulp'),
    replace = require('gulp-replace');

    gulp.task('release', function() {
        // cache busting for html
        gulp.src([app/**'])
            .pipe(replace(/\.html.*'/g, '.html?v' + Date.now() + "'"))
            .pipe(gulp.dest('web/app'));

        // cache busting for js
        gulp.src(['main.html'])
            .pipe(replace(/version = (\d+)/g, 'version = ' + Date.now()))
            .pipe(gulp.dest('src/AppBundle/Resources/views'));
    });

main.html

<html>
    <head>
        <script src="{{ asset('js/systemjs.config.js') }}"></script>
        <script>
            var systemLocate = System.locate,
                version = 1477001404442;
            System.locate = function(load) {
                var System = this;

                return Promise.resolve(systemLocate.call(this, load)).then(function(address) {
                    return address + System.cacheBust;
                });
            };
            System.cacheBust = '?v=' + version;

            System.import('app').catch(function(err){ console.error(err); });
        </script>
    </head>
</html>

any-component.ts

@Component({
    selector: 'any-selector',
    templateUrl: '/app/template.html'
})

then just execute gulp release and you are good to go live

Victor Bredihin
  • 2,220
  • 21
  • 30
2

I had a similar question. As a quick hack, i've solved this by introducing a global var in a module like export var fileVersion = '?tmplv=' + Date.now(); - at development time and change it for production - and use it in components like:

@Component({
    selector: 'my-component',
    templateUrl: '/app/templates/my-component.html' + fileVersion,
})

So I have just to refresh the browser to see the changes. In production use export var fileVersion = '?tmplv=v1.3.7yourVersionNumber';.

Community
  • 1
  • 1
Kaveh Shahbazian
  • 13,088
  • 13
  • 80
  • 139
  • where i have to declare variable "fileVersion"? Currently i'm using angular cli for angular 2 app. I have defined fileVersion in environment file and import in component and try to use in temlateUrl. But it gives error "fileVerison is undefined" – RAVI PATEL Jun 22 '17 at 10:51
  • Anywhere before where it's used. And should be declared globally, as a top level variable of a package (hence the `export`). But I think this answer might be from whence Angular was 2.0 Beta and even after release, the routing and libs and perhaps many other things changed very much. So seems to me Angular (4 now?) might have a better solution. I no longer use Angular. I use VueJS, these days. – Kaveh Shahbazian Jun 22 '17 at 21:56
1

I just appended a string to the templateUrl to avoid caching. Here is my solution: https://csjdpw.atlassian.net/wiki/x/gCGWAg

G Chen
  • 199
  • 3
  • 12
0

A better solution is to bundle all of the required files and produce a single .js file, which can be invalidated very easily using a simple query string at the end of the path (?v=1.x). For more info take a look at this official document: Introduction to Webpack: https://angular.io/docs/ts/latest/guide/webpack.html

VahidN
  • 18,457
  • 8
  • 73
  • 117
-4

I think the easiest thing to do is open a new session using incognito mode because nothing gets cached so it will always force a reload.

james_s_tayler
  • 1,823
  • 1
  • 15
  • 20
  • 1
    This would work if I was the only one using the website; however, there are many users that are using the site and if I introduce a bug that I needed to roll back, its going to be very difficult to ask users to use incognito mode. I bet IE does not have an incognito mode too. – Keith Neuse Jan 15 '16 at 09:57