Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

AngularJs ng-cloak Problems on large Pages


cloakedI’ve been working on a rather complex and large Angular page. Unlike a typical AngularJs SPA style ‘application’ this particular page is just that: a single page with a large amount of data on it that has to be visible all at once. The problem is that when this large page loads it flickers and displays template markup briefly before kicking into its actual content rendering. This is is what the Angular ng-cloak is supposed to address, but in this case I had no luck getting it to work properly.

This application is a shop floor app where workers need to see all related information in one big screen view, so some of the benefits of Angular’s routing and view swapping features couldn’t be applied. Instead, we decided to have one very big view but lots of ng-controllers and directives to break out the logic for code separation. For code separation this works great – there are a number of small controllers that deal with their own individual and isolated application concerns.

For HTML separation we used partial ASP.NET MVC Razor Views which made breaking out the HTML into manageable pieces super easy and made migration of this page from a previous server side Razor page much easier. We were also able to leverage most of our server side localization without a lot of  changes as a bonus. But as a result of this choice the initial HTML document that loads is rather large – even without any data loaded into it, resulting in a fairly large DOM tree that Angular must manage.

Large Page and Angular Startup

The problem on this particular page is that there’s quite a bit of markup – 35k’s worth of markup without any data loaded, in fact. It’s a large HTML page with a complex DOM tree. There are quite a lot of Angular {{ }} markup expressions in the document.

Angular provides the ng-cloak directive to try and hide the element it cloaks so that you don’t see the flash of these markup expressions when the page initially loads before Angular has a chance to render the data into the markup expressions.

<div id="mainContainer" class="mainContainer boxshadow"
     ng-app="app" ng-cloak>

Note the ng-cloak attribute on this element, which here is an outer wrapper element of the most of this large page’s content. ng-cloak is supposed to prevent displaying the content below it, until Angular has taken control and is ready to render the data into the templates.

Alas, with this large page the end result unfortunately is a brief flicker of un-rendered markup which looks like this:

Unrendered

It’s brief, but plenty ugly – right?  And depending on the speed of the machine this flash gets more noticeable with slow machines that take longer to process the initial HTML DOM.

ng-cloak Styles

ng-cloak works by temporarily hiding the marked up element and it does this by essentially applying a style that does this:

[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
  display: none !important;
}

This style is inlined as part of AngularJs itself. If you looking at the angular.js source file you’ll find this at the very end of the file:

!angular.$$csp() && angular.element(document)
.find(
'head')
.prepend(
'<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],' +
'
[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,' +
'.ng-hide{display:none !important;}ng\\:form{display:block;}'
'.ng-animate-block-transitions{transition:0s all!important;-webkit-transition:0s all!important;}' +
'</style>'
);

This is is meant to initially hide any elements that contain the ng-cloak attribute or one of the other Angular directive permutation markup. Unfortunately on this particular web page ng-cloak had no effect – I still see the flicker.

Why doesn’t ng-cloak work?

The problem is of course – timing. The problem is that Angular actually needs to get control of the page before it ever starts doing anything like process even the ng-cloak attribute (or style etc). Because this page is rather large (about 35k of non-data HTML) it takes a while for the DOM to actually plow through the HTML. With the Angular <script> tag defined at the bottom of the page after the HTML DOM content there’s a slight delay which causes the flicker.

For smaller pages the initial DOM load/parse cycle is so fast that the markup never shows, but with larger content pages it may show and become an annoying problem.

Workarounds

There a number of simple ways around this issue and some of them are hinted on in the Angular documentation.

Load Angular Sooner

One obvious thing that would help with this is to load Angular at the top of the page  BEFORE the DOM loads and that would give it much earlier control. The old ng-cloak documentation actually recommended putting the Angular.js script into the header of the page (apparently this was recently removed), but generally it’s not a good practice to load scripts in the header for page load performance. This is especially true if you load other libraries like jQuery which should be loaded prior to loading Angular so it can use jQuery rather than its own jqLite subset. This is not something I normally would like to do and also something that I’d likely forget in the future and end up right back here :-).

