55

I have an AngularJS project, I want to prevent a FOUC during page load on a classname. I've read about ng-template but that seems useful only for content within a tag.

<body class="{{ bodyClass }}">

I would like it to be "login" on page load. Any strategy for this? Or do I just have to fudge it and load it as 'login' and manually use javascript to to tweak the DOM just for this instance.

Casey Flynn
  • 13,654
  • 23
  • 103
  • 194
  • 3
    Have a look at this post using the ng-cloak directive! http://deansofer.com/posts/view/14/AngularJs-Tips-and-Tricks-UPDATED#fouc. If you put it on the body, it will hide all the page until every {{}} tags are parsed. – jpmorin Apr 18 '13 at 05:32
  • 3
    Please award the answer. – Les Hazlewood Jul 04 '13 at 16:21

5 Answers5

79

What you are looking for is ng-cloak.
You have to add it like this:

<body class="{{ bodyClass }}" ng-cloak>

and this will prevent unwanted flashing.
Link to docs about this.

Edit:
It is also advisable to put the snippet below into your CSS file, according to docs.

"For the best result, the angular.js script must be loaded in the head section of the html document; alternatively, the css rule above must be included in the external stylesheet of the application."

[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
    display: none !important;
}
the-lay
  • 1,461
  • 12
  • 22
  • 8
    Will this cause Googlebot to not index the inner content or punish the site for cloaking? – Prathan Thananart Sep 17 '13 at 16:36
  • 4
    @PrathanThananart If much of your content is being loaded asynchronously, then visible or not I think you won't be getting much indexing value. Unless Google JS crawling has gotten a lot smarter! – andrewb May 09 '15 at 09:01
  • I do get flashes of angular despite using this official way. Sometimes even twice... [the solution below (styling first, angular undiding only there&then)](http://stackoverflow.com/a/22518125/444255) works much more reliable IMHO... – Frank N Jun 30 '16 at 09:10
  • @FrankN, docs were updated with mentioning to put ng-cloak display none into css. Thanks, will edit my answer. – the-lay Jul 01 '16 at 09:29
23

One of the problems that you're facing is that the browser will display the <body> element before AngularJS has loaded and started manipulating the DOM. What other people said about ng-class is correct, it will do the right class applying but you still need to not show anything before Angular is ready.

In previous versions of Angular you could do this:

<body style="display:none" ng-show="true" ng-class="{{bodyClass}}">

In recent versions this doesn't work however because ng-show does its visibility by adding and removing the ng-hide class (which is less specific than an element attribute)

What I've been doing recently is this:

<head>
...
    <style> <!-- Or you could include this in an existing style sheet -->
        .ng-cloak {
            display: none !important;
        }
    </style>    
</head>
<body class="ng-cloak" ng-cloak ng-class="{{bodyClass}}">

Doing it this way means that the <body> will be immediately hidden by the style for the ng-cloak class. When Angular does start up it will process all of the directives include ng-class and ng-cloak, so your <body> element will then have the right class and be visible.

Read more here ng-cloak directive

Thomas Landauer
  • 7,857
  • 10
  • 47
  • 99
Nicholas Green
  • 612
  • 8
  • 10
  • 2
    couldn't you just omit the class and do `[ng-cloak] { display... }`? – Zach Lysobey May 06 '14 at 18:16
  • 1
    @ZachL, I'm not sure I understand the question. If you mean "why don't you put the display:none as an inline style?", the problem with putting display:none on the inline style is that it will have a high precedence and won't be removed by ng-cloak or ng-show/ng-hide. This means that the element will stay hidden unless you write something extra to remove the display:none from the inline style. – Nicholas Green May 06 '14 at 23:29
  • Hey @Nicholas, I was suggesting that perhaps you could omit the `ng-cloak` class, and select the element by the `ng-cloak` attribute using the [`attribute` selector](http://css-tricks.com/attribute-selectors/). I wasn't able to solve my own particular issue with `ng-cloak` no matter how I did it (solved it otherwise), so cannot confirm if this would actually work. – Zach Lysobey May 06 '14 at 23:36
  • Styling on the attribute like that seems feasible. Or you could skip the attribute and only use the class: CSS `.ng-cloak { display: none !important; }` and HTML `stuff`. – Henrik N May 31 '14 at 20:27
  • Works perfect for me. Note: doesn't have to be body tag. Works on any tag, for example: `
    `
    – dualmon Jun 28 '16 at 23:47
  • This solution is my prefererred. An initial, styled(!, by class or style attrib, but not by any anuglar foobaar) display none, and only then make it visible depending on certain things, i.e. `ng-class={displayblock: ctrl.results > 0}`. Extra important on ng-repeat tags for example. – Frank N Jun 30 '16 at 09:08
6

There is a solution to the flash problem in plain CSS which is bulletproof. For the class of your app root element add the following. This solution works by order of processing: everything is hidden in CSS, angular is loaded, and then CSS shows the app root. I like to use this so if there are other non-angular elements on the page they will render first making the page appear to load faster.

/* in your CSS file in head */
.myApp {
    display:none;
}

<div class="myApp" ng-app="myApp"></div>

Just before the body ends add your script references and some inline CSS:

<script language="javascript" type="text/javascript" src="angular.min.js"></script>
<style>
.myApp {
    display:block;
}
</style>
</body>
mbokil
  • 3,202
  • 30
  • 22
3

Instead of using class, you should use ng-class

<body ng-class="{{ bodyClass }}">
Alex G
  • 1,321
  • 1
  • 24
  • 31
1

I've noticed sometimes even ng-cloak doesn't seem to actually work. Maybe I am doing something wrong.

<body ng-cloak class="{{ bodyClass }}">

I also do one more small thing similar to @mbokil you can hide the element by default. By using an attribute selector targeting any elements with the attribute ng-cloak you're able to utilize angular directives. This has several benefits, but the main benefit is once angular fires it removes this attribute which will trigger a redraw.

<style>
  [ng-cloak] {
    display:none;
  }
</style>
Jeff Richardson
  • 146
  • 1
  • 4