Angularjs - Hide content until DOM loaded
Asked Answered
E

6

68

I am having an issue in Angularjs where there is a flicker in my HTML before my data comes back from the server.

Here is a video demonstrating the issue: http://youtu.be/husTG3dMFOM - notice the #| and the gray area to the right.

I have tried ngCloak with no success (although ngCloak does prevent the brackets from appearing as promised) and am wondering the best way to hide content until the HTML has been populated by Angular.

I got it to work with this code in my controller:

var caseCtrl = function($scope, $http, $routeParams) {
    $('#caseWrap').hide(); // hides when triggered using jQuery
    var id = $routeParams.caseId;

    $http({method: 'GET', url: '/v1/cases/' + id}).
        success(function(data, status, headers, config) {                   
            $scope.caseData = data;

            $('#caseWrap').show(); // shows using jQuery after server returns data
        }).
        error(function(data, status, headers, config) {
            console.log('getCase Error', arguments);
        });
}

...but I have heard time and time again not to manipulate the DOM from a controller. My question is how can I achieve this using a directive? In other words, how can I hide the element that a directive is attached to until all content is loaded from the server?

Elbowroom answered 8/8, 2013 at 18:24 Comment(1)
hey I think this answer should be the accepted one now ;) https://mcmap.net/q/280422/-angularjs-hide-content-until-dom-loadedSupra
S
148

In your CSS add:

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

and just add a "ng-cloak" attribute to your div like here:

<div id="template1" ng-cloak>{{scoped_var}}<div>

doc: https://docs.angularjs.org/api/ng/directive/ngCloak

Supra answered 30/8, 2014 at 21:30 Comment(4)
ng-cloak ! only that :)Establishmentarian
nice, saved me a big ugly messForzando
Thanks a lot. The Angular doc is not clear on that, it looks like they say that the CSS is already embedded but it's not. Thanks for the tip!Leasehold
Nice, Solved :)Serranid
C
10

On your caseWrap element, put ng-show="contentLoaded" and then where you currently have $('#caseWrap').show(); put $scope.contentLoaded = true;

If the caseWrap element is outside this controller, you can do the same kind of thing using either $rootScope or events.

Cosme answered 8/8, 2013 at 18:34 Comment(0)
T
8

Add the following to your CSS:

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

The compiling of your angular templates isn't happening fast enough.

UPDATE

You should not do DOM manipulation in your controller. There are two thing you can do... 1. You can intercept changes to the value within the scope of the controller via a directive! In your case, create a directive as an attribute that is assigned the property you want to watch. In your case, it would be caseData. If casedata is falsey, hide it. Otherwise, show it.

  1. A simpler way is just use ngShow='casedata'.

Code

var myApp = angular.module('myApp', []);
myApp.controller("caseCtrl", function ($scope, $http, $routeParams, $timeout) {
    $scope.caseData = null;
    //mimic a delay in getting the data from $http
    $timeout(function () {
        $scope.caseData = 'hey!';
    }, 1000);

})
    .directive('showHide', function () {
    return {
        link: function (scope, element, attributes, controller) {
            scope.$watch(attributes.showHide, function (v) {
                if (v) {
                    element.show();
                } else {
                    element.hide();
                }
            });
        }
    };
});

HTML

<div ng-controller='caseCtrl' show-hide='caseData'>using directive</div>
<div ng-controller='caseCtrl' ng-show='caseData'>using ngShow</div>

JSFIDDLE:http://jsfiddle.net/mac1175/zzwBS/

Tomika answered 8/8, 2013 at 18:28 Comment(1)
@ScottyBollinger I should also tell you to not do DOM manipulation in the controller. It is considered a bad practice and not. (See "Using Controllers Correctly" in docs.angularjs.org/guide/dev_guide.mvc.understanding_controller). I am going to update my answer soon as to how to fix this...Tomika
T
2

Since you asked for a directive, try this.

.directive('showOnLoad', function() {
  return {
    restrict: 'A',
    link: function($scope,elem,attrs) {

      elem.hide();

      $scope.$on('show', function() {
        elem.show();
      });

    }
  }
});

Stick (show-on-load) in your element, and in your controller inject $rootScope, and use this broadcast event when the html has loaded.

$rootScope.$broadcast('show');
Teachin answered 8/8, 2013 at 18:49 Comment(1)
Thanks for the response. The above answer was more efficient. I only asked for a new directive because I didn't know this could be achieved with the ngShow directive.Elbowroom
S
1

I have used Zack's response to create a 'loading' directive, which might be useful to some people.

Template:

<script id="ll-loading.html" type="text/ng-template">
  <div layout='column' layout-align='center center'>
    <md-progress-circular md-mode="indeterminate" value="" md-diameter="52"></md-progress-circular>
  </div>
</script>

Directive:

    directives.directive('loading', function() {
  return {
    restrict: 'E',
    template: 'll-loading.html',
    link: function($scope,elem,attrs) {

      elem.show();

      $scope.$on('loaded', function() {
        console.log("loaded: ");
        elem.hide();
      });

    }
  }
});

This example uses angular-material in the html

Sueannsuede answered 22/12, 2015 at 16:52 Comment(0)
M
0

The accepted answer didn't work for me. I had some elements that had ng-show directives and the elements would still show momentarily even with the ng-cloak. It appears that the ng-cloak was resolved before the ng-show returned false. Adding the ng-hide class to my elements fixed my issue.

Mashburn answered 28/3, 2016 at 21:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.