Posted in Angular  JavaScript  CSS  HTML  

The Voices of Reason


 

Maurice
June 03, 2014

# re: AngularJs ng-cloak Problems on large Pages

I agree manually adding the ng-cloak style is the easiest way.

I often use ng-include to avoid this. You can prevent the additional HTTP request by preloading the template cache. Do that by including script elements with type="text/ng-template" and the id matching the URL or the template. Something like:

<script type="text/ng-template" id="/Templates/Books/List">
@Html.Action("List")
</script>

BTW Avoiding {{}} doesn't really work. The same FOUC appears with an ng-repeat.

Zote
June 03, 2014

# re: AngularJs ng-cloak Problems on large Pages

try

<div class='ng-cloak'></div>

instead of

<div ng-cloak></div>

Regards

Rick Strahl
June 03, 2014

# re: AngularJs ng-cloak Problems on large Pages

@Zote - doesn't matter whether you use ng-cloak or class="ng-cloak". The CSS will capture both and try to hide it *if it is there*. With large files though it's important to add the CSS manually rather than let Angular inject it.

John Papa
June 05, 2014

# re: AngularJs ng-cloak Problems on large Pages

Rick, I use the manual CSS for this. My CSS always loads first in my apps, thus having a style for ng-cloak covers this situation nicely since it runs first before any rendering or angular code.

thanhtruongngoc
August 13, 2014

# re: AngularJs ng-cloak Problems on large Pages

hi,

It works well with style.

thanks,

Alex
December 01, 2014

# re: AngularJs ng-cloak Problems on large Pages

style="display:none"
doesn't work for modern versions of AngularJs

Rick Strahl
December 02, 2014

# re: AngularJs ng-cloak Problems on large Pages

@Alex - works for me with Angular 1.3.3...

Rick Strahl
December 02, 2014

# re: AngularJs ng-cloak Problems on large Pages

@Alex - works for me with Angular 1.3.3. I don't see why it wouldn't work - it's just CSS you're setting.

Torgeir
December 10, 2014

# re: AngularJs ng-cloak Problems on large Pages

Sounds like you have your solution, but I how about this approach?

I have found that for large DOMs it often makes sense to use ng-if to toggle content. The ng-if directive appears similar to ng-hide/show, but with the important difference that it physically removes the actual DOM elements rather than hiding them using styles. My experience is that you can trim your DOM a lot this way. Also, another added benefit is that you don't incur watches on the ng-if excluded content like you would with the ng-show approach.

I have some more info in an article I wrote a while back:
http://www.syntaxsuccess.com/#/viewarticle/547a8ba2c26c307c614c715e

You make a good point by saying that it's easier to control permission based content by returning it from the server. However, I would argue that the same could be accomplished by having your angular application talk to a data api with server side permission checks instead. One idea would be to move away from an mvc, view based application, and instead talk to a web api service that returns json based on user permissions in tandem with ng-if.

Anyway, not sure if this is useful, but just a thought.

Johan
August 26, 2015

# re: AngularJs ng-cloak Problems on large Pages

I've had problems with the brief displayof angularJS expressions too, even though i was using the ng-cloak construction. (including the ng-cloak styleclass with display: none !important; in my css file) It turned out that in my case, the ng-cloak construction was working properly, but the flickering was caused by debugging tools. In my case firebug was also enabled, which caused slow rendering of the page (timing issues). When I disabled firebug for my page, everything worked as it was supposed to. (no flickering)

Luis Palacios
March 23, 2016

# re: AngularJs ng-cloak Problems on large Pages

The nuclear option actually help me a lot dealing with elements in a modal, thank you!

Michael
June 26, 2016

# re: AngularJs ng-cloak Problems on large Pages

Thank you Rick, adding the ng-cloak style manually resolved my issue and I am using angular 1.5.6

PP
April 13, 2017

# re: AngularJs ng-cloak Problems on large Pages

Thank You Rick. Worked for me

 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2017