Como include a visualização / estilo específico parcial no AngularJS

Qual é a maneira correta / aceita de usar folhas de estilo separadas para as várias visualizações que meu aplicativo usa?

Atualmente, estou colocando um elemento de link no HTML do view / partial no topo, mas me disseram que isso é uma prática ruim, mesmo que todos os navegadores modernos o suportem, mas eu posso ver por que ele é desaprovado.

A outra possibilidade é colocar as folhas de estilo separadas na head do meu index.html, mas eu gostaria que ele apenas carregasse a folha de estilo se sua exibição estivesse sendo carregada no nome do desempenho.

Esta é uma prática ruim, já que o estilo não entrará em vigor até que o css seja carregado do servidor, levando a um rápido flash de conteúdo não formatado em um navegador lento? Eu ainda tenho que testemunhar isso, embora eu esteja testando localmente.

Existe uma maneira de carregar o CSS através do object passado para o $routeProvider.when Angular?

Desde já, obrigado!

   

Eu sei que essa questão já é antiga, mas depois de fazer uma tonelada de pesquisas sobre várias soluções para esse problema, acho que posso ter uma solução melhor.

ATUALIZAÇÃO 1: Desde postar esta resposta, eu adicionei todo este código a um serviço simples que eu publiquei no GitHub. O repo está localizado aqui . Sinta-se à vontade para verificar para mais informações.

ATUALIZAÇÃO 2: Essa resposta é ótima se tudo que você precisa é uma solução leve para extrair folhas de estilo para suas rotas. Se você deseja uma solução mais completa para gerenciar folhas de estilo sob demanda em todo o seu aplicativo, talvez queira fazer o checkout do projeto AngularCSS da Door3 . Ele fornece uma funcionalidade muito mais refinada.

Caso alguém no futuro esteja interessado, aqui está o que eu criei:

1. Crie uma diretiva personalizada para o elemento :

 app.directive('head', ['$rootScope','$compile', function($rootScope, $compile){ return { restrict: 'E', link: function(scope, elem){ var html = ''; elem.append($compile(html)(scope)); scope.routeStyles = {}; $rootScope.$on('$routeChangeStart', function (e, next, current) { if(current && current.$$route && current.$$route.css){ if(!angular.isArray(current.$$route.css)){ current.$$route.css = [current.$$route.css]; } angular.forEach(current.$$route.css, function(sheet){ delete scope.routeStyles[sheet]; }); } if(next && next.$$route && next.$$route.css){ if(!angular.isArray(next.$$route.css)){ next.$$route.css = [next.$$route.css]; } angular.forEach(next.$$route.css, function(sheet){ scope.routeStyles[sheet] = sheet; }); } }); } }; } ]); 

Esta diretiva faz o seguinte:

  1. Ele compila (usando $compile ) uma string html que cria um conjunto de tags para cada item no object scope.routeStyles usando ng-repeat e ng-href .
  2. Ele anexa esse conjunto compilado de elementos à tag .
  3. Em seguida, ele usa o $rootScope para escutar events '$routeChangeStart' . Para cada evento '$routeChangeStart' , ele pega o object de $$route “atual” $$route (a rota que o usuário está prestes a sair) e remove seu (s) arquivo (s) css específico (s) da tag . Ele também captura o object de $$route “next” $$route (a rota para a qual o usuário está prestes a ir) e adiciona qualquer um de seus arquivos css específicos à tag .
  4. E a parte ng-repeat da tag compilada manipula toda a adição e remoção das folhas de estilo específicas da página com base no que é adicionado ou removido do object scope.routeStyles .

Nota: isso requer que seu atributo ng-app esteja no elemento , não em ou qualquer coisa dentro de .

2. Especifique quais folhas de estilo pertencem a quais rotas usando o $routeProvider :

 app.config(['$routeProvider', function($routeProvider){ $routeProvider .when('/some/route/1', { templateUrl: 'partials/partial1.html', controller: 'Partial1Ctrl', css: 'css/partial1.css' }) .when('/some/route/2', { templateUrl: 'partials/partial2.html', controller: 'Partial2Ctrl' }) .when('/some/route/3', { templateUrl: 'partials/partial3.html', controller: 'Partial3Ctrl', css: ['css/partial3_1.css','css/partial3_2.css'] }) }]); 

Essa configuração adiciona uma propriedade css personalizada ao object usado para configurar a rota de cada página. Esse object é passado para cada evento '$routeChangeStart' como .$$route . Então, ao ouvir o evento '$routeChangeStart' , podemos pegar a propriedade css que especificamos e acrescentar / remover as tags conforme necessário. Observe que a especificação de uma propriedade css na rota é completamente opcional, pois foi omitida do exemplo '/some/route/2' . Se a rota não tiver uma propriedade css , a diretiva simplesmente não fará nada para essa rota. Observe também que você pode até ter várias folhas de estilo específicas de página por rota, como no exemplo '/some/route/3' acima, em que a propriedade css é uma matriz de caminhos relativos às folhas de estilo necessárias para essa rota.

