Screenshot of the new interface

AngularJS, Routing, CSS and Title tags

I finally bit the bullet, after months of finding the right project to learn angular I finally found a good use case.

I will be revamping https://www.drupalstatus.org/ the coming weeks and this being an authenticated back-end (so no SEO issues) it makes total sense to do this in Angular as a single page application.

After getting started with the free AngularJS tutorial on CodeSchool (which I can highly recommend I jumped straight in.

Now that I’m in full on development mode I’m starting to get some basic issues, with the first one being:

How to manage HTML partials?

My whole application exists in the following page:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title ng-bind="header"></title>
<script src="./js/ext/angular.js"></script>
<script src="./js/ext/angular-route.js"></script>
<script src="./js/app.js"></script>
</head>
<body ng-view>
  </body>
</html>

The <body ng-view> part is key, I make use of ngRoute like this:

myApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/login', {
templateUrl: 'templates/login/login.html',
controller: 'loginCtrl'
}).
when('/register', {
templateUrl: 'templates/login/register.html',
controller: 'registerCtrl'
}).
when('/user/home', {
templateUrl: 'templates/user/home.html',
controller: 'homeCtrl'
}).
otherwise({
redirectTo: '/login'
});
}]);

So whenever a user navigates, the proper snippet will be loaded and the right controller.

This concept is key for the following two solutions:

How to manage the page title properly navigating trough partials.

Remember that <title ng-bind=”header”></title> in my index.html ?

I change the title by setting the rootscope value whenever the proper controller is loaded for that section.

myApp.controller('someCtrl', function ($rootScope) {

$rootScope.header = "This is the Section title";

This neat little trick makes sure that all pages have the proper title.

How to manage CSS loads.

As you know, my partials are being loaded in the <body ng-view> tag.

Now I have different styles for my ‘account’ screen and my ‘login’ screens.

The first thing I tried is adding <link rel=”stylesheet” href=”./css/some.css”> in my body tags.

This works 100%. (It’s not really official but all browsers support it).

The big disadvantage is that the DOM is reprocessed every time a new snippet (and thus the CSS) loads. Aside from taking up time (milliseconds) it also makes your screen flash.

For me this was unacceptable, since we where going for the smooth one-page-app experience.

I started googling around and found many people with the same issue. There are some average, bad and totally wrong solutions out there. I guess one of the ‘better’ solutions would be to use Yappli/angular-css-injector. But I hate using frameworks or plug-ins for things I consider to be ‘one-liners’ as it brings more maintenance and uncertainty to the project.

Here is how I did it:

myApp.factory('Styles', function () {

var loadedStyles = [];

var addLink = function(styleHref,styleHash) {
var link = document.getElementById(styleHash);

if(typeof link !== "undefined" && link != null) {
link.disabled = false;
} else {
var newLink = document.createElement('link');
newLink.rel = "stylesheet";
newLink.type = "text/css";
newLink.id = styleHash;
newLink.href = styleHref;
document.getElementsByTagName('head')[0].appendChild(newLink);
}
};

var removeLink = function(styleHash) {
var link = document.getElementById(styleHash);
if(typeof link !== "undefined" && link != null) {
link.disabled = true;
}
};

return {
loadStyles: function(styles) {
styles.forEach(function(styleHref) {
var styleHash = styleHref.toString().hashCode();
var isLoaded = false;

loadedStyles.forEach(function (h) {
if (h == styleHash) {
isLoaded = true;
}
});

if (!isLoaded) {
loadedStyles.push(styleHash);
addLink(styleHref,styleHash);
}
});
},
unloadStyles: function(styles) {
styles.forEach(function(styleHref) {
var styleHash = styleHref.toString().hashCode();
loadedStyles.forEach(function (h, index) {
if (h == styleHash) {
loadedStyles.splice(index, 1);
removeLink(styleHash);
}
});
});
}
}
});

Now the main difference of a Factory over a Service is that this allows me to keep track of the ‘loaded’ state.

Whenever I want to load certain styles I do so from my Controller like this:

myApp.controller('someCtrl', function (Styles) {
    Styles.loadStyles(['/css/ext/file1.css','/css/ext/file2.css']);

And if you want to unload any of the styles:

myApp.controller('someCtrl', function (Styles) {
     Styles.unloadStyles(['/css/ext/file1.css']);

As things tend to go, what I assumed would be a one liner is now a bigger than expected subproject that can load, unload disable and re-enable styles taking up way too much of my productive time (including this post) but for me the joy in development is more in solving issues when learning a new framework or language then finding a quick fix, and in this case it forced me to implement a Factory thus further growing my AngularJS skills.

Bonus: using javascript in partials

Any javascript code in <script type=”text/javascript”> is not parsed when including partials but I found this neat little trick. I do think you should be able to do without, but I like to do some in-line javascript while testing so I’m using it for now.

Use the following in your app.js

myApp.directive('script', function() {
return {
restrict: 'E',
scope: false,
link: function(scope, elem, attr) {
if (attr.type === 'text/javascript-lazy') {
var code = elem.text();
var f = new Function(code);
f();
}
}
};
});

This will allow you to use the following in your partials:

<script type="text/javascript-lazy">
alert('Hello medium!');
</script>

I hope this helps someone else during their Angular discovery !

Cheers,
Sam Hermans www.lumturio.com