19

I am trying to dynamically add css to my html head using angular js. Here is sample code

<div ng-repeat="stylesheet in stylesheets">
        <link href="/myapp/{{stylesheet.href}}" type="{{stylesheet.type}}" rel="stylesheet" media="{{stylesheet.media}}" title="{{stylesheet.title}}" />
</div>

This code works as expected, but when browser loads the page, it tries to fetch css resources with raw angularjs templates and I see "404 not found error" in network tab of firebug.

Eg: request http://localhost:8080/myapp/%7B%7Bstylesheet.href%7D%7D, status 404

When page is completely loaded, it does substitution of template values and loads proper css.

Is there a way to avoid 404 error and make it load css after angularjs processing?

1
  • I made a AngularJS service to use easily the @Artem solution. It's here on GitHub. Commented May 16, 2013 at 13:31

4 Answers 4

31

You should use ng-href instead of href.

<link ng-repeat="stylesheet in stylesheets" ng-href="{{stylesheet.href}}" type="{{stylesheet.type}}" rel="stylesheet" />

Example

Sign up to request clarification or add additional context in comments.

Comments

2

There's a another option using $route.resolve and promises. This will wait until the CSS is actually loaded not only added to the head (after that the browser just starts retrieving the file and depending on CSS size can cause page reflow).

// Routing setup
.config(function ($routeProvider) {
  $routeProvider
  .when('/home', {
      controller: 'homeCtrl', 
      templateUrl: 'home.tpl.html'
  }).when('/users', {
      controller: 'usersCtrl', 
      controllerAs: 'vm',
      templateUrl: 'users.tpl.html',
      resolve: {
        load: ['injectCSS', function (injectCSS) {
          return injectCSS.set("users", "users.css");
        }]
      }
  }).otherwise({
      // default page
      redirectTo: '/home'
  });
})

Service implementation

.factory("injectCSS", ['$q', '$http', 'MeasurementsService', function($q, $http, MeasurementsService){
  var injectCSS = {};

  var createLink = function(id, url) {
    var link = document.createElement('link');
    link.id = id;
    link.rel = "stylesheet";
    link.type = "text/css";
    link.href = url;
    return link;
  }

  var checkLoaded = function (url, deferred, tries) {
    for (var i in document.styleSheets) {
      var href = document.styleSheets[i].href || "";
      if (href.split("/").slice(-1).join() === url) {
        deferred.resolve();
        return;
      }
    }
    tries++;
    setTimeout(function(){checkLoaded(url, deferred, tries);}, 50); 
  };

  injectCSS.set = function(id, url){
    var tries = 0,
      deferred = $q.defer(),
      link;

    if(!angular.element('link#' + id).length) {
      link = createLink(id, url);
      link.onload = deferred.resolve;
      angular.element('head').append(link);
    }
    checkLoaded(url, deferred, tries);

    return deferred.promise;
  };

  return injectCSS;
}])

You could add a timeout using tries if this is something you would like to include.

Check out this post for more details:https://medium.com/angularjs-meetup-south-london/angular-dynamically-injecting-css-file-using-route-resolve-and-promises-7bfcb8ccd05b

Comments

2

I've created a very simple example of how make a conditionaly css addition

<link rel="stylesheet" ng-if="lang_direction == 'rtl'" ng-href="{{lang_direction == 'rtl' ? 'css/rtl.css' : ''}}" >

Comments

0

For anyone wishing to create truly dynamic CSS at runtime with AngularJS this is what I used.

index.html

<head> <style type="text/css" ng-bind-html="styles"></style> </head>

cssService

this.$rootScope.myStyles = ".test { color : red; }";

This is just an example, it may be better for you to put the styles into an indexController if you have one and keep it off the $rootScope

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.