AngularJS ng-cloak flicker issues
I recently stumbled into an issue with AngularJS using the ng-cloak directive. Part of the page I was trying to hide would still flicker even though I used ng-cloak. I found this a bit silly as the documentation states the following:
[...] Use this directive to avoid the undesirable flicker effect caused by the html template display. https://docs.angularjs.org/api/ng/directive/ngCloak
ng-cloak is not working – wtf?
So why is it that a few people have issues with it?
Well… it differs from project to project but in my case the project I am working on is not the standard SPA application nor is it bit sized – it is a rather large website. The overall issue is that the initial HTML document being loaded is big which doesn’t make it easy for AngularJS as it is being loaded in the bottom of the page. It has to catch up and traverse the DOM and then take control of the page before anything can come into effect.
Anyways, some of the workarounds you’ll find are:
- Trying to load AngularJS sooner i.e. in the head
- Adding custom css to your stylesheet similar to what the AngularJS framework adds to hide ng-cloaked elements – your external stylesheet loads faster*
- Add inline style to hide the content and toggle it manually
- ..and some other options you’ll find by googling
*With regards to adding custom CSS as recommended by the docs be aware of CSS specificity as you might have CSS that is more specific which will override the above. You can use this nice tool CSS specificity calculator, it might be handy.
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
Unfortunately none of the workarounds mentioned above actually fixed my issue.
I had some markup similar to this:
and I found out that the second part of the expression namely
!cartCtrl.basket.hasPrice
would not evaluate to false immediately as I expected. When I printed the values to the DOM I could see that first it displayed the expression as true and then false. It would happen in a split second and this was the cause to my flicker! Essentially the parse cycle evaluates the boolean expression’ two states which the causes the flickering.
My solution
I found out that the basket variable was declared and available on the scope directly.
ctrl.basket = {};
function init() {
// for brevity...
ctrl.basket = reponse.data.basket;
}
This obviously made it available on the scope while being empty until the init function was done running. Changing it to the following would then make it a private variable on the controller and make it available on the scope when it is ready and populated.
var basket = {};
function init() {
// for brevity...
basket = response.data.basket;
ctrl.basket = basket;
}
Our final markup would end up looking like this, where we check that we have a basket and that our cart is not empty and has a price.
And this would be the end of our flickering!
PS. it is still recommended that you do add the extra custom CSS to your own external stylesheet.