How to append a stylesheet to <head> in AngularJS $routeProvider?
Asked Answered
M

4

3

I want to load a specific CSS file only when a user accesses the contact.html view on my AngularJS app/site. I found this answer which almost made sense to me How to include view/partial specific styling in AngularJS The accepted answer by charlietfl reads :

Could append a new stylesheet to head within $routeProvider. For simplicity am using a string but could create new link element also, or create a service for stylesheets

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

Biggest benefit of prelaoding in page is any background images will already exist, and less lieklyhood of FOUC

The problem is that I do not know exactly where in my $routeProvider to add the code. charlietfl mentions where to put it in a comment which reads

not if the when for the route hasn't been called. Can put this code in controller callback of when within the routeProvider, or perhaps within resolve callback which likely triggers sooner

I have modified my $routeProvider following the advice. I added a resolve in the object following .when('/contact'. Here is my code

angular.module('maxmythicApp', ['ngResponsiveImages'])
  .config(function ($locationProvider, $routeProvider) {
    $locationProvider.html5Mode(true);
    $locationProvider.hashPrefix = '!';
    $routeProvider
      .when('/', {
        templateUrl: '/views/design.html',
        controller: 'MainCtrl'
      })
      .when('/design', {
        templateUrl: '/views/design.html',
        controller: 'MainCtrl'
      })
      .when('/about', {
        templateUrl: '/views/about.html',
        controller: 'MainCtrl'
      })
      .when('/contact', {
        templateUrl: '/views/contact.html',
        controller: 'MainCtrl',
        resolve: 
          if( !angular.element('link#myViewName').length){
            angular.element('head').append('<link id="myViewName" href="myViewName.css" rel="stylesheet">');
          }
      })
      .otherwise({
        redirectTo: '/'
      });
  });

Would this work?

Mancuso answered 28/8, 2013 at 14:32 Comment(2)
You have to use `$compile' to let angular know that you have to add something to the dom. This should automatically call $scope.$apply() which inturn would call $$digest. This should do the trick.Adulthood
Where exactly would I place $compile?Mancuso
C
9

I made a service for it.

Important part of the code :

var head = angular.element(document.querySelector('head')); // TO make the code IE < 8 compatible, include jQuery in your page and replace "angular.element(document.querySelector('head'))" by "angular.element('head')"

if(head.scope().injectedStylesheets === undefined)
{
    head.scope().injectedStylesheets = [];
    head.append($compile("<link data-ng-repeat='stylesheet in injectedStylesheets' data-ng-href='{{stylesheet.href}}' rel='stylesheet' />")(scope)); // Found here : https://mcmap.net/q/37070/-conditionally-rendering-css-in-html-head
}

head.scope().injectedStylesheets.push({href: "/url/to/style.css"});

Full code in Github : https://github.com/Yappli/angular-css-injector)

Cologarithm answered 8/10, 2013 at 20:34 Comment(0)
C
2

UPDATED: Here is the solution to inject(load) a specific CSS using the $routeProvider.

The solution described below is an alternative to apply different classes and page title based on the route which could be used in other situations.

For each route I've created a new key called 'bodyClass' and 'title' (but you could called anything you like it) and it looks like this:

'use strict';
var myApp = angular.module('myApp', 'ngResource'])
.config(function ($routeProvider) {

myApp.siteName = 'My Cool App';

$routeProvider
    .when('/home', {
     title:'Home - ' + myApp.siteName,
     bodyClass: 'home',
     templateUrl: 'views/home.html',
     controler: 'bmsHomeCtrl'
    })
    .when('/contact', {
      title:'Contact - ' + myApp.siteName,
      bodyClass: 'contact',
      templateUrl: 'views/contact.html',
      controler: 'bmsContactCtrl'
    })
    .otherwise({
      redirectTo: '/home'
    });
});

Then for each $routeChangeSuccess event I change the <title> of the page and also the class of the <body>.

myApp.run(['$location', '$rootScope', function($location, $rootScope) {
    $rootScope.$on('$routeChangeSuccess', function (event, current, previous) {

      if (current.$$route) {

        $rootScope.title = current.$$route.title;

        $rootScope.bodyClass = current.$$route.bodyClass;
      }
    });
}]);

You put the code above on the same main app.js (for example) file.

On my index.html page, which renders the views, I have the following codes to pick up the title and class:

<title>{{ title }}</title>

<body class="{{ bodyClass }}">

So if i visit the home page of my application the title tag will be

<tittle> Home - My Cool App</tittle>

and the body tag will be

<body class="home">

It's working like a charm.

I know this solution doesn't load a CSS file, but you could put those styles inside a '.contact' class that is applied only when you hit a specific route.

Not sure if solves your problem but I hope that helps or at least point you on the right direction.

Czardom answered 28/8, 2013 at 15:37 Comment(2)
The css in the .contact class will be loaded with the rest of the main css when that resource is requested. It doesn't get loaded dynamically, but applied dynamically.Oakie
@Oakie that's correct. And what I have described above is also correct! :) Maybe the word "loaded" is a bit misleading. So I am happy to change it to "applied".Czardom
W
2

For a full solution I suggest using AngularCSS.

As you already know, in Angular we can include templates (structure) and controllers (behavior) in pages and components. AngularCSS enables the last missing piece: attaching stylesheets (presentation).

Routes example:

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

Directive example:

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

You can read more about AngularCSS here:

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

Whitsun answered 13/1, 2015 at 17:43 Comment(0)
G
1

I think the best/simplest answer is one I left here. Someone else asked the same question, so I came up with some simple code and a small github repo to handle this scenario.

Giraldo answered 17/11, 2014 at 18:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.