AngularJS deshabilita el almacenamiento en caché parcial en la máquina de desarrollo

210

Tengo un problema con el almacenamiento en caché de parciales en AngularJS.

En mi página HTML tengo:

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

donde se cargan mis parciales

Cuando cambio el código HTML en mi parcial, el navegador aún carga datos antiguos.

¿Hay algún trabajo alrededor?

Mennion
fuente
44
Solo una nota rápida: tuve un problema con esto que estaba más relacionado con los encabezados de control de caché que mi aplicación Flask estaba enviando de vuelta. Resolví el problema agregando app.config.update(SEND_FILE_MAX_AGE_DEFAULT=0)a mi flask_app.py. (Me imagino que existen cosas similares para otros servidores web).
gatoatigrado
44
Si utilizas Chrome acaba de hacer una Ctrl+Shift+R(es decir, duro Recargar) y no importa lo que el almacenamiento en caché mecanismo se utiliza cromo lo ignorará y vuelva a recuperar todos los scripts, hojas de estilo, etc.
snajahi
44
ctrl + shift + R no me funciona en Chrome, pero en la pestaña "red" de herramientas de desarrollador, hacer clic en "deshabilitar caché" funciona perfectamente. Para mí, este es un problema del lado del cliente que no debe resolverse utilizando hacks en el servidor como muchas de las sugerencias a continuación; debe corregirse en el cliente donde existe el "problema". Si lo arregla en el servidor y olvida desarmarlo, la producción podría verse afectada negativamente.
Ant Kutschera
8
ctrl + shift + R omite el caché para las solicitudes normales. solicitudes ajax realizadas desde angular para ng-include| ng-viewEl | templateUrlno son manejados por este atajo
André Werlang
2
No puede pedir a todos los usuarios finales que hagan Ctrl + Shift + R cuando visiten el sitio, entonces, ¿cuál es la respuesta a esta pregunta para el caso de no desarrollo? "Para mí, este es un problema del lado del cliente que no debe resolverse utilizando hacks en el servidor como muchas de las sugerencias a continuación" - No estoy de acuerdo, no puede controlar a los clientes en un entorno web, por lo que la solución para la producción debe ser impulsado por la aplicación. Por esa razón, acepté: $ rootScope. $ On ('$ viewContentLoaded', function () {$ templateCache.removeAll ();});
Robert Christian

Respuestas:

200

Para el desarrollo, también puede desactivar la memoria caché del navegador: en Chrome Dev Tools en la parte inferior, haga clic con el botón derecho en el engranaje y marque la opción

Desactivar caché (mientras DevTools está abierto)

Actualización: en Firefox hay la misma opción en Debugger -> Configuración -> Sección Avanzada (marcada para la Versión 33)

Actualización 2: aunque esta opción aparece en Firefox, algunos informan que no funciona. Sugiero usar firebug y seguir la respuesta de hadaytullah.

LukeSolar
fuente
77
Esta debería ser la respuesta aceptada porque no requiere un cambio de código, y está más al punto de la solicitud del OP. Por supuesto, desearía que una aplicación de producción almacenara en caché las solicitudes, por lo que hacer lo que sugirieron las personas anteriores, aunque sea bien intencionado, podría resultar problemático si el código se deja en una aplicación de producción.
Aaron Wagner
¿Alguna sugerencia para los otros navegadores como Firefox?
Lereveme
En Firefox: Debugger> Configuración (el equipo) hay la misma opción.
LukeSolar
55
Esto no funciona en Firefox. Incluso cuando el caché está deshabilitado y la caja de herramientas está abierta, las plantillas aún se almacenan en caché.
Patrick J Collins el
44
¿El almacenamiento en caché también afecta la producción? ¿Qué sucede si envío nuevos archivos web al servidor? ¿Qué impide que las solicitudes posteriores de los clientes de producción carguen versiones en caché prepublicadas?
meffect
111

Basándose en la respuesta de @ Valentyn un poco, aquí hay una forma de borrar siempre automáticamente el caché cada vez que cambie el contenido de ng-view:

myApp.run(function($rootScope, $templateCache) {
   $rootScope.$on('$viewContentLoaded', function() {
      $templateCache.removeAll();
   });
});
Mark Rajcok
fuente
@ user252690 Probablemente también deba asegurarse de que la plantilla html no se haya enviado con los encabezados de caché. Ver aquí y aquí para posibles soluciones
Chris Foster
30
Una pequeña advertencia: vaciar $ templateCache podría tener consecuencias no deseadas. Por ejemplo, UI Bootstrap agrega parciales predeterminados directamente a $ templateCache durante la inicialización y luego espera que estén allí.
Strille
@Strille Estaba tratando de usar el modal angular-ui-bootstrap. La ventana emergente no era visible. porque $ templateCache.removeAll (); alguna solución para ello?
Mukun
44
@Mukun: No hay una solución fácil tal como lo veo, aparte de no usar removeAll (), sino simplemente usar remove () para eliminar las claves que necesita borrar. Necesitaría algún tipo de contabilidad para saber qué teclas eliminar.
Strille
¿Hay alguna forma de borrar la memoria caché solo para la memoria caché de vista ui específica?
Gayan
37

