O AngularJS desabilita o cache parcial na máquina dev

210

Estou com problemas com o cache de parciais no AngularJS.

Na minha página HTML eu tenho:

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

onde minhas parciais são carregadas.

Quando altero o código HTML no meu parcial, o navegador ainda carrega dados antigos.

Existe alguma solução alternativa?

Mennion
fonte
4
Apenas uma observação rápida: tive um problema mais relacionado aos cabeçalhos de controle de cache que meu aplicativo Flask estava enviando de volta. Eu contornei o problema adicionando app.config.update(SEND_FILE_MAX_AGE_DEFAULT=0)ao meu flask_app.py. (Eu imagino que coisas semelhantes existam para outros servidores web).
gatoatigrado 13/08/13
4
Se você estiver usando o Chrome basta fazer uma Ctrl+Shift+R(ou seja duro Recarregar) e não importa o mecanismo de cache é cromo utilizado irá ignorá-lo e re-buscar todos os scripts, folhas de estilo etc.
snajahi
4
ctrl + shift + R não funciona para mim no Chrome, mas na guia "rede" das ferramentas do desenvolvedor, clicar em "desativar cache" funciona perfeitamente. Para mim, esse é um problema do lado do cliente que não deve ser resolvido usando hacks no servidor, como muitas das sugestões abaixo; deve ser corrigido no cliente onde o "problema" existe. Se você corrigi-lo no servidor e esquecer de corrigi-lo, a produção poderá ser afetada adversamente.
Ant Kutschera
8
ctrl + shift + R ignora o cache para solicitações normais. pedidos ajax feitos de angular para ng-include| ng-view| templateUrlnão são tratados por este atalho
André Werlang
2
Você não pode pedir a todos os usuários finais que usem Ctrl + Shift + R ao visitar o site, então qual é a resposta para esta pergunta no caso de não desenvolvimento? "Para mim, este é um problema do lado do cliente que não deve ser resolvido usando hacks no servidor, como muitas das sugestões abaixo" - não concordo, você não pode controlar clientes em um ambiente web, portanto a correção para produção deve ser orientado a aplicativos. Por esse motivo, aceitei: $ rootScope. $ On ('$ viewContentLoaded', function () {$ templateCache.removeAll ();});
Robert Christian

Respostas:

200

Para desenvolvimento, você também pode desativar o cache do navegador - Nas Ferramentas de desenvolvimento do Chrome, no canto inferior direito, clique na engrenagem e marque a opção

Desativar o cache (enquanto DevTools está aberto)

Atualização: No Firefox, existe a mesma opção no Depurador -> Configurações -> Seção Avançada (marcada para a Versão 33)

Atualização 2: Embora esta opção apareça no Firefox, alguns relatórios não funcionam. Sugiro usar o firebug e seguir a resposta hadaytullah.

LukeSolar
fonte
7
Essa deve ser a resposta aceita, pois não requer alteração de código e está mais ao ponto da solicitação do OP. É claro que você deseja que um aplicativo de produção armazene em cache as solicitações, portanto, fazer o que as pessoas acima sugeriram, embora bem intencionadas, pode ser problemático se o código for deixado em um aplicativo de produção.
Aaron Wagner
Alguma sugestão para outros navegadores como o Firefox?
Lereveme 24/10
No Firefox: Depurador> Configurações (o equipamento), existe a mesma opção.
LukeSolar
5
Isso não funciona no Firefox. Mesmo quando o cache está desativado e a caixa de ferramentas está aberta, os modelos ainda são armazenados em cache.
Patrick J Collins
4
O cache também afeta a produção? E se eu enviar novos arquivos da Web para o servidor, o que impede solicitações subsequentes de clientes de produção de carregar versões em cache pré-publicadas?
meffect
111

Com base na resposta do @ Valentyn, aqui está uma maneira de sempre limpar automaticamente o cache sempre que o conteúdo do ng-view for alterado:

myApp.run(function($rootScope, $templateCache) {
   $rootScope.$on('$viewContentLoaded', function() {
      $templateCache.removeAll();
   });
});
Mark Rajcok
fonte
@ user252690 Provavelmente também é necessário garantir que o modelo html não foi enviado com cabeçalhos de cache. Veja aqui e aqui para possíveis correções
Chris Foster
30
Um aviso: esvaziar o $ templateCache pode realmente ter conseqüências não intencionais. Por exemplo, o UI Bootstrap adiciona parciais padrão diretamente ao $ templateCache durante a inicialização e espera que elas estejam lá.
Strille 21/08/14
@ Strille Eu estava tentando usar o angular-ui-bootstrap Modal. O pop-up não estava visível. porque $ templateCache.removeAll (); alguma correção para isso?
Mukun
4
@Mukun: Não há uma solução fácil da maneira que eu vejo, além de não usar removeAll (), mas usar apenas remove () para excluir as chaves que você precisa limpar. Você precisaria de algum tipo de contabilidade para saber quais chaves excluir.
Strille
existe alguma maneira de limpar o cache apenas para o cache específico da visualização da interface do usuário.
Gayan
37

