Cách bao gồm kiểu xem / kiểu cụ thể một phần trong AngularJS


132

Cách thích hợp / được chấp nhận để sử dụng các bảng định kiểu riêng biệt cho các chế độ xem khác nhau mà ứng dụng của tôi sử dụng là gì?

Hiện tại tôi đang đặt một yếu tố liên kết trong chế độ xem / một phần html ở trên cùng nhưng tôi được cho biết đây là thực tiễn tồi mặc dù tất cả các trình duyệt hiện đại đều hỗ trợ nó nhưng tôi có thể thấy tại sao nó lại nhăn mặt.

Khả năng khác là đặt các bảng định kiểu riêng biệt trong index.html của headtôi nhưng tôi muốn nó chỉ tải biểu định kiểu nếu chế độ xem của nó được tải theo tên hiệu suất.

Đây có phải là cách làm tồi tệ vì kiểu dáng sẽ không có hiệu lực cho đến khi css được tải từ máy chủ, dẫn đến một nội dung nhanh không được định dạng trong trình duyệt chậm? Tôi vẫn chưa chứng kiến ​​điều này mặc dù tôi đang thử nghiệm nó tại địa phương.

Có cách nào để tải CSS thông qua đối tượng được truyền cho Angular $routeProvider.whenkhông?

Cảm ơn trước!


Tôi xác nhận xác nhận "flash nhanh của nội dung chưa được định dạng" của bạn. Tôi đã sử dụng <link>thẻ css ở định dạng này , với Chrome mới nhất, máy chủ trên máy cục bộ của tôi (và "Tắt bộ nhớ cache" để mô phỏng các điều kiện "tải đầu tiên"). Tôi tưởng tượng rằng việc chèn trước một <style>thẻ trong phần html trên máy chủ sẽ tránh được vấn đề này.
tốt nhất

Câu trả lời:


150

Tôi biết câu hỏi này đã cũ, nhưng sau khi thực hiện rất nhiều nghiên cứu về các giải pháp khác nhau cho vấn đề này, tôi nghĩ rằng tôi có thể đã đưa ra một giải pháp tốt hơn.

CẬP NHẬT 1: Kể từ khi đăng câu trả lời này, tôi đã thêm tất cả các mã này vào một dịch vụ đơn giản mà tôi đã đăng lên GitHub. Các repo được đặt ở đây . Hãy kiểm tra nó để biết thêm.

CẬP NHẬT 2: Câu trả lời này là tuyệt vời nếu tất cả những gì bạn cần là một giải pháp nhẹ để kéo các bảng định kiểu cho các tuyến đường của bạn. Nếu bạn muốn có một giải pháp hoàn chỉnh hơn để quản lý các bảng định kiểu theo yêu cầu trong toàn bộ ứng dụng của mình, bạn có thể muốn kiểm tra dự án AngularCSS của Door3 . Nó cung cấp nhiều chức năng chi tiết hơn.

Trong trường hợp bất kỳ ai trong tương lai quan tâm, đây là những gì tôi nghĩ ra:

1. Tạo một lệnh tùy chỉnh cho <head>phần tử:

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

Lệnh này thực hiện những điều sau đây:

  1. Nó biên dịch (sử dụng $compile) một chuỗi html tạo ra một tập hợp các <link />thẻ cho mọi mục trong scope.routeStylesđối tượng sử dụng ng-repeatng-href.
  2. Nó nối thêm tập hợp các <link />phần tử vào <head>thẻ.
  3. Sau đó, nó sử dụng $rootScopeđể lắng nghe các '$routeChangeStart'sự kiện. Đối với mọi '$routeChangeStart'sự kiện, nó lấy $$routeđối tượng "hiện tại" (tuyến đường mà người dùng sắp rời đi) và xóa (các) tệp css cụ thể một phần khỏi <head>thẻ. Nó cũng lấy $$routeđối tượng "tiếp theo" (tuyến đường mà người dùng sắp đi) và thêm bất kỳ (các) tệp css cụ thể nào của nó vào <head>thẻ.
  4. ng-repeatmột phần của <link />thẻ đã biên dịch xử lý tất cả việc thêm và xóa các biểu định kiểu dành riêng cho trang dựa trên những gì được thêm vào hoặc xóa khỏi scope.routeStylesđối tượng.

Lưu ý: điều này đòi hỏi ng-appthuộc tính của bạn nằm trên <html>phần tử, không phải trên <body>hoặc bất cứ thứ gì bên trong <html>.

2. Chỉ định biểu định kiểu nào thuộc về tuyến nào sử dụng $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']
        })
}]);