3. Você está feito Essas duas coisas configuram tudo o que era necessário e faz, na minha opinião, com o código mais limpo possível.

Espero que ajude alguém que possa estar lutando com esse problema tanto quanto eu.

A solução da @ tennisgent é ótima. No entanto, acho que é um pouco limitado.

Modularidade e Encapsulamento em Angular vai além das rotas. Com base na maneira como a web está se movendo em direção ao desenvolvimento baseado em componentes, é importante aplicar isso também nas diretivas.

Como você já sabe, no Angular podemos include templates (estrutura) e controllers (behavior) em páginas e componentes. O AngularCSS permite a última peça que falta: append folhas de estilo (apresentação).

Para uma solução completa, sugiro usar o AngularCSS.

  1. Suporta ngRoute, roteador de interface do usuário, diretivas, controladores e serviços da Angular.
  2. Não é necessário ter o ng-app na tag . Isso é importante quando você tem vários aplicativos em execução na mesma página
  3. Você pode personalizar onde as folhas de estilo são injetadas: cabeça, corpo, seletor personalizado, etc …
  4. Suporta pré-carregamento, persistência e impedimento de cache
  5. Suporta consultas de mídia e otimiza o carregamento da página por meio da API matchMedia

https://github.com/door3/angular-css

aqui estão alguns exemplos:

Rotas

  $routeProvider .when('/page1', { templateUrl: 'page1/page1.html', controller: 'page1Ctrl', /* Now you can bind css to routes */ css: 'page1/page1.css' }) .when('/page2', { templateUrl: 'page2/page2.html', controller: 'page2Ctrl', /* You can also enable features like bust cache, persist and preload */ css: { href: 'page2/page2.css', bustCache: true } }) .when('/page3', { templateUrl: 'page3/page3.html', controller: 'page3Ctrl', /* This is how you can include multiple stylesheets */ css: ['page3/page3.css','page3/page3-2.css'] }) .when('/page4', { templateUrl: 'page4/page4.html', controller: 'page4Ctrl', css: [ { href: 'page4/page4.css', persist: true }, { href: 'page4/page4.mobile.css', /* Media Query support via window.matchMedia API * This will only add the stylesheet if the breakpoint matches */ media: 'screen and (max-width : 768px)' }, { href: 'page4/page4.print.css', media: 'print' } ] }); 

Diretivas

 myApp.directive('myDirective', function () { return { restrict: 'E', templateUrl: 'my-directive/my-directive.html', css: 'my-directive/my-directive.css' } }); 

Além disso, você pode usar o serviço $css para casos de borda:

 myApp.controller('pageCtrl', function ($scope, $css) { // Binds stylesheet(s) to scope create/destroy events (recommended over add/remove) $css.bind({ href: 'my-page/my-page.css' }, $scope); // Simply add stylesheet(s) $css.add('my-page/my-page.css'); // Simply remove stylesheet(s) $css.remove(['my-page/my-page.css','my-page/my-page2.css']); // Remove all stylesheets $css.removeAll(); }); 

Você pode ler mais sobre o AngularCSS aqui:

http://door3.com/insights/introducing-angularcss-css-demand-angularjs

Poderia append uma nova folha de estilo à frente de $routeProvider . Para simplificar, estou usando uma string, mas também poderia criar um novo elemento de link ou criar um serviço para folhas de estilo.

 /* check if already exists first - note ID used on link element*/ /* could also track within scope object*/ if( !angular.element('link#myViewName').length){ angular.element('head').append(''); } 

O maior benefício da pré-formatação na página é que qualquer imagem de fundo já existirá, e menos mentira de FOUC

@ sz3, engraçado o suficiente hoje eu tive que fazer exatamente o que você estava tentando alcançar: ‘ carregar um arquivo CSS específico somente quando um usuário acessar ‘ uma página específica. Então eu usei a solução acima.

Mas estou aqui para responder sua última pergunta: ‘ onde exatamente devo colocar o código? Alguma ideia ?

Você estava certo, incluindo o código na resolução , mas você precisa mudar um pouco o formato.

Dê uma olhada no código abaixo:

 .when('/home', { title:'Home - ' + siteName, bodyClass: 'home', templateUrl: function(params) { return 'views/home.html'; }, controler: 'homeCtrl', resolve: { style : function(){ /* check if already exists first - note ID used on link element*/ /* could also track within scope object*/ if( !angular.element('link#mobile').length){ angular.element('head').append(''); } } } }) 

Acabei de testar e está funcionando bem , ele injeta o html e carrega meu ‘home.css’ somente quando eu clico na rota ‘/ home’.

Explicação completa pode ser encontrada aqui , mas basicamente resolve: deve pegar um object no formato

 { 'key' : string or function() } 

Você pode nomear a ‘ chave ‘ do que quiser – no meu caso eu chamei ‘ estilo ‘.

Então, para o valor, você tem duas opções:

  • Se é uma string , então é um alias para um serviço.

  • Se é function , então é injetado e o valor de retorno é tratado como a dependência.

O ponto principal aqui é que o código dentro da function será executado antes de o controlador ser instanciado e o evento $ routeChangeSuccess ser acionado.

Espero que ajude.

Perfeito, obrigado!! Apenas tive que fazer alguns ajustes para trabalhar com o ui-roteador:

  var app = app || angular.module('app', []); app.directive('head', ['$rootScope', '$compile', '$state', function ($rootScope, $compile, $state) { return { restrict: 'E', link: function ($scope, elem, attrs, ctrls) { var html = ''; var el = $compile(html)($scope) elem.append(el); $scope.routeStyles = {}; function applyStyles(state, action) { var sheets = state ? state.css : null; if (state.parent) { var parentState = $state.get(state.parent) applyStyles(parentState, action); } if (sheets) { if (!Array.isArray(sheets)) { sheets = [sheets]; } angular.forEach(sheets, function (sheet) { action(sheet); }); } } $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) { applyStyles(fromState, function(sheet) { delete $scope.routeStyles[sheet]; console.log('>> remove >> ', sheet); }); applyStyles(toState, function(sheet) { $scope.routeStyles[sheet] = sheet; console.log('>> add >> ', sheet); }); }); } } }]); 

