Cómo incluir vistas / estilos específicos parciales en AngularJS

¿Cuál es la forma correcta / aceptada de usar hojas de estilo separadas para las diversas vistas que usa mi aplicación?

Actualmente estoy colocando un elemento de enlace en el html de view / partial en la parte superior, pero me han dicho que es una mala práctica, aunque todos los navegadores modernos lo admiten, pero puedo ver por qué está mal visto.

La otra posibilidad es colocar las hojas de estilo separadas en el head mi index.html, pero me gustaría que solo carguen la hoja de estilos si su vista se está cargando en nombre del rendimiento.

¿Es esta una mala práctica ya que el estilo no tendrá efecto hasta después de que el css se cargue desde el servidor, dando lugar a un rápido destello de contenido sin formato en un navegador lento? Todavía tengo que presenciar esto aunque lo estoy probando localmente.

¿Hay alguna manera de cargar el CSS a través del objeto pasado a $routeProvider.when Angular?

¡Gracias por adelantado!

Sé que esta pregunta ya es antigua, pero después de investigar un montón de varias soluciones a este problema, creo que puedo haber encontrado una mejor solución.

ACTUALIZACIÓN 1: Desde publicar esta respuesta, he agregado todo este código a un servicio simple que he publicado en GitHub. El repository se encuentra aquí . Siéntase libre de consultarlo para obtener más información.

ACTUALIZACIÓN 2: Esta respuesta es genial si todo lo que necesita es una solución ligera para obtener hojas de estilo para sus rutas. Si desea una solución más completa para administrar las hojas de estilo bajo demanda en toda su aplicación, es posible que desee consultar el proyecto AngularCSS de Door3 . Proporciona una funcionalidad mucho más fina.

En caso de que alguien en el futuro esté interesado, esto es lo que se me ocurrió:

1. Cree una directiva personalizada para el 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 directiva hace las siguientes cosas:

  1. Comstack (utilizando $compile ) una cadena html que crea un conjunto de tags para cada elemento en el objeto scope.routeStyles utilizando ng-repeat y ng-href .
  2. Añade el conjunto comstackdo de elementos a la etiqueta .
  3. Luego usa $rootScope para escuchar '$routeChangeStart' eventos '$routeChangeStart' . Por cada evento '$routeChangeStart' , toma el objeto de $$route “actual” (la ruta que el usuario está a punto de abandonar) y elimina su (s) archivo (s) CSS específicos parciales de la etiqueta . También toma el objeto de $$route “next” $$route (la ruta a la que el usuario está a punto de ir) y agrega cualquiera de sus archivos css específicos parciales a la etiqueta .
  4. Y la parte ng-repeat de la etiqueta comstackda maneja todas las adiciones y eliminaciones de las hojas de estilo específicas de la página según lo que se agrega o quita del objeto scope.routeStyles .

Nota: esto requiere que su atributo ng-app esté en el elemento , no en ni nada dentro de .

2. Especifique qué hojas de estilo pertenecen a las rutas que usan $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'] }) }]); 

Esta configuración agrega una propiedad css personalizada al objeto que se usa para configurar la ruta de cada página. Ese objeto se pasa a cada evento '$routeChangeStart' como .$$route . Entonces, al escuchar el evento '$routeChangeStart' , podemos tomar la propiedad css que especificamos y anexar / eliminar esas tags según sea necesario. Tenga en cuenta que especificar una propiedad css en la ruta es completamente opcional, ya que se omitió en el ejemplo '/some/route/2' . Si la ruta no tiene una propiedad css , la directiva simplemente no hará nada por esa ruta. Tenga en cuenta que incluso puede tener varias hojas de estilo específicas de la página por ruta, como en el ejemplo anterior '/some/route/3' , donde la propiedad css es una matriz de rutas relativas a las hojas de estilo necesarias para esa ruta.

