Jaki jest prawidłowy / akceptowany sposób używania oddzielnych arkuszy stylów dla różnych widoków używanych w mojej aplikacji?
Obecnie umieszczam element link w kodzie HTML widoku / częściowej na górze, ale powiedziano mi, że jest to zła praktyka, mimo że wszystkie współczesne przeglądarki go obsługują, ale widzę, dlaczego jest niezadowolony.
Inną możliwością jest umieszczenie oddzielnych arkuszy stylów w pliku index.html head
ale chciałbym, aby ładował arkusz stylów tylko wtedy, gdy jego widok jest ładowany w nazwie wydajności.
Czy jest to zła praktyka, ponieważ stylizacja nie zacznie obowiązywać, dopóki css nie zostanie załadowany z serwera, co prowadzi do szybkiego flashowania niesformatowanej zawartości w powolnej przeglądarce? Nie byłem jeszcze tego świadkiem, chociaż testuję to lokalnie.
Czy istnieje sposób na załadowanie CSS przez obiekt przekazany do Angulara? $routeProvider.when
?
Z góry dziękuję!
źródło
<link>
tagów css w tym formacie , z najnowszym Chrome, serwerem na moim komputerze lokalnym (i włączonym „Wyłącz pamięć podręczną”, aby zasymulować warunki „pierwszego ładowania”). Wyobrażam sobie, że wstępne wstawienie<style>
tagu w części html na serwerze pozwoliłoby uniknąć tego problemu.Odpowiedzi:
Wiem, że to pytanie jest stare, ale po przeprowadzeniu wielu badań nad różnymi rozwiązaniami tego problemu, myślę, że mogłem znaleźć lepsze rozwiązanie.
Na wypadek, gdyby ktoś w przyszłości był zainteresowany, oto co wymyśliłem:
1. Utwórz niestandardową dyrektywę dla
<head>
elementu: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; }); } }); } }; } ]);
Ta dyrektywa ma następujące znaczenie:
$compile
) ciąg html, który tworzy zestaw<link />
tagów dla każdego elementu wscope.routeStyles
obiekcie przy użyciung-repeat
ing-href
.<link />
elementów do<head>
znacznika.$rootScope
do nasłuchiwania'$routeChangeStart'
zdarzeń. Przy każdym'$routeChangeStart'
zdarzeniu pobiera „bieżący”$$route
obiekt (trasę, którą użytkownik ma zamiar opuścić) i usuwa ze<head>
znacznika jego częściowo specyficzne pliki css . Przechwytuje również „następny”$$route
obiekt (trasę, do której ma się udać użytkownik) i dodaje do<head>
znacznika dowolny ze swoich częściowo specyficznych plików css .ng-repeat
część zebranych<link />
uchwyty znaczników wszystkich dodawanie i usuwanie arkuszy stylów strona specyficznych oparciu o to, co dostaje dodawane lub usuwane zscope.routeStyles
obiektu.Uwaga: wymaga to, aby twój
ng-app
atrybut znajdował się na<html>
elemencie, a nie na<body>
lub cokolwiek wewnątrz<html>
.2. Określ, które arkusze stylów należą do których tras, używając
$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'] }) }]);
Ta konfiguracja dodaje
css
właściwość niestandardową do obiektu, który jest używany do konfigurowania trasy każdej strony. Ten obiekt jest przekazywany do każdego'$routeChangeStart'
zdarzenia jako.$$route
. Tak więc podczas słuchania'$routeChangeStart'
zdarzenia możemy pobrać określoną przez nascss
właściwość i<link />
w razie potrzeby dołączyć / usunąć te tagi. Zauważ, że określeniecss
właściwości na trasie jest całkowicie opcjonalne, ponieważ zostało pominięte w'/some/route/2'
przykładzie. Jeśli trasa nie macss
właściwości,<head>
dyrektywa po prostu nie zrobi nic dla tej trasy. Zauważ również, że możesz mieć nawet wiele arkuszy stylów specyficznych dla strony na trasę, jak w'/some/route/3'
powyższym przykładzie, gdziecss
właściwość jest tablicą względnych ścieżek do arkuszy stylów potrzebnych dla tej trasy.3. Gotowe Te dwie rzeczy konfigurują wszystko, co było potrzebne, i moim zdaniem robi to z możliwie najczystszym kodem.
Mam nadzieję, że pomoże to komuś innemu, kto może borykać się z tym problemem tak samo jak ja.
źródło
index.html
pliku. Zatem w powyższym przykładzieindex.html
znajdowałby się w katalogu głównym, acss
folder w katalogu głównym, zawierający wszystkie pliki css. ale możesz dowolnie kształtować strukturę aplikacji, o ile używasz prawidłowych ścieżek względnych.angular.forEach(current.$$route.css, function(sheet){ delete scope.routeStyles[sheet]; });
.Rozwiązanie @ tennisgent jest świetne. Myślę jednak, że jest trochę ograniczony.
Modułowość i enkapsulacja w Angular wykraczają poza trasy. Biorąc pod uwagę sposób, w jaki sieć zmierza w kierunku rozwoju opartego na komponentach, ważne jest, aby zastosować to również w dyrektywach.
Jak już wiesz, w Angular możemy dołączyć szablony (strukturę) i kontrolery (zachowanie) do stron i komponentów. AngularCSS umożliwia ostatni brakujący element: dołączanie arkuszy stylów (prezentacja).
Aby uzyskać pełne rozwiązanie, sugeruję użycie AngularCSS.
ng-app
w<html>
tagu. Jest to ważne, gdy na tej samej stronie działa wiele aplikacjihttps://github.com/door3/angular-css
Oto kilka przykładów:
Trasy
$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' } ] });
Dyrektywy
myApp.directive('myDirective', function () { return { restrict: 'E', templateUrl: 'my-directive/my-directive.html', css: 'my-directive/my-directive.css' } });
Dodatkowo możesz skorzystać z
$css
usługi dla skrajnych przypadków: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(); });
Możesz przeczytać więcej o AngularCSS tutaj:
http://door3.com/insights/introducing-angularcss-css-demand-angularjs
źródło
Można dołączyć nowy arkusz stylów, aby wejść do środka
$routeProvider
. Dla uproszczenia używam łańcucha, ale mógłbym również utworzyć nowy element łącza lub utworzyć usługę dla arkuszy stylów/* 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">'); }
Największą zaletą wstępnego kodowania na stronie jest to, że obrazy tła będą już istniały i mniej prawdopodobne
FOUC
źródło
<link>
w<head>
od index.html statycznie, chociaż?when
trasy. Można umieścić ten kod wcontroller
wywołaniu zwrotnym wwhen
ramachrouteProvider
lub być może wresolve
wywołaniu zwrotnym, które prawdopodobnierouteprovider
dodasz go do ... ten komentarz dotyczył umieszczania go w nagłówku strony głównej, gdy strona jest wyświetlana@ sz3, dość zabawne dzisiaj musiałem zrobić dokładnie to, co próbujesz osiągnąć: „ załaduj określony plik CSS tylko wtedy, gdy użytkownik uzyska dostęp ” do określonej strony. Więc użyłem powyższego rozwiązania.
Ale jestem tutaj, aby odpowiedzieć na twoje ostatnie pytanie: „ gdzie dokładnie mam umieścić kod. Jakieś pomysły ?
Miałeś rację, włączając kod do rozwiązania , ale musisz nieco zmienić format.
Spójrz na poniższy kod:
.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">'); } } } })
Właśnie przetestowałem i działa dobrze , wstrzykuje html i ładuje mój „home.css” tylko wtedy, gdy trafiam na trasę „/ home”.
Pełne wyjaśnienie można znaleźć tutaj , ale w zasadzie rozwiązanie: należy pobrać obiekt w formacie
{ 'key' : string or function() }
Możesz nazwać „ klucz ” jak tylko chcesz - w moim przypadku nazwałem „ styl ”.
Następnie dla wartości masz dwie opcje:
Jeśli jest to ciąg znaków , jest to alias usługi.
Jeśli jest to funkcja , zostaje wstrzyknięta, a wartość zwracana jest traktowana jako zależność.
Głównym celem jest to, że kod wewnątrz funkcji zostanie wykonany przed utworzeniem instancji kontrolera i wywołaniem zdarzenia $ routeChangeSuccess.
Mam nadzieję, że to pomoże.
źródło
Wspaniale! Dziękuję!! Musiałem tylko wprowadzić kilka poprawek, aby działał z routerem 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); }); }); } } }]);
źródło
Jeśli potrzebujesz tylko swojego CSS do zastosowania w jednym określonym widoku, używam tego przydatnego fragmentu w moim kontrolerze:
$("body").addClass("mystate"); $scope.$on("$destroy", function() { $("body").removeClass("mystate"); });
Spowoduje to dodanie klasy do mojego
body
tagu, gdy stan się załaduje, i usunie ją, gdy stan zostanie zniszczony (np. Ktoś zmieni strony). To rozwiązuje mój problem związany z potrzebą zastosowania CSS tylko do jednego stanu w mojej aplikacji.źródło
„użyj ścisłego”; angular.module ('app') .run (['$ rootScope', '$ state', '$ stateParams', function ($ rootScope, $ state, $ stateParams) {$ rootScope. $ state = $ state; $ rootScope . $ stateParams = $ stateParams;}]) .config (['$ stateProvider', '$ urlRouterProvider', 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' ] }); } ] } }) );
źródło