Como se menciona en las otras respuestas, aquí y aquí , el caché se puede borrar mediante el uso de:

$templateCache.removeAll();

Sin embargo, como lo sugiere gatoatigrado en el comentario , esto solo parece funcionar si la plantilla html se sirvió sin ningún encabezado de caché.

Entonces esto funciona para mí:

En angular:

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

Puede agregar encabezados de caché de varias maneras, pero aquí hay un par de soluciones que funcionan para mí.

Si usa IIS, agregue esto a su web.config:

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

Si usa Nginx, puede agregar esto a su configuración:

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

Editar

Me acabo de dar cuenta de que la pregunta menciona la devmáquina, pero espero que esto pueda ayudar a alguien ...

Chris Foster
fuente
2
Sí, aunque esto no responde directamente a la pregunta original, de hecho me ayudó a resolver el problema de almacenamiento en caché en un sitio web en vivo.
Andre
31

Si está hablando de caché que se ha utilizado para el almacenamiento en caché de plantillas sin volver a cargar toda la página, puede vaciarlo de la siguiente manera:

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

Y en marcado:

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

Y presione este botón para borrar el caché.

Valentyn Shybanov
fuente
22

Solución para Firefox (33.1.1) usando Firebug (22.0.6)

  1. Herramientas> Herramientas web> Firebug> Abrir Firebug.
  2. En las vistas de Firebug, vaya a la vista "Red".
  3. Aparecerá un símbolo de menú desplegable junto a "Red" (título de la vista).
  4. Seleccione "Desactivar caché del navegador" en el menú desplegable.
hadaytullah
fuente
Intenté en Firebug (2.0.11) y Firefox (38.0.1) en mac, pero esto no funcionó.
paullb
19

Este fragmento me ayudó a deshacerme del almacenamiento en caché de plantillas

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

Los detalles del siguiente fragmento se pueden encontrar en este enlace: http://oncodesign.io/2014/02/19/safely-prevent-template-caching-in-angularjs/

Broma de código
fuente
nitpick, pero ¿cuándo la corriente nunca es un objeto? Tampoco estoy seguro de que nunca esté allí, peroif (!current) { return; }
Nate-Wilkins
Esto derrota el almacenamiento en caché de Angular de plantillas basadas en rutas, pero no parciales ng-include'd.
bradw2k
16

Estoy publicando esto solo para cubrir todas las posibilidades ya que ninguna de las otras soluciones funcionó para mí (arrojaron errores debido a dependencias de plantilla angular-bootstrap, entre otras).

Mientras desarrolla / depura una plantilla específica, puede asegurarse de que siempre se actualice al incluir una marca de tiempo en la ruta, como esta:

       $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();
            };
          }
        });

Tenga ?nd=' + Date.now()en cuenta el final en la templateUrlvariable.

