Sunday, June 23, 2013

CSS Lazy Loading in AngularJS

The simple way:

A classical Angular app starts with a HTML file. In the head tag you'll put all the  link tags for your templates style, and under the  body tag you'll stick the ng-app directive.

The Problem:

1. Every module should be an atomic unit:

Each module (controller,template and a CSS) should function as an independent unit. We should be able to take a module out and add new one without major changes to the whole app.

In a classical angular app, with each module template taking in out out you should maintain the link tags in the Head. With many templates this is no a pretty sight...

The right way to too it would be adding the link tag inside the module template file. The problem is that link tag is not allowed inside the body tag, and although it will work, it's ain't going to be pretty.

2. A template should be able to ask 3rd party style:

In addition to it's own style file, a template may use a 3rd party UI lib (like AngularUI, AngularStrap or a custom directive with a CSS file).

Maintaining this dependencies in the head tag by hand is not a big fun and error prone.
In addition, multiple modules can use the same 3rd party lib, and we should make sure to load it's CSS file only once because browsers are not smart to prevent it.

3. Sometimes we have no access to the head tag:

In our case, this application is wrapped with many JSP templates and pages, and it should be self contained. Touching the wrapping JSPs is not an option.

The first solution:

I created an Angular service that can load a CSS file by appending a link tag to the head tag and I've using it in the controllers:


But this is not a good solution.
First, it don't prevent loading the 3rd party CSS files over multiple times.
Second' it is ugly! the CSS let the browser know how it should render the template. It's place is in the template and not in the controller!

A better solution:

I've created a new directive and used it in the templates. 
I added a peace of logic to verify we are loading each style file only once:






Calculated paths

In my app, we are using a server mechanism to calculate the path to all of our resources, so the href value should be calculated:



The problem is that the directive code is executed before the basicResourcePath is being evaluated which leads to a wrong CSS path.
The solution I've found is to use the $observe on the attributes to the directive:



3 comments:

  1. Seems like the problem with using a directive is when you have a directive that requires a file.

    For example if you have an <input datepicker /> element (where 'datepicker' is a custom directive which needs to load some CSS for styling) you'd have to add your <lazy-style/> element.

    ReplyDelete
  2. Adrian - this is another good scenario in addition to the scenarios above. thanks.

    ReplyDelete
  3. Hey! An interesting problem you are writing about. However, how do you unload a stylesheet when you have two modules with conflicting CSS?

    ReplyDelete