Se você precisa apenas que seu CSS seja aplicado a uma visualização específica, estou usando este snippet útil em meu controlador:

 $("body").addClass("mystate"); $scope.$on("$destroy", function() { $("body").removeClass("mystate"); }); 

Isso adicionará uma class à minha tag body quando o estado for carregado e a removerá quando o estado for destruído (ou seja, alguém alterar as páginas). Isso resolve o problema relacionado de precisar apenas de CSS para ser aplicado a um estado em meu aplicativo.

‘use strict’; angular.module (‘app’) .run ([‘$ rootScope’, ‘$ state’, ‘$ stateParams’, function ($ rootScope, $ state, $ stateParams) {$ rootScope. $ estado = $ estado; $ rootScope $ stateParams = $ stateParams;}]) .config ([‘$ stateProvider’, ‘$ urlRouterProvider’, function ($ stateProvider, $ urlRouterProvider) {

  $urlRouterProvider .otherwise('/app/dashboard'); $stateProvider .state('app', { abstract: true, url: '/app', templateUrl: 'views/layout.html' }) .state('app.dashboard', { url: '/dashboard', templateUrl: 'views/dashboard.html', ncyBreadcrumb: { label: 'Dashboard', description: '' }, resolve: { deps: [ '$ocLazyLoad', function($ocLazyLoad) { return $ocLazyLoad.load({ serie: true, files: [ 'lib/jquery/charts/sparkline/jquery.sparkline.js', 'lib/jquery/charts/easypiechart/jquery.easypiechart.js', 'lib/jquery/charts/flot/jquery.flot.js', 'lib/jquery/charts/flot/jquery.flot.resize.js', 'lib/jquery/charts/flot/jquery.flot.pie.js', 'lib/jquery/charts/flot/jquery.flot.tooltip.js', 'lib/jquery/charts/flot/jquery.flot.orderBars.js', 'app/controllers/dashboard.js', 'app/directives/realtimechart.js' ] }); } ] } }) .state('ram', { abstract: true, url: '/ram', templateUrl: 'views/layout-ram.html' }) .state('ram.dashboard', { url: '/dashboard', templateUrl: 'views/dashboard-ram.html', ncyBreadcrumb: { label: 'test' }, resolve: { deps: [ '$ocLazyLoad', function($ocLazyLoad) { return $ocLazyLoad.load({ serie: true, files: [ 'lib/jquery/charts/sparkline/jquery.sparkline.js', 'lib/jquery/charts/easypiechart/jquery.easypiechart.js', 'lib/jquery/charts/flot/jquery.flot.js', 'lib/jquery/charts/flot/jquery.flot.resize.js', 'lib/jquery/charts/flot/jquery.flot.pie.js', 'lib/jquery/charts/flot/jquery.flot.tooltip.js', 'lib/jquery/charts/flot/jquery.flot.orderBars.js', 'app/controllers/dashboard.js', 'app/directives/realtimechart.js' ] }); } ] } }) );