Tuesday, July 7, 2015

non-blocking CSS loading using AngularJS

Ever wanted to load a lot of css, without blocking the browsers rendering of the page?

the simplest way to do that using angular is make the entire page an app, and slap an ng-href on your like like this:

 <link ng-href="/someCssFile.css" rel="stylesheet">

That line alone makes sure the CSS is only loaded, after angular has started. That should add some points to your pagespeed index already! However, if not taken care of, there will be a FOUC shown to your user. Your app might kick in, but not (all) the css is already loaded. Read this blog from Ilya Griorik for more details

It works, because ng-href means nothing to the browser, so the link tag is ignored. However, once angular kicks into action, it evaluates the ng-href, and updates the links href property. By doing this, it will start loading the css, after angualr has started.

If you want to take this a step futher, create a small directive like this:
    angular
        .module('sePreloadCss', ['sePreload'])
        .directive('seLazy', seLazy);

    /* @ngInject */
    function SeLazy (PreloadRegistery) {
        var directive = {
            controller: LazyController,
            restrict:   'A',
            // make sure this is ready before the src kicks in.
            priority:   1000
        };
        return directive;

        /* ngInject */
        function LazyController($element,$attrs) {
            var rel = $attrs.rel;
            var elm = $element[0];
            if (typeof rel !== 'string' || 
                rel.toLowerCase()!=='stylesheet' ||
                typeof $attrs.ngHref !== 'string') {
                return;
            }
            PreloadRegistery.register(elm);

            this.loaded = function loaded() {
                console.log(elm.href + " finished loading!")
                PreloadRegistery.done(elm);
            };
            
            elm.addEventListener("load", this.loaded);

        }
    }
Complemented by a service:
    angular
        .module('sePreload', [])
        .factory('PreloadRegistery', PreloadRegistery);

    /* @ngInject */
    function PreloadRegistery($q) {
        var map      = new Map();
        var loadDone = $q.defer();
        var service  = {
            register: register,
            done:     done,
            loadDone: loadDone.promise,
            has:      has
        };
        return service;
        ////////////////

        function register(elm) {
            map.set(elm, false);
        }

        function has(elm) {
            return map.has(elm);
        }

        function done(elm) {
            var allDone = true;
            map.set(elm, true);
            map.forEach(function (isDone, key) {
                if (!isDone) {allDone = false; }
            });
            if (allDone) {
                loadDone.resolve(true);
            }
        }
    }

Now you can do the following in your appController (the first controller in your app that gets started, even before routing!)
    angular
        .module('myModule',["sePreload", "sePreloadCss"])
        .controller('AppController', AppController);

    /* @ngInject */
    function AppController($q, PreloadRegistery, someTable) {
        var vm   = this;
        vm.title = 'AppController';
        vm.side  = $mdSidenav;

        $q.all([
              someTable.loadDone,
              PreloadRegistery.loadDone
            ])
            .then(activate);
        return;

        function activate () {
          vm.appReady = true;
          routerConfig();
        }

And modify your link a little bit like this:
 <link se-lazy ng-href="/someCssFile.css" rel="stylesheet">

and then finally in your template you can do something like this: (you can use ngCloak alongside!)
   <body ng-controller="appController as vm">
      <div ng-hide='vm.appReady'> 
           Your app assets are being loaded.... 
           (perhaps a nice CSS spinner inserted here!) 
      </div>
      <div ng-show='vm.appReady' style="display:none"> 

           Yay!, my app is ready, and can be shown here!
      
      </div>

I did build a small showcasing plunk. There you can see all this in action for yourself

A serious note, If one of the CSS files fails to load, the spinner will be there forever. This should be handled by your app. There is no one-fits-all solution for that. In some cases, you want to go on without the css, but if it's critical to your app, you might want to warn the user, or let the app send you an alert, or.....

By doing all this, you will gain quite a couple of page-speed point. Your page will load faster, and you have more control over what gets displayed at what time. But more important, you can inform your user what is going on, and they won't witness a lot of funky things going on. I created this, because I was building a page that uses Angalar Material Design, and FontAwesome. Also I needed to preload quite some data. This trick enabled me to show something to the user instantly, in stead of displaying a blank page for 4 to 10 seconds.

Wednesday, October 29, 2014

Is Angular 2.0 hurting the community?

Is Angular 2.0 hurting the community?

Whats up with all the gloom and doom?

I think there is no valid reason for this. Sure, there will be a new angular version in the future, and I sure hope it will be ready by the end of next year. However, realistically I don't see that happen. there is just too much that is still to be determined.

Angular 2.0 might not be backwards compatible (there is no final decision about this, but lets assume it won't!) It has a very clear migration path. All concepts that you have learned in previous versions do still apply. So, if you have followed the 'Angular way' most, if not all of your business logic is clearly separated from the interface. Just re-wrapping it into the 2.0 will not be that hard. Don't get me wrong, it's a rewrite, but not a complete one.
Most things you are doing in 1.x are way easier to create in 2.0. There will be much less awkward syntax, and more logic 'flow' to your programs.
If you are (and you should) following John Papa's style guide moving to 2.0 will feel quite natural. Sure there will be issues, but hey, aren't we paid to deal with stuff like this?

Then there is this other thing. 2.x is targeting mobile and evergreen browsers. We all want that, don't we? The trend towards that is picking up, and I hope for sure that we will really can do this by the end of 2015.
However, stuff we (as developers in the common sense) did build in the past, have a nasty habit of staying alive much longer than anybody would have ever thought! (windows XP anyone? or more close to us, VIM, EMACS? In our world, dinosaurs are still very well alive, and some even gets wide support!)


Aside from all that, Angular 1.x is already in use by a lot of (big) companies, alone inside Google there are to date 1600+ Angular 1.x applications (outside the angular team!). I know a couple of banks using it for their customer facing web-sites. I know there are quite a lot of internal (big) applications written in 1.0. Those will not all be rewritten anytime soon, (soon as in the next 5 years!)
With such an investment into the 1.x, you can be pretty much sure, 1.x will be supported for the next decade. If not by Google, there will be others. Don't forget, there is a really large community surrounding Angular 1.3, and inside this community are quite some large corporations. You don't see this, because almost every one committing does this on his/her personal name, but check their employers.

This all makes sure angular is a safe bet!
But don't take my word for it, I encourage you to do your own research!