Cấu hình này thêm một thuộc tính tùy chỉnh csscho đối tượng được sử dụng để thiết lập tuyến đường của mỗi trang. Đối tượng đó được truyền cho mỗi '$routeChangeStart'sự kiện như .$$route. Vì vậy, khi nghe '$routeChangeStart'sự kiện, chúng tôi có thể lấy thuộc csstính mà chúng tôi đã chỉ định và chắp thêm / xóa các <link />thẻ đó khi cần. Lưu ý rằng việc chỉ định một thuộc csstính trên tuyến là hoàn toàn tùy chọn, vì nó đã bị bỏ qua khỏi '/some/route/2'ví dụ. Nếu tuyến đường không có csstài sản, <head>chỉ thị sẽ không làm gì cho tuyến đường đó. Cũng lưu ý rằng bạn thậm chí có thể có nhiều bảng định kiểu dành riêng cho mỗi tuyến, như trong '/some/route/3'ví dụ trên, trong đó thuộc csstính là một mảng các đường dẫn tương đối đến các bảng định kiểu cần thiết cho tuyến đó.

3. Bạn đã hoàn thành Hai điều đó thiết lập mọi thứ cần thiết và theo ý tôi, với mã sạch nhất có thể.

Hy vọng rằng sẽ giúp người khác có thể đang đấu tranh với vấn đề này nhiều như tôi.


2
Thánh moly, cảm ơn vì điều này! Chính xác những gì tôi đang tìm kiếm :). Chỉ cần thử nghiệm nó bây giờ và nó hoạt động hoàn hảo (cộng với dễ thực hiện). Có lẽ bạn nên tạo một yêu cầu kéo cho việc này và đưa nó vào cốt lõi. Tôi biết những người của AngularJS đang tìm kiếm css trong phạm vi, đây có thể là một bước đi đúng hướng?
smets.kevin

Những kẻ đó thông minh hơn tôi nhiều. Tôi chắc chắn rằng họ đã nghĩ đến giải pháp này (hoặc một giải pháp tương tự) trước đây và đã chọn không triển khai nó vào cốt lõi vì bất kỳ lý do gì.
quần vợt

Vị trí chính xác cho tập tin css là gì? Có css: 'css / part1.css' ngụ ý thư mục css trong thư mục gốc của ứng dụng góc?
Cordle

Nó liên quan đến index.htmltập tin của bạn . Vì vậy, trong ví dụ trên, index.htmlsẽ ở cssthư mục gốc và thư mục sẽ ở thư mục gốc, chứa tất cả các tệp css. nhưng bạn có thể cấu trúc ứng dụng của mình theo cách bạn muốn, miễn là bạn sử dụng các đường dẫn tương đối chính xác.
quần vợt

1
@Kappys, tập lệnh sẽ xóa kiểu cho chế độ xem trước đó khi bạn chuyển sang chế độ xem mới. Nếu bạn không muốn điều đó xảy ra, chỉ cần xóa đoạn mã sau khỏi lệnh : angular.forEach(current.$$route.css, function(sheet){ delete scope.routeStyles[sheet]; });.
quần vợt

34

@ giải pháp của tennisgent là tuyệt vời. Tuy nhiên, tôi nghĩ là một chút hạn chế.

Tính mô đun và đóng gói trong Angular vượt ra ngoài các tuyến đường. Dựa trên cách web chuyển sang phát triển dựa trên thành phần, điều quan trọng là phải áp dụng điều này trong các chỉ thị.

Như bạn đã biết, trong Angular chúng ta có thể bao gồm các mẫu (cấu trúc) và bộ điều khiển (hành vi) trong các trang và thành phần. AngularCSS cho phép phần còn thiếu cuối cùng: đính kèm bảng định kiểu (bản trình bày).

Đối với một giải pháp đầy đủ, tôi đề nghị sử dụng AngularCSS.

  1. Hỗ trợ ngRoute, Bộ định tuyến UI, chỉ thị, bộ điều khiển và dịch vụ của Angular.
  2. Không bắt buộc phải có ng-apptrong <html>thẻ. Điều này rất quan trọng khi bạn có nhiều ứng dụng đang chạy trên cùng một trang
  3. Bạn có thể tùy chỉnh vị trí của các bảng định kiểu: đầu, thân, bộ chọn tùy chỉnh, v.v ...
  4. Hỗ trợ tải trước, lưu trữ lâu bền và lưu trữ bộ đệm
  5. Hỗ trợ truy vấn phương tiện và tối ưu hóa tải trang thông qua API matchMedia

https://github.com/door3/angular-css

Dưới đây là một số ví dụ:

Tuyến đường

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

Chỉ thị

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

Ngoài ra, bạn có thể sử dụng $cssdịch vụ cho các trường hợp cạnh:

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();

});

Bạn có thể đọc thêm về AngularCSS tại đây:

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


1
Tôi thực sự thích cách tiếp cận của bạn ở đây, nhưng đã tự hỏi làm thế nào nó có thể được sử dụng trong một ứng dụng sản xuất trong đó tất cả các kiểu css cần được nối với nhau? Đối với các mẫu html, tôi sử dụng $ templateCache.put () cho mã sản xuất và thật tuyệt khi làm điều gì đó tương tự cho css.
Tom Makin

Nếu bạn cần lấy CSS được nối từ máy chủ, bạn luôn có thể thực hiện một số thứ như /getCss?files=file1(.css),file2,file3 và máy chủ sẽ phản hồi với cả 3 tệp theo thứ tự nhất định và được nối.
Petr Urban

13

