css 如何在AngularJS中包含视图/局部特定样式?

bt1cpqcv  于 2022-12-20  发布在  Angular
关注(0)|答案(7)|浏览(154)

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

目前我在视图/部分的html顶部放置了一个link元素,但我被告知这是一个糟糕的做法,尽管所有现代浏览器都支持它,但我可以理解为什么它不受欢迎。
另一种可能性是将单独的样式表放在我的index.html的head中,但我希望它只在视图以性能的名义加载时才加载样式表。
这是不是一个糟糕的做法,因为样式直到css从服务器加载后才会生效,导致在一个缓慢的浏览器中快速闪现未格式化的内容?虽然我在本地测试它,但我还没有看到这一点。
有没有办法通过传递给Angular的$routeProvider.when的对象来加载CSS?

7hiiyaii

7hiiyaii1#

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

**更新1:**自从发布这个答案后,我已经将所有这些代码添加到了一个简单的服务中,我已经发布到了GitHub上。repo位于here。请随时查看更多信息。
**UPDATE 2:**如果您只需要一个轻量级的解决方案来为路线拉入样式表,那么这个答案非常好。如果您想要一个更完整的解决方案来管理整个应用程序中的按需样式表,那么您可能需要 checkout 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.它编译(使用$compile)一个html字符串,该字符串使用ng-repeatng-hrefscope.routeStyles对象中的每个项目创建一组<link />标记。
1.它将编译后的<link />元素集附加到<head>标记。
1.然后,它使用$rootScope侦听'$routeChangeStart'事件。它获取“当前”$$route对象(用户将要离开的路线)并删除其部分特定的css文件它还获取“next”$$route对象(用户将要前往的路线),并将其任何特定于partial的css文件添加到<head>标记。
1.编译后的<link />标记的ng-repeat部分根据添加到scope.routeStyles对象或从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']
        })
}]);

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

3.你已经完成了这两件事设置了所有需要的东西,在我看来,它用尽可能干净的代码做到了这一点。

希望这能帮助其他可能和我一样在这个问题上苦苦挣扎的人。

ig9co6j1

ig9co6j12#

@tennisgent的解决方案很棒,不过,我觉得有点局限。
模块化和Angular 封装超越了路径,基于web正在向基于组件的开发发展的方式,在指令中应用这一点也很重要。
正如您所知,在Angular中,我们可以在页面和组件中包含模板(结构)和控制器(行为)。AngularCSS启用了最后一个缺失的部分:附加样式表(演示文稿)。
对于完整的解决方案,我建议使用AngularCSS。
1.支持Angular的ngRoute、UI路由器、指令、控制器和服务。
1.不需要在<html>标签中包含ng-app。当您在同一页面上运行多个应用程序时,这一点很重要
1.您可以自定义样式表的注入位置:头部、主体、自定义选择器等...
1.支持预加载、持久化和缓存破坏
1.通过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

ssgvzors

ssgvzors3#

可以在$routeProvider中的head后添加一个新的样式表。为了简单起见,我使用了一个字符串,但是也可以创建一个新的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

gudnpqoy

gudnpqoy4#

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

我刚刚测试过,它运行良好,它注入html,只有当我点击“/home”路径时才加载我的“home.css”。

完整的说明可以在here中找到,但基本上**resolve:**应该获得以下格式的对象

{
  'key' : string or function()
}

您可以随意命名“key”-在我的例子中,我称之为“style”。
对于值,您有两个选项:

  • 如果它是一个字符串,那么它是一个服务的别名。
  • 如果它是function,那么它被注入,返回值被视为依赖项。

这里的要点是,函数内部的代码将在控制器示例化和$routeChangeSuccess事件触发之前执行。
希望能有所帮助。

uklbhaso

uklbhaso5#

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

mefy6pfw6#

如果您只需要将CSS * 应用于 * 一个特定视图,我将在控制器中使用以下代码片段:

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

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

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

k7fdbhmy

k7fdbhmy7#

“使用严格”;模块(“应用程序”).运行([“$根范围”,“$状态”,“$状态参数”,函数($根范围,$状态,$状态参数){ $根范围.$状态= $状态;$根范围。$状态参数= $状态参数;} ]).config([ '$状态提供程序','$urlRouterProvider',函数($状态提供程序,$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'
                                    ]
                                });
                            }
                        ]
                    }
                })
                 );

相关问题