Conforme mencionado nas outras respostas, aqui e aqui , o cache pode ser limpo usando:

$templateCache.removeAll();

No entanto, como sugerido por gatoatigrado no comentário , isso parece funcionar apenas se o modelo html foi exibido sem nenhum cabeçalho de cache.

Então, isso funciona para mim:

Em angular:

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

Você pode adicionar cabeçalhos de cache de várias maneiras, mas aqui estão algumas soluções que funcionam para mim.

Se estiver usando IIS, adicione isso ao seu web.config:

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

Se estiver usando o Nginx, você pode adicionar isso à sua configuração:

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

Editar

Acabei de perceber que a pergunta mencionava a devmáquina, mas espero que isso ainda ajude alguém ...

Chris Foster
fonte
2
sim, mesmo que isso não responda diretamente à pergunta original, isso realmente me ajudou a resolver o problema de armazenamento em cache em um site ativo.
21414 Andre Andre
31

Se você estiver falando sobre o cache usado para armazenar em cache modelos, sem recarregar a página inteira, poderá esvaziá-lo com algo como:

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

E na marcação:

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

E pressione este botão para limpar o cache.

Valentyn Shybanov
fonte
22

Solução para Firefox (33.1.1) usando Firebug (22.0.6)

  1. Ferramentas> Ferramentas Web> Firebug> Abrir Firebug.
  2. Nas visualizações do Firebug, vá para a visualização "Net".
  3. Um símbolo do menu suspenso aparecerá ao lado de "Rede" (título da exibição).
  4. Selecione "Desativar cache do navegador" no menu suspenso.
hadaytullah
fonte
Tentei no Firebug (2.0.11) e Firefox (38.0.1) no mac, mas isso não funcionou.
paullb
19

Esse trecho me ajudou a me livrar do cache de modelos

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

Os detalhes do seguinte snippet podem ser encontrados neste link: http://oncodesign.io/2014/02/19/safely-prevent-template-caching-in-angularjs/

Code Prank
fonte
nitpick mas quando a corrente nunca é um objeto? Não tenho certeza que é já não lá também, masif (!current) { return; }
Nate-Wilkins
Isso anula o armazenamento em cache do Angular de modelos baseados em rota, mas não parciais ng-include'd.
Bradw2k
16

Estou postando isso apenas para cobrir todas as possibilidades, pois nenhuma das outras soluções funcionou para mim (elas geraram erros devido a dependências do modelo angular-bootstrap, entre outras).

Enquanto estiver desenvolvendo / depurando um modelo específico, você pode garantir que ele sempre atualize incluindo um carimbo de data / hora no caminho, como este:

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

Observe o final ?nd=' + Date.now()na templateUrlvariável.

