Reputation: 2363
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?
Upvotes: 70
Views: 81694
Reputation: 11067
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
Upvotes: 148
Reputation: 1831
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.
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.
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();
}
});
}
};
});
<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/
Upvotes: 8
Reputation: 8308
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.
Upvotes: 0
Reputation: 302
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
Upvotes: 1
Reputation: 8407
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');
Upvotes: 2
Reputation: 40337
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.
Upvotes: 10