Angularjs - Hide content until DOM loaded

Angularjs

Angularjs Problem Overview


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?

Angularjs Solutions


Solution 1 - Angularjs

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

Solution 2 - Angularjs

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.

Solution 3 - Angularjs

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.

  2. 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/

Solution 4 - Angularjs

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');

Solution 5 - Angularjs

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

Solution 6 - Angularjs

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionScotty BollingerView Question on Stackoverflow
Solution 1 - AngularjsSebastien HorinView Answer on Stackoverflow
Solution 2 - Angularjsdnc253View Answer on Stackoverflow
Solution 3 - AngularjsmitchView Answer on Stackoverflow
Solution 4 - AngularjsZack ArgyleView Answer on Stackoverflow
Solution 5 - AngularjsB JarmainView Answer on Stackoverflow
Solution 6 - AngularjsGremashView Answer on Stackoverflow