CSS 如何在 AngularJS 中包含视图/部分特定样式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15193492/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to include view/partial specific styling in AngularJS
提问by Brandon
What is the proper/accepted way to use separate stylesheets for the various views my application uses?
为我的应用程序使用的各种视图使用单独的样式表的正确/可接受的方法是什么?
Currently I'm placing a link element in the view/partial's html at the top but I've been told this is bad practice even though all modern browsers support it but I can see why it's frowned upon.
目前,我在顶部的视图/部分 html 中放置了一个链接元素,但我被告知这是不好的做法,即使所有现代浏览器都支持它,但我可以理解为什么它不受欢迎。
The other possibility is placing the separate stylesheets in my index.html's head
but I would like it to only load the stylesheet if its view is being loaded in the name of performance.
另一种可能性是将单独的样式表放在我的 index.html 中,head
但我希望它只加载样式表,如果它的视图是以性能的名义加载的。
Is this bad practice since styling won't take effect until after the css is loaded form the server, leading to a quick flash of unformatted content in a slow browser? I have yet to witness this although I'm testing it locally.
这是不好的做法,因为样式在从服务器加载 css 后才会生效,导致在慢速浏览器中快速闪烁未格式化的内容?虽然我正在本地测试,但我还没有见证这一点。
Is there a way to load the CSS through the object passed to Angular's $routeProvider.when
?
有没有办法通过传递给 Angular 的对象加载 CSS $routeProvider.when
?
Thanks in advance!
提前致谢!
回答by tennisgent
I know this question is old now, but after doing a ton of research on various solutions to this problem, I think I may have come up with a better solution.
我知道这个问题现在已经很老了,但是在对这个问题的各种解决方案进行了大量研究之后,我想我可能想出了一个更好的解决方案。
UPDATE 1:Since posting this answer, I have added all of this code to a simple service that I have posted to GitHub. The repo is located here. Feel free to check it out for more info.
UPDATE 2:This answer is great if all you need is a lightweight solution for pulling in stylesheets for your routes. If you want a more complete solution for managing on-demand stylesheets throughout your application, you may want to checkout Door3's AngularCSS project. It provides much more fine-grained functionality.
更新 1:自从发布此答案后,我已将所有这些代码添加到我发布到 GitHub 的一个简单服务中。回购位于此处。请随时查看以获取更多信息。
更新 2:如果您只需要一个轻量级的解决方案来为您的路线拉入样式表,那么这个答案就很好。如果您想要一个更完整的解决方案来管理整个应用程序中的按需样式表,您可能需要查看Door3 的 AngularCSS 项目。它提供了更细粒度的功能。
In case anyone in the future is interested, here's what I came up with:
如果将来有人感兴趣,这就是我想出的:
1. Create a custom directive for the <head>
element:
1. 为<head>
元素创建自定义指令:
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;
});
}
});
}
};
}
]);
This directive does the following things:
该指令执行以下操作:
- It compiles (using
$compile
) an html string that creates a set of<link />
tags for every item in thescope.routeStyles
object usingng-repeat
andng-href
. - It appends that compiled set of
<link />
elements to the<head>
tag. - It then uses the
$rootScope
to listen for'$routeChangeStart'
events. For every'$routeChangeStart'
event, it grabs the "current"$$route
object (the route that the user is about to leave) and removes its partial-specific css file(s) from the<head>
tag. It also grabs the "next"$$route
object (the route that the user is about to go to) and adds any of its partial-specific css file(s) to the<head>
tag. - And the
ng-repeat
part of the compiled<link />
tag handles all of the adding and removing of the page-specific stylesheets based on what gets added to or removed from thescope.routeStyles
object.
- 它编译(使用
$compile
)一个 html 字符串,该字符串使用和<link />
为scope.routeStyles
对象中的每个项目创建一组标签。ng-repeat
ng-href
- 它将已编译的一组
<link />
元素附加到<head>
标记中。 - 然后它使用
$rootScope
来侦听'$routeChangeStart'
事件。对于每个'$routeChangeStart'
事件,它获取“当前”$$route
对象(用户即将离开的路径)并从<head>
标签中删除其部分特定的 css 文件。它还抓取“下一个”$$route
对象(用户即将前往的路径)并将其任何特定于部分的 css 文件添加到<head>
标签中。 ng-repeat
编译<link />
标记的部分根据添加到scope.routeStyles
对象或从对象中删除的内容处理所有页面特定样式表的添加和删除。
Note:this requires that your ng-app
attribute is on the <html>
element, not on <body>
or anything inside of <html>
.
注意:这需要你的ng-app
属性是对<html>
元素,而不是<body>
或内部的任何物件<html>
。
2. Specify which stylesheets belong to which routes using the $routeProvider
:
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']
})
}]);
This config adds a custom css
property to the object that is used to setup each page's route. That object gets passed to each '$routeChangeStart'
event as .$$route
. So when listening to the '$routeChangeStart'
event, we can grab the css
property that we specified and append/remove those <link />
tags as needed. Note that specifying a css
property on the route is completely optional, as it was omitted from the '/some/route/2'
example. If the route doesn't have a css
property, the <head>
directive will simply do nothing for that route. Note also that you can even have multiple page-specific stylesheets per route, as in the '/some/route/3'
example above, where the css
property is an array of relative paths to the stylesheets needed for that route.
此配置css
向用于设置每个页面的路由的对象添加自定义属性。该对象'$routeChangeStart'
作为.$$route
. 因此,在侦听'$routeChangeStart'
事件时,我们可以获取css
我们指定的属性并<link />
根据需要附加/删除这些标签。请注意,css
在路由上指定属性是完全可选的,因为它在'/some/route/2'
示例中被省略了。如果该路由没有css
属性,则该<head>
指令将对该路由不执行任何操作。另请注意,每个路由甚至可以有多个特定于页面的样式表,如上'/some/route/3'
例所示,其中css
属性是该路由所需的样式表的相对路径数组。
3. You're doneThose two things setup everything that was needed and it does it, in my opinion, with the cleanest code possible.
3. 你已经完成了这两件事 设置了所有需要的东西,在我看来,它用最干净的代码来完成。
Hope that helps someone else who might be struggling with this issue as much as I was.
希望能帮助那些可能和我一样在这个问题上挣扎的人。
回答by castillo.io
@tennisgent's solution is great. However, I think is a little limited.
@tennisgent 的解决方案很棒。不过,我觉得有点局限。
Modularity and Encapsulation in Angular goes beyond routes. Based on the way the web is moving towards component-based development, it is important to apply this in directives as well.
Angular 中的模块化和封装超越了路由。基于 Web 向基于组件的开发发展的方式,在指令中应用这一点也很重要。
As you already know, in Angular we can include templates (structure) and controllers (behavior) in pages and components. AngularCSSenables the last missing piece: attaching stylesheets (presentation).
如您所知,在 Angular 中,我们可以在页面和组件中包含模板(结构)和控制器(行为)。AngularCSS实现了最后一个缺失的部分:附加样式表(演示)。
For a full solution I suggest using AngularCSS.
对于完整的解决方案,我建议使用 AngularCSS。
- Supports Angular's ngRoute, UI Router, directives, controllers and services.
- Doesn't required to have
ng-app
in the<html>
tag. This is important when you have multiple apps running on the same page - You can customize where the stylesheets are injected: head, body, custom selector, etc...
- Supports preloading, persisting and cache busting
- Supports media queries and optimizes page load via matchMedia API
- 支持 Angular 的 ngRoute、UI 路由器、指令、控制器和服务。
- 不需要
ng-app
在<html>
标签中有。当您在同一页面上运行多个应用程序时,这一点很重要 - 您可以自定义注入样式表的位置:头部、主体、自定义选择器等...
- 支持预加载、持久化和缓存破坏
- 支持媒体查询并通过 matchMedia API 优化页面加载
https://github.com/door3/angular-css
https://github.com/door3/angular-css
Here are some examples:
这里有些例子:
Routes
路线
$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'
}
]
});
Directives
指令
myApp.directive('myDirective', function () {
return {
restrict: 'E',
templateUrl: 'my-directive/my-directive.html',
css: 'my-directive/my-directive.css'
}
});
Additionally, you can use the $css
service for edge cases:
此外,您可以将该$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();
});
You can read more about AngularCSS here:
您可以在此处阅读有关 AngularCSS 的更多信息:
http://door3.com/insights/introducing-angularcss-css-demand-angularjs
http://door3.com/insights/introducing-angularcss-css-demand-angularjs
回答by charlietfl
Could append a new stylesheet to head within $routeProvider
. For simplicity am using a string but could create new link element also, or create a service for stylesheets
可以附加一个新的样式表到 head 内$routeProvider
。为简单起见,我使用字符串但也可以创建新的链接元素,或为样式表创建服务
/* 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">');
}
Biggest benefit of prelaoding in page is any background images will already exist, and less lieklyhood of FOUC
在页面中预加载的最大好处是任何背景图像都已经存在,并且不太可能 FOUC
回答by Denison Luz
@sz3, funny enough today I had to do exactly what you were trying to achieve: 'load a specific CSS file only when a user access' a specific page. So I used the solution above.
@sz3,今天很有趣,我必须完全按照您的目的去做:“仅当用户访问”特定页面时才加载特定的 CSS 文件。所以我使用了上面的解决方案。
But I am here to answer your last question: 'where exactly should I put the code. Any ideas?'
但我在这里回答你的最后一个问题:'我应该把代码放在哪里。有什么想法吗?
You were right including the code into the resolve, but you need to change a bit the format.
您是对的,将代码包含在resolve 中,但您需要稍微更改格式。
Take a look at the code below:
看看下面的代码:
.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">');
}
}
}
})
I've just tested and it's working fine, it injects the html and it loads my 'home.css' only when I hit the '/home' route.
我刚刚测试过,它工作正常,它注入 html 并仅在我点击“/home”路线时加载我的“home.css”。
Full explanation can be found here, but basically resolve:should get an object in the format
完整的解释可以在这里找到,但基本上解决了:应该得到一个格式的对象
{
'key' : string or function()
}
You can name the 'key' anything you like - in my case I called 'style'.
您可以将“键”命名为任何您喜欢的名称——在我的例子中,我称之为“样式”。
Then for the value you have two options:
然后对于该值,您有两个选择:
If it's a string, then it is an alias for a service.
If it's function, then it is injected and the return value is treated as the dependency.
如果它是string,则它是服务的别名。
如果它是function,则将其注入并将返回值视为依赖项。
The main point here is that the code inside the function is going to be executed before before the controller is instantiated and the $routeChangeSuccess event is fired.
这里的要点是函数内部的代码将在控制器实例化和 $routeChangeSuccess 事件被触发之前执行。
Hope that helps.
希望有帮助。
回答by CraigM
Awesome, thank you!! Just had to make a few adjustments to get it working with ui-router:
太棒了,谢谢你!!只需进行一些调整即可使其与 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 = '<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);
});
});
}
}
}]);
回答by Matt
If you only need your CSS to be appliedto one specific view, I'm using this handy snippet inside my controller:
如果您只需要将 CSS应用于一个特定视图,我将在我的控制器中使用这个方便的代码段:
$("body").addClass("mystate");
$scope.$on("$destroy", function() {
$("body").removeClass("mystate");
});
This will add a class to my body
tag when the state loads, and remove it when the state is destroyed (i.e. someone changes pages). This solves my related problem of only needing CSS to be applied to one state in my application.
这将body
在状态加载时向我的标签添加一个类,并在状态被破坏(即有人更改页面)时将其删除。这解决了我只需要将 CSS 应用于我的应用程序中的一种状态的相关问题。
回答by rambaburoja
'use strict'; angular.module('app') .run( [ '$rootScope', '$state', '$stateParams', function($rootScope, $state, $stateParams) { $rootScope.$state = $state; $rootScope.$stateParams = $stateParams; } ] ) .config( [ '$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
'使用严格'; 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'
]
});
}
]
}
})
);