如何在AngularJS中包含视图/部分特定的样式
对于我的应用程序使用的各种视图,使用单独的样式表的正确/可接受的方式是什么?
目前,我在顶部的view / partial的html中放置了一个链接元素,但是我已经被告知这是不好的做法,即使所有的现代浏览器都支持它,但是我可以看到为什么它被折磨。
另一种可能是将独立的样式表放在我的index.html head
但是我希望它只加载样式表,如果它的视图以性能名称加载的话。
这是不好的做法,因为样式将不会生效,直到从服务器加载css后,导致慢速浏览器中的未格式化内容快速闪光? 虽然我在本地进行testing,但我还没有目睹这一点。
有没有办法通过传递给Angular的$routeProvider.when
的对象加载CSS?
提前致谢!
我现在知道这个问题已经很老了,但是在对这个问题的各种解决scheme做了大量的研究之后,我想我可能会想出一个更好的解决scheme。
更新1:自发布这个答案以来,我已经将所有这些代码添加到一个简单的服务,我已经发布到GitHub。 回购在这里 。 随时查看更多信息。
更新2:如果你需要的是一个轻量级的解决scheme,为你的路线提供样式表,这个答案是很好的。 如果您想在整个应用程序中pipe理按需样式表的更完整解决scheme,您可能需要签出Door3的AngularCSS项目 。 它提供了更精细的function。
如果将来任何人都有兴趣,下面是我想到的:
1.为<head>
元素创build一个自定义指令:
app.directive('head', ['$rootScope','$compile', function($rootScope, $compile){ return { restrict: 'E', link: function(scope, elem){ var html = '<link rel="stylesheet" ng-repeat="(routeCtrl, cssUrl) in routeStyles" ng-href="{{cssUrl}}" />'; 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; }); } }); } }; } ]);
这个指令做了以下的事情:
- 它编译(使用
$compile
)一个htmlstring,为使用ng-repeat
和ng-href
的scope.routeStyles
对象中的每个项目创build一组<link />
标记。 - 它将编译后的
<link />
元素集添加到<head>
标记中。 - 然后它使用
$rootScope
来监听'$routeChangeStart'
事件。 对于每个'$routeChangeStart'
事件,它抓取“当前”$$route
对象(用户即将离开的路由),并从<head>
标记中删除其部分特定的css文件。 它也抓取“下一个”$$route
对象(用户即将到达的路由),并将其任何部分特定的css文件添加到<head>
标记。 - 编译后的
<link />
标记的ng-repeat
部分根据添加到scope.routeStyles
对象中或从中删除的内容来处理所有添加和删除特定于页面的样式表。
注意: 这要求ng-app
属性在<html>
元素上,而不在<body>
或者<html>
。
2.使用$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'] }) }]);
这个configuration文件将一个自定义的css
属性添加到用于设置每个页面的路由的对象。 该对象作为.$$route
传递给每个'$routeChangeStart'
事件。 所以当听'$routeChangeStart'
事件时,我们可以获取我们指定的css
属性,并根据需要追加/删除这些<link />
标签。 请注意,在路由上指定一个css
属性是完全可选的,因为它在'/some/route/2'
示例中被省略了。 如果这个路由没有css
属性,那么<head>
指令对这个路由将不起任何作用。 还要注意,甚至可以在每个路由中都有多个特定于页面的样式表,就像上面的'/some/route/3'
示例一样,其中css
属性是该路由所需的样式表的相对path的数组。
3.你完成了这两件事设置了一切需要的东西,在我看来,它是尽可能干净的代码。
希望能够帮助那些可能像我一样挣扎于这个问题的人。
@ tennisgent的解决scheme是伟大的。 不过,我觉得有点有限。
Angular的模块化和封装超越了路线。 基于networking向基于组件开发的方向发展,在指令中应用这一点也很重要。
如您所知,在Angular中,我们可以在页面和组件中包含模板(结构)和控制器(行为)。 AngularCSS使最后缺less的一块:附加样式表(演示文稿)。
对于完整的解决scheme,我build议使用AngularCSS。
- 支持Angular的ngRoute,UI路由器,指令,控制器和服务。
- 不需要在
<html>
标记中包含ng-app
。 当您有多个应用程序在同一页面上运行时,这一点很重要 - 您可以自定义样式表注入的位置:头部,正文,自定义select器等…
- 支持预加载,持久和caching清除
- 支持媒体查询,并通过matchMedia API优化页面加载
https://github.com/door3/angular-css
这里有些例子:
路线
$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' } ] });
指令
myApp.directive('myDirective', function () { return { restrict: 'E', templateUrl: 'my-directive/my-directive.html', css: 'my-directive/my-directive.css' } });
另外,您可以使用$css
服务来处理边缘情况:
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(); });
你可以在这里阅读更多关于AngularCSS:
http://door3.com/insights/introducing-angularcss-css-demand-angularjs
可以在$routeProvider
追加一个新的样式表。 为了简单,我使用一个string,但也可以创build新的链接元素,或者为样式表创build一个服务
/* 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('<link id="myViewName" href="myViewName.css" rel="stylesheet">'); }
页面中预先填充的最大好处是任何背景图片都已经存在,而且FOUC FOUC
那么FOUC
@ sz3,今天我已经足够搞笑了,我不得不做你想要达到的目标:' 只有当用户访问 '一个特定的页面时加载一个特定的CSS文件 。 所以我使用了上面的解决scheme。
但我在这里回答你的最后一个问题:' 我应该把代码放在哪里? 任何想法 ?
你是正确的,包括代码到解决scheme ,但你需要改变一下格式。
看看下面的代码:
.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('<link id="home" href="home.css" rel="stylesheet">'); } } } })
我刚刚testing,它工作正常 ,它注入的HTML,它只加载我的'home.css'只有当我点击'/家'路线。
可以在这里find完整的解释,但基本上解决:应该获得格式的对象
{ 'key' : string or function() }
你可以把“ 钥匙 ”命名为任何你喜欢的东西 – 就我而言,我称之为“ 风格 ”。
那么对于你有两个select的价值:
-
如果它是一个string ,那么它是一个服务的别名。
-
如果它是函数 ,那么它被注入并且返回值被视为依赖。
这里的要点是函数内部的代码将在控制器被实例化之前被执行,并且$ routeChangeSuccess事件被触发。
希望有所帮助。
真棒,谢谢! 只需做一些调整,以使其与UI路由器工作:
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 = '<link rel="stylesheet" ng-repeat="(routeCtrl, cssUrl) in routeStyles" ng-href="{{cssUrl}}" />'; 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); }); }); } } }]);
如果你只需要将你的CSS 应用到一个特定的视图,那么我在控制器中使用这个方便的代码片段:
$("body").addClass("mystate"); $scope.$on("$destroy", function() { $("body").removeClass("mystate"); });
这将在状态加载时将一个类添加到我的body
标记中,并在状态被破坏时将其删除(即某人更改页面)。 这解决了我的应用程序中只需要将CSS应用于一个状态的相关问题。
“严格使用”; angular.module('app').run(['$ rootScope','$ state','$ stateParams',function($ rootScope,$ state,$ stateParams){$ rootScope。$ state = $ state; $ 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' ] }); } ] } }) );