Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditionally-rendering css in html head

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?

like image 645
Raja Avatar asked Aug 11 '12 05:08

Raja


4 Answers

You should use ng-href instead of href.

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

Example

like image 159
Artem Andreev Avatar answered Oct 31 '22 05:10

Artem Andreev


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

like image 45
Gerard Sans Avatar answered Oct 31 '22 05:10

Gerard Sans


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' : ''}}" >
like image 25
Michael Kisilenko Avatar answered Oct 31 '22 06:10

Michael Kisilenko


I made a AngularJS service to use easily the @Artem solution.

It's here on GitHub.

like image 33
Gabriel Avatar answered Oct 31 '22 06:10

Gabriel