Có thể nối một biểu định kiểu mới vào trong $routeProvider. Để đơn giản, tôi đang sử dụng một chuỗi nhưng cũng có thể tạo thành phần liên kết mới hoặc tạo một dịch vụ cho biểu định kiểu

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

Lợi ích lớn nhất của việc prelaoding trong trang là bất kỳ hình ảnh nền nào cũng sẽ tồn tại và ít nói dối hơn FOUC


Tuy nhiên, điều này sẽ không hoàn thành điều tương tự như chỉ bao gồm <link>trong phần <head>của index.html tĩnh chứ?
Brandon

không nếu whentuyến đường chưa được gọi. Có thể đặt mã này trong controllercuộc gọi lại whentrong routeProviderhoặc có thể trong resolvecuộc gọi lại có khả năng kích hoạt sớm hơn
charlietfl

Oh okay, xấu của tôi, mà nhấp không. Trông khá chắc chắn ngoại trừ bạn có thể giải thích cách tải trước của nó nếu tôi tiêm nó khi nào?
Brandon

1
nó không tải trước nếu bạn thêm nó vào routeprovider... bình luận đó là về việc đưa nó vào đầu trang chính khi trang được phục vụ
charlietfl

-_- xin lỗi, tôi thiếu ngủ nếu bạn không thể nói. Dù sao, đó là nơi tôi đang ở hiện tại. Cố gắng tìm hiểu xem chi phí tải tất cả các bảng định kiểu của tôi cùng một lúc có tốt hơn so với việc có một số FOUC khi người dùng chuyển chế độ xem. Tôi đoán đó thực sự không phải là một câu hỏi liên quan đến Angular nhiều như về ứng dụng web UX. Mặc dù vậy, cảm ơn, có lẽ tôi sẽ đi với đề xuất của bạn nếu tôi quyết định không tải trước.
Brandon

5

@ sz3, đủ buồn cười hôm nay tôi đã phải làm chính xác những gì bạn đang cố gắng đạt được: 'chỉ tải một tệp CSS cụ thể khi người dùng truy cập ' một trang cụ thể. Vì vậy, tôi đã sử dụng giải pháp trên.

Nhưng tôi ở đây để trả lời câu hỏi cuối cùng của bạn: ' chính xác tôi nên đặt mã ở đâu. Có ý kiến gì không? '

Bạn đã đúng bao gồm cả mã vào giải quyết , nhưng bạn cần thay đổi một chút định dạng.

Hãy xem mã dưới đây:

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

Tôi vừa mới thử nghiệm và nó hoạt động tốt , nó tiêm html và nó tải 'home.css' của tôi chỉ khi tôi nhấn tuyến đường '/ home'.

Có thể tìm thấy giải thích đầy đủ ở đây , nhưng về cơ bản giải quyết: nên lấy một đối tượng ở định dạng

{
  'key' : string or function()
} 

Bạn có thể đặt tên cho ' khóa ' bất cứ điều gì bạn thích - trong trường hợp của tôi, tôi gọi là ' phong cách '.

Sau đó, đối với giá trị bạn có hai tùy chọn:

  • Nếu đó là một chuỗi , thì đó là bí danh cho một dịch vụ.

  • Nếu nó hoạt động , thì nó được thêm vào và giá trị trả về được coi là phụ thuộc.

Điểm chính ở đây là mã bên trong hàm sẽ được thực thi trước khi bộ điều khiển được khởi tạo và sự kiện $ routeChangeSuccess được kích hoạt.

Mong rằng sẽ giúp.


2

Tuyệt vời, cảm ơn bạn !! Chỉ cần thực hiện một vài điều chỉnh để làm cho nó hoạt động với 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);
                });
            });
        }
    }
}]);

Tôi thực sự không cần gỡ bỏ và thêm ở mọi nơi vì css của tôi đã bị rối, nhưng điều này rất hữu ích với ui-router! Cảm ơn :)
imsheth 14/03/2016

1

Nếu bạn chỉ cần áp dụng CSS của mình cho một chế độ xem cụ thể, tôi đang sử dụng đoạn mã tiện dụng này trong bộ điều khiển của mình:

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

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

Điều này sẽ thêm một lớp vào bodythẻ của tôi khi trạng thái tải và xóa nó khi trạng thái bị hủy (tức là có người thay đổi trang). Điều này giải quyết vấn đề liên quan của tôi khi chỉ cần CSS được áp dụng cho một trạng thái trong ứng dụng của tôi.


0

'Sử dụng nghiêm ngặt'; angular.module ('app') .run (['$ rootScope', '$ state', '$ stateParams', function ($ rootScope, $ state, $ stateParams) {$ rootScope. $ state = $ state; $ rootScope . $ stateParams = $ stateParams;}]) .config (['$ stateProvider', '$ urlRouterProvider', chức năng ($ 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'
                                    ]
                                });
                            }
                        ]
                    }
                })
                 );

Một ví dụ mã đơn giản không có ngữ cảnh hiếm khi là một câu trả lời đủ cho một câu hỏi. Hơn nữa, câu hỏi này đã có một câu trả lời được chấp nhận cao.
AJ X.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.