如何在 AngularJS 中包含视图/部分特定样式

对应用程序使用的各种视图使用单独的样式表的正确/可接受的方法是什么?

目前,我把一个 link 元素放在 view/part 的 html 的顶部,但是我被告知这是不好的做法,尽管所有的现代浏览器都支持它,但是我可以理解为什么它不受欢迎。

另一种可能性是将单独的样式表放在 index.html 的 head中,但是我希望它只加载以性能名义加载其视图的样式表。

这是不是一个坏习惯,因为样式直到从服务器加载 css 之后才会生效,导致在缓慢的浏览器中快速闪过未格式化的内容?虽然我在本地进行测试,但我还没有亲眼目睹。

有没有办法通过传递给 Angular 的 $routeProvider.when的对象来加载 CSS?

先谢谢你!

104547 次浏览

可以在 $routeProvider中附加一个新的样式表。为简单起见,我使用字符串,但也可以创建新的 link 元素,或者为样式表创建服务

/* 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

@ sz3,有趣的是,今天我不得不做的正是你试图实现的: ‘ 仅当用户访问 时才加载特定的 CSS 文件’一个特定的页面。所以我用了上面的方法。

但我在这里回答你的最后一个问题: “ 我到底应该把代码放在哪里。任何想法?”

将代码包含到 决心中是正确的,但是您需要稍微改变一下格式。

看看下面的代码:

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

我刚刚测试过,它工作得很好 ,它注入了 html 并且只有当我到达“/home”路径时才加载我的‘ home.css’。

完整的解释可以找到 给你,但基本上 解决:应该得到一个对象的格式

{
'key' : string or function()
}

你可以命名的‘ 钥匙’任何你喜欢-在我的情况下,我称为’风格’。

那么对于价值,你有两个选择:

  • 如果它是 绳子,那么它就是服务的别名。

  • 如果是 功能,则注入它并处理返回值 作为依赖项

这里的要点是,在实例化控制器和触发 $routeChangeSuccess 事件之前,将执行函数中的代码。

希望能帮上忙。

我知道这个问题现在已经过时了,但是在对这个问题的各种解决方案做了大量研究之后,我想我可能已经找到了一个更好的解决方案。

更新1: 自从发布这个答案以来,我已经将所有这些代码添加到我发布到 GitHub 的一个简单服务中。回购位于 给你。想知道更多信息,请随意查看。

更新2: 如果您只需要一个轻量级的解决方案来为您的路线提供样式表,那么这个答案非常棒。如果您想要在整个应用程序中管理随需应变样式表的更完整的解决方案,您可能需要签出 Door3的 AngularCSS 项目。它提供了更细粒度的功能。

如果将来有人感兴趣的话,这是我想到的:

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

本指令的作用如下:

  1. 它编译(使用 $compile)一个 html 字符串,该字符串使用 ng-repeatng-hrefscope.routeStyles对象中的每个项目创建一组 <link />标记。
  2. 它将编译后的一组 <link />元素附加到 <head>标记。
  3. 然后它使用 $rootScope来侦听 '$routeChangeStart'事件。对于每个 '$routeChangeStart'事件,它获取“当前”$$route对象(用户即将离开的路由) ,并从 <head>标记中删除其部分特定的 css 文件。它还获取“ next”$$route对象(用户即将访问的路由) ,并将其部分特定的 css 文件添加到 <head>标记中。
  4. 编译后的 <link />标记的 ng-repeat部分根据添加到 scope.routeStyles对象或从 scope.routeStyles对象中删除的内容,处理特定于页面的样式表的所有添加和删除。

注意: < em > 这要求您的 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']
})
}]);

此配置将一个自定义 css属性添加到用于设置每个页面路由的对象中。该对象作为 .$$route传递给每个 '$routeChangeStart'事件。因此,当侦听 '$routeChangeStart'事件时,我们可以获取我们指定的 css属性,并根据需要附加/删除那些 <link />标记。注意,在路由上指定 css属性是完全可选的,因为它在 '/some/route/2'示例中被省略了。如果路由没有 css属性,那么 <head>指令将不对该路由执行任何操作。还要注意,您甚至可以在每个路由中拥有多个特定于页面的样式表,如上面的 '$routeChangeStart'0示例所示,其中 css属性是到该路由所需的样式表的相对路径数组。

3你完了 这两样东西设置了所需的所有东西,在我看来,它使用了尽可能干净的代码。

希望这能帮到其他和我一样为这个问题苦苦挣扎的人。

太棒了,谢谢! ! 只是需要做一些调整,让它与 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);
});
});
}
}
}]);

@ tennisgent 的解决方案很棒,不过我觉得有点局限。

模块化和角度封装超越了路线。基于 Web 正朝着基于组件的开发方向发展的方式,在指令中应用这一点也很重要。

正如你已经知道的,在 Angular 中,我们可以在页面和组件中包括模板(结构)和控制器(行为)。AngularCSS启用最后缺少的部分: 附加样式表(演示文稿)。

对于一个完整的解决方案,我建议使用 AngularCSS。

  1. 支持 Angular 的 ngRoute、 UI 路由器、指令、控制器和服务。
  2. 不需要在 <html>标签中包含 ng-app。当有多个应用程序在同一页面上运行时,这一点非常重要
  3. 您可以自定义样式表的注入位置: head、 body、 customselector 等等。
  4. 支持预加载、持久化和缓存崩溃
  5. 支持媒体查询,并通过 match Media 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

如果您只需要您的 CSS 是 应用到一个特定的视图,我在我的控制器中使用这个方便的代码片段:

$("body").addClass("mystate");


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

这将在状态加载时向我的 body标记添加一个类,并在状态被破坏时删除它(即有人更改了页面)。这解决了我的相关问题,只需要 CSS 被应用到我的应用程序中的一个状态。

「严格使用」 ; Module (‘ app’) 。运行( [ “ $rootScope”,“ $state”,“ $stateParams”, 函数($rootScope,$state,$stateParams){ $rootScope $state = $state; $rootScope. $stateParams = $stateParams; } ] ) . config ( [ “ $state Provider”“ $urlRouterProvider” 函数($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'
]
});
}
]
}
})
);