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

Bra*_*don 131 css model-view-controller angularjs

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

目前我在顶部的view/partial的html中放置一个链接元素,但我被告知这是不好的做法,即使所有现代浏览器都支持它,但我可以看到为什么它不赞成.

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

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

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

提前致谢!

ten*_*ent 150

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

更新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;
                        });
                    }
                });
            }
        };
    }
]);
Run Code Online (Sandbox Code Playgroud)

该指令执行以下操作:

  1. 它编译(使用$compile)一个html字符串,使用和<link />scope.routeStyles对象中的每个项创建一组标记.ng-repeatng-href
  2. 它将编译的<link />元素集附加到<head>标记.
  3. 然后它使用它$rootScope来监听'$routeChangeStart'事件.对于每个'$routeChangeStart'事件,它都会抓取"当前" $$route对象(用户即将离开的路径)并从<head>标记中删除其部分特定的css文件.它还抓取"下一个" $$route对象(用户即将前往的路径),并将其任何部分特定的css文件添加到<head>标记中.
  4. ng-repeat编译<link />标记的一部分根据添加到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']
        })
}]);
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 圣洁的莫莉,谢谢你!正是我在寻找:).刚刚测试它,它完美地工作(加上易于实现).也许你应该为此创建一个pull请求并将其放入核心.我知道AngularJS的人正在研究范围内的CSS,这可能是朝着正确方向迈出的一步? (2认同)

cas*_*.io 34

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

Angular中的Modularity和Encapsulation超越了路径.基于Web向基于组件的开发的方式,将其应用于指令也很重要.

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

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

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

指令

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

此外,您可以将$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();

});
Run Code Online (Sandbox Code Playgroud)

您可以在此处阅读有关AngularCSS的更多信息:

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


cha*_*tfl 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">');
}
Run Code Online (Sandbox Code Playgroud)

在页面中预先编码的最大好处是任何背景图像都已经存在,而且更少的谎言 FOUC


Den*_*Luz 5

@sz3,今天很有趣,我必须完全按照您的目的去做:“仅当用户访问”特定页面时才加载特定的 CSS 文件。所以我使用了上面的解决方案。

但我在这里回答你的最后一个问题:'我应该把代码放在哪里。有什么想法吗?

您是对的,将代码包含在resolve 中,但您需要稍微更改格式。

看看下面的代码:

.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">');
      }
    }
  }
})
Run Code Online (Sandbox Code Playgroud)

我刚刚测试过,它工作正常,它注入 html 并仅在我点击“/home”路线时加载我的“home.css”。

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

{
  'key' : string or function()
} 
Run Code Online (Sandbox Code Playgroud)

您可以将“”命名为任何您喜欢的名称——在我的例子中,我称之为“样式”。

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

  • 如果它是string,则它是服务的别名。

  • 如果它是function,则将其注入并将返回值视为依赖项。

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

希望有帮助。