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

时间:2013-03-04 02:09:07

标签: css model-view-controller angularjs

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

目前我在顶部的view / partial的html中放置了一个链接元素,但我被告知这是不好的做法,即使所有现代浏览器都支持它,但我明白为什么它很不满意。

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

这是不好的做法,因为样式在服务器加载css之后才会生效,导致在慢速浏览器中快速刷新未格式化的内容?我还没有看到这个,虽然我在本地测试它。

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

提前致谢!

7 个答案:

答案 0 :(得分:148)

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

  

更新1:自发布此答案以来,我已将所有此代码添加到我发布到GitHub的简单服务中。回购邮件位于here。请随时查看更多信息。

     

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

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

<强> 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. 使用<link />scope.routeStyles编译(使用$compile)html字符串,为ng-repeat对象中的每个项目创建一组ng-href个标记
  2. 它将已编译的<link />元素集附加到<head>标记。
  3. 然后使用$rootScope来监听'$routeChangeStart'个事件。对于每个'$routeChangeStart'事件,它会抓取“当前”$$route对象(用户即将离开的路径)并从<head>中删除其部分特定的css文件标签。它还抓取“下一个”$$route对象(用户即将前往的路径),并将其任何部分特定的css文件添加到<head>标记。
  4. 编译后的ng-repeat标记的<link />部分根据添加到scope.routeStyles对象或从ng-app对象中删除的内容处理所有添加和删除特定于页面的样式表。
  5. 注意: 这要求您的<html>元素位于<body>元素上,而不是<html>$routeProvider内的任何内容}。

    <强> 2。使用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

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

    第3。你已经完成了 这两件事设置了所需的一切,在我看来,它可以用最干净的代码来实现。

    希望能帮助那些可能像我一样努力解决这个问题的人。

答案 1 :(得分:34)

@ tennisgent的解决方案很棒。但是,我觉得有点限制。

Angular中的模块化和封装超越了路径。基于Web向基于组件的开发的方式,将其应用于指令也很重要。

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

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

  1. 支持Angular的ngRoute,UI路由器,指令,控制器和服务。
  2. ng-app标记中不需要<html>。当您在同一页面上运行多个应用时,这很重要
  3. 您可以自定义样式表的注入位置:头部,正文,自定义选择器等......
  4. 支持预加载,持久化和缓存清除
  5. 支持媒体查询并通过matchMedia API优化页面加载
  6. 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

答案 2 :(得分:13)

可以在$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">');
}

页面中预先编码的最大好处是任何背景图像都已存在,并且FOUC

的谎言较少

答案 3 :(得分:4)

@ 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'。

可以找到here的完整说明,但基本上解决:应该以格式

获取对象
{
  'key' : string or function()
} 

您可以将“”命名为您喜欢的任何内容 - 就我而言,我称之为“风格”。

然后,对于该值,您有两个选项:

  • 如果是字符串,则它是服务的别名。

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

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

希望有所帮助。

答案 4 :(得分:2)

太棒了,谢谢!只需进行一些调整就可以使用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);
                });
            });
        }
    }
}]);

答案 5 :(得分:0)

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

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

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

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

答案 6 :(得分:0)

&#39;使用严格的&#39 ;; angular.module(&#39;应用&#39;)     。跑(         [             &#39; $ rootScope&#39;,&#39; $ state&#39;,&#39; $ stateParams&#39;,             function($ rootScope,$ state,$ stateParams){                 $ rootScope。$ state = $ state;                 $ rootScope。$ stateParams = $ stateParams;             }         ]     )     的.config(         [             &#39; $ stateProvider&#39;,&#39; $ urlRouterProvider&#39;,             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'
                                    ]
                                });
                            }
                        ]
                    }
                })
                 );