diosney
fuente
1
Más tarde, puede configurar a .value('DEBUG', true)para habilitar esa línea o no.
diosney
1
Mi solución fue usar la siguiente dentro de la fase de inicialización de mi módulo principal bajo .run(function($rootScope) { $rootScope.DEBUG = true; ...y luego dentro de la directiva de la inyección del $ rootScope como .directive('filter', ['$rootScope', function($rootScope)...y en el objeto de la propiedad devuelta: templateUrl: '/app/components/filter/filter-template.html' + ($rootScope.DEBUG ? '?n=' + Date.now() : ''). ¿Tal vez podría elaborar su enfoque .value ('DEBUG', verdadero)? ¡Votado!
JackLeEmmerdeur
El uso de .value('DEBUG', truees el mismo que el que usó $rootScope, pero sin abarrotarlo :) Posteriormente podría inyectarse DEBUGen el controlador y consultar como un servicio normal.
diosney
¿Podrías extender el código fuente en tu respuesta para incluir la .value(...)cosita, si no es demasiado sofisticada? Supongo que el concepto detrás es una mejor práctica angular desconocida para mí.
JackLeEmmerdeur
1
Esta solución es muy útil cuando se trabaja con Ionic. Me ahorrará mucho tiempo ya que hace que la recarga en vivo sea útil nuevamente. ¡Gracias una tonelada!
ajuser
11

Como otros han dicho, derrotar el almacenamiento en caché por completo para fines de desarrollo se puede hacer fácilmente sin cambiar el código: use una configuración de navegador o un complemento. Fuera del desarrollador, para derrotar el almacenamiento en caché de plantillas angulares de plantillas basadas en rutas, elimine la URL de la plantilla del caché durante $ routeChangeStart (o $ stateChangeStart, para el enrutador UI) como lo mostró Shayan. Sin embargo, eso NO afecta el almacenamiento en caché de las plantillas cargadas por ng-include, porque esas plantillas no se cargan a través del enrutador.

Quería poder reparar cualquier plantilla, incluidas las cargadas por ng-include, en producción y hacer que los usuarios reciban la revisión en su navegador rápidamente, sin tener que volver a cargar toda la página. Tampoco me preocupa derrotar el almacenamiento en caché HTTP para plantillas. La solución es interceptar cada solicitud HTTP que realiza la aplicación, ignorar aquellas que no son para las plantillas .html de mi aplicación, luego agregar un parámetro a la URL de la plantilla que cambia cada minuto. Tenga en cuenta que la comprobación de ruta es específica de la ruta de las plantillas de su aplicación. Para obtener un intervalo diferente, cambie las matemáticas para el parámetro o elimine el% por completo para no almacenar en caché.

// 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
fuente
Estoy interesado en su solución (¿mantiene este código para siempre?) O durante un cierto período de tiempo después de realizar un cambio? Motivo: estaría preocupado por el rendimiento. Además, mencionas las derrotas de efectos secundarios HTTP. ¿Quieres decir que es un buen efecto secundario correcto, lo que significa que es lo que pretendías para tener 'hotfix'? Thx
jamie
1
Dejo este código para siempre, aunque redujimos la duración del almacenamiento en caché a 10 minutos. Por lo tanto, cada 10 minutos de uso, un usuario volverá a cargar plantillas html nuevas. Para mi aplicación biz, es un costo aceptable obtener la capacidad de parchear plantillas en caliente, pero obviamente perjudicaría demasiado el rendimiento para algunos tipos de aplicaciones. ... Es desafortunado que el almacenamiento en caché HTTP también sea derrotado, pero no veo una manera inteligente de derrotar el almacenamiento en caché de plantillas angulares sin derrotar a etag, etc. El almacenamiento en caché de la plantilla angular de IMO simplemente no es lo suficientemente configurable.
bradw2k
1
+1 Tuve la misma idea, pero solo intercepto localhost. Usted puede ver mi aplicación del interceptor aquí: overengineer.net/...
joshcomley
@joshcomley Si solo necesita derrotar el almacenamiento en caché en localhost, ¿por qué no usar un complemento de navegador que derrote todo el almacenamiento en caché?
bradw2k
@ bradw2k de esta manera tengo el control total de lo que está y no está en caché, lo que puede ser útil para el desarrollo. También pruebo en todos los navegadores, y no todos los navegadores tienen extensiones que hacen lo que necesito. También puedo tener un indicador en el sitio en desarrollo que me dice si el almacenamiento en caché está deshabilitado o no, ya que a veces solo quiero deshabilitar y probar en todos los navegadores durante un tiempo.
joshcomley
8

Si está utilizando un enrutador UI, puede usar un decorador y actualizar el servicio $ templateFactory y agregar un parámetro de cadena de consulta a templateUrl, y el navegador siempre cargará la nueva plantilla desde el servidor.

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]);

Estoy seguro de que puede lograr el mismo resultado decorando el método "when" en $ routeProvider.

Aman Mahajan
fuente
Una alternativa mucho mejor es usar un complemento como gulp-angular-templatecache para registrar plantillas angulares js en $ templateCache. Esto debe usarse junto con gulp-rev. Cada vez que cambia una plantilla, se creará un nuevo archivo JavaScript con un número de revisión diferente y el almacenamiento en caché nunca será un problema.
Aman Mahajan
3

Descubrí que el método de interceptor HTTP funciona bastante bien y permite una mayor flexibilidad y control. Además, puede buscar en la memoria caché para cada versión de producción utilizando un hash de versión como la variable buster.

Así es como se ve el método de desarrollo de caché de desarrollo 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
fuente
1

Aquí hay otra opción en Chrome.

Presiona F12para abrir las herramientas de desarrollador. Luego, Recursos > Almacenamiento en caché > Actualizar cachés .

ingrese la descripción de la imagen aquí

Me gusta esta opción porque no tengo que deshabilitar el caché como en otras respuestas.

Encadenar
fuente
En Chrome v. 54.0.2840.99 en noviembre de 2016, encontré esto en una pestaña llamada Aplicación en lugar de Recursos.
StackOverflowUser
0

No hay una solución para evitar el almacenamiento en caché del navegador / proxy ya que no puede tener el control sobre él.

¡La otra forma de forzar contenido nuevo a sus usuarios es cambiar el nombre del archivo HTML! Exactamente como https://www.npmjs.com/package/grunt-filerev hace para los activos.

Thomas Decaux
fuente