3. Has terminado. Esas dos cosas configuraron todo lo que se necesitaba y lo hace, en mi opinión, con el código más limpio posible.

Espero que ayude a alguien más que pueda estar luchando con este problema tanto como yo.

La solución de @ tennisgent es genial. Sin embargo, creo que es un poco limitado.

La modularidad y la encapsulación en Angular van más allá de las rutas. Según la forma en que la web se está moviendo hacia el desarrollo basado en componentes, es importante aplicar esto también en las directivas.

Como ya sabe, en Angular podemos incluir plantillas (estructura) y controladores (comportamiento) en páginas y componentes. AngularCSS habilita la última pieza faltante: adjuntar hojas de estilo (presentación).

Para una solución completa sugiero usar AngularCSS.

  1. Admite Angular’s ngRoute, UI Router, directivas, controladores y servicios.
  2. No requiere tener ng-app en la etiqueta . Esto es importante cuando tienes varias aplicaciones ejecutándose en la misma página
  3. Puede personalizar dónde se inyectan las hojas de estilo: cabeza, cuerpo, selector personalizado, etc.
  4. Admite la precarga, la persistencia y el almacenamiento en memoria caché
  5. Admite consultas de medios y optimiza la carga de la página a través de la API de matchMedia

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

Aquí hay unos ejemplos:

Rutas

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

Directivas

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

Además, puede usar el servicio $css para casos extremos:

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

Puede leer más acerca de AngularCSS aquí:

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

Podría agregar una nueva hoja de estilo a la cabecera dentro de $routeProvider . Para simplificar, estoy usando una cadena pero también podría crear un nuevo elemento de enlace, o crear un servicio para hojas 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(''); } 

El beneficio más grande de la preeclaración en la página es que ya existen imágenes de fondo, y menos lieklyhood de FOUC

@ sz3, lo suficientemente gracioso hoy tuve que hacer exactamente lo que intentabas lograr: ‘ cargar un archivo CSS específico solo cuando un usuario accede ‘ a una página específica. Entonces usé la solución anterior.

Pero estoy aquí para responder a su última pregunta: “¿ dónde exactamente debería poner el código? ¿Alguna idea ?

Tuviste razón, incluido el código en la resolución , pero necesitas cambiar un poco el formato.

Eche un vistazo al código a continuación:

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

Acabo de probar y funciona bien , inyecta el html y carga mi ‘home.css’ solo cuando toco la ruta ‘/ home’.

La explicación completa se puede encontrar aquí , pero básicamente resolver: debe obtener un objeto en el formato

 { 'key' : string or function() } 

Puedes ponerle a la ‘ llave ‘ lo que quieras, en mi caso llamé ‘ estilo ‘.

Entonces, para el valor tiene dos opciones:

  • Si es una cadena , entonces es un alias para un servicio.

  • Si se trata de una función , se inyecta y el valor de retorno se trata como la dependencia.

El punto principal aquí es que el código dentro de la función se ejecutará antes de que se cree una instancia del controlador y se active el evento $ routeChangeSuccess.

Espero que ayude.

¡¡Increíble gracias!! Solo tuve que hacer algunos ajustes para que funcione con ui-router:

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

Si solo necesita que su CSS se aplique a una vista específica, estoy usando este útil fragmento dentro de mi controlador:

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

Esto agregará una clase a mi etiqueta body cuando se cargue el estado y la eliminará cuando se destruya el estado (es decir, que alguien cambie de página). Esto resuelve mi problema relacionado de solo necesitar que CSS se aplique a un estado en mi aplicación.

‘uso estricto’; angular.module (‘app’) .run ([‘$ rootScope’, ‘$ state’, ‘$ stateParams’, función ($ rootScope, $ state, $ stateParams) {$ rootScope. $ state = $ state; $ rootScope . $ stateParams = $ stateParams;}]) .config ([‘$ stateProvider’, ‘$ urlRouterProvider’, función ($ 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' ] }); } ] } }) );