diosney
fonte
1
Mais tarde, você pode definir um .value('DEBUG', true)para ativar ou não essa linha.
diosney
1
Minha solução foi usar o seguinte dentro da fase de inicialização do meu módulo principal sob .run(function($rootScope) { $rootScope.DEBUG = true; ...e, em seguida, dentro da directiva injetar US $ rootScope como .directive('filter', ['$rootScope', function($rootScope)...e no objeto-propriedade retornado: templateUrl: '/app/components/filter/filter-template.html' + ($rootScope.DEBUG ? '?n=' + Date.now() : ''). Talvez você possa elaborar sua abordagem .value ('DEBUG', true)? Voto a favor!
JackLeEmmerdeur
O uso de .value('DEBUG', trueé o mesmo que você fez $rootScope, mas sem desordená-lo :) Mais tarde, você pode injetar DEBUGno controlador e consultar como um serviço normal.
Diosney
Você poderia estender o código fonte na sua resposta para incluir o .value(...)item, se não for muito sofisticado? Suponho que o conceito por trás seja uma melhor prática angular desconhecida para mim.
JackLeEmmerdeur
1
Esta solução é muito útil ao trabalhar com o Ionic. Isso vai me poupar muito tempo, pois torna a carga hepática útil novamente. Muito obrigado!
ajuser
11

Como já foi dito, derrotar o cache completamente para fins de desenvolvimento pode ser feito facilmente sem alterar o código: use uma configuração do navegador ou um plugin. Fora do dev, para impedir o armazenamento em cache de modelos angulares de modelos baseados em rotas, remova a URL do modelo durante o cache de $ routeChangeStart (ou $ stateChangeStart, para o roteador da interface do usuário), como mostrou Shayan. No entanto, isso NÃO afeta o armazenamento em cache dos modelos carregados pelo ng-include, porque esses modelos não são carregados pelo roteador.

Eu queria poder corrigir qualquer modelo, incluindo os carregados pelo ng-include, na produção e fazer com que os usuários recebessem o hotfix no navegador rapidamente, sem precisar recarregar a página inteira. Também não estou preocupado em derrotar o cache HTTP de modelos. A solução é interceptar todas as solicitações HTTP que o aplicativo faz, ignorar aquelas que não são dos modelos .html do meu aplicativo e adicionar um parâmetro ao URL do modelo que muda a cada minuto. Observe que a verificação de caminho é específica ao caminho dos modelos do seu aplicativo. Para obter um intervalo diferente, altere a matemática do parâmetro ou remova o% completamente para não obter armazenamento em cache.

// 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
fonte
Estou interessado em sua solução - você mantém esse código para sempre - ou por um certo período de tempo depois de fazer uma alteração? Razão - estaria preocupado com o desempenho. Além disso, você menciona o efeito colateral que derrota o HTTP. Quer dizer que é um bom efeito colateral maneira correta? - o que significa que é o que você destina para ter 'hotfix' Thx
Jamie
1
Eu deixo esse código para sempre, embora tenhamos reduzido o tempo de bloqueio de cache para 10 minutos. Portanto, a cada 10 minutos de uso, o usuário recarregará novos modelos de html. Para meu aplicativo de negócios, é um custo aceitável obter a capacidade de aplicar hot-templates, mas obviamente isso prejudicaria muito o desempenho de alguns tipos de aplicativos. ... É lamentável que o cache HTTP também seja derrotado, mas não vejo uma maneira de derrotar inteligentemente o cache de modelos Angular sem derrotar também o Etag etc. O cache angular do modelo da IMO simplesmente não é configurável o suficiente.
Bradw2k
1
+1 Eu tive a mesma ideia, mas só intercepto pelo host local. Você pode ver a minha implementação do interceptor aqui: overengineer.net/...
joshcomley
@joshcomley Se você só precisa derrotar o cache no localhost, por que não usar um plug-in de navegador que derrote todo o cache?
Bradw2k 02/03
@ bradw2k desta maneira, eu estou no controle completo do que é e não é armazenado em cache, o que pode ser útil para o desenvolvimento. Também testo em todos os navegadores, e nem todos os navegadores têm extensões que fazem o que eu preciso. Também posso ter um indicador no site em desenvolvimento informando se o armazenamento em cache está desabilitado ou não, pois às vezes eu quero apenas desabilitar e testar nos navegadores por um tempo.
precisa saber é o seguinte
8

Se você estiver usando o roteador da interface do usuário, poderá usar um decorador e atualizar o serviço $ templateFactory e anexar um parâmetro de sequência de consultas ao templateUrl, e o navegador sempre carregará o novo modelo no 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]);

Tenho certeza de que você pode obter o mesmo resultado decorando o método "when" no $ routeProvider.

Aman Mahajan
fonte
Uma alternativa muito melhor é usar um plug-in como gulp-angular-templatecache para registrar modelos js angulares no $ templateCache. Isso deve ser usado junto com gulp-rev. Sempre que um modelo é alterado, um novo arquivo JavaScript com um número de revisão diferente será criado e o cache nunca será um problema.
Aman Mahajan
3

Descobri que o método do interceptor HTTP funciona muito bem e permite flexibilidade e controle adicionais. Além disso, você pode bloquear o cache para cada release de produção usando um hash de release como a variável do buster.

Aqui está o que o método dev cachebusting parece usar 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
fonte
1

Aqui está outra opção no Chrome.

Clique F12para abrir as ferramentas do desenvolvedor. Em seguida, Recursos > Armazenamento em Cache > Atualizar Cache .

insira a descrição da imagem aqui

Eu gosto dessa opção porque não preciso desativar o cache como em outras respostas.

Jess
fonte
No Chrome v. 54.0.2840.99 em novembro de 2016, encontrei isso em uma guia chamada Application.rather than Resources.
StackOverflowUser
0

Não há solução para impedir o cache do navegador / proxy, pois você não pode ter o controle.

A outra maneira de forçar novos conteúdos para seus usuários é renomear o arquivo HTML! Exatamente como https://www.npmjs.com/package/grunt-filerev faz para ativos.

Thomas Decaux
fonte