Imagine some heavy content that might be rendered on a web page, such as a chart. Angular gives 2 options to toggle the visibility of said content.
ng-show
You may be interested in a custom directive ng-lazy-show by Alan Colver. It allows combining both ng-if
and ng-show
:
<div ng-lazy-show="showFilters" lendio-business-filters></div>
The code is small and can easily be added to your project:
var ngLazyShowDirective = ['$animate', function ($animate) {
return {
multiElement: true,
transclude: 'element',
priority: 600,
terminal: true,
restrict: 'A',
link: function ($scope, $element, $attr, $ctrl, $transclude) {
var loaded;
$scope.$watch($attr.ngLazyShow, function ngLazyShowWatchAction(value) {
if (loaded) {
$animate[value ? 'removeClass' : 'addClass']($element, 'ng-hide');
}
else if (value) {
loaded = true;
$transclude(function (clone) {
clone[clone.length++] = document.createComment(' end ngLazyShow: ' + $attr.ngLazyShow + ' ');
$animate.enter(clone, $element.parent(), $element);
$element = clone;
});
}
});
}
};
}];
angular.module('yourModule').directive('ngLazyShow', ngLazyShowDirective);
It was @new-dev 's answer that inspired my own combination idea to get a quite heavy component fast using a toggle button.
My original problem was that my page consists of ~20 components each taking ~1 second to load. Each being toggled by a button.
If using plain ng-if I would get instant page loading and 1 sec delay after a toggle press.
If using plain ng-show I would get instant toggle presses but 20 second page loading delay...
So, now I combine them with a ng-mouseenter control too. Like this.
<div ng-mouseenter="create=true">
<button ng-click="showAll = !showAll"></button>
<!--lightweight content-->
<div ng-show="showAll">
<div ng-if="create">
<!--Heavy content-->
</div>
</div>
</div>
This gave me superb performance in both chrome and IE. The time it takes for the user to actually click the button after entering the component is used for the DOM creation. And then the DOM nodes are quickly displayed once (if) the click arrives.
I also never set the ng-if expression to false again. Keeping the DOM cached if the user keeps toggle that section.
Update: The reason I do not have the ng-if and ng-show on the same div is that in IE it caused flickering. When mouseenter event arrived it would show the content and then hide it. In chrome it was ok to have it on the same div.
+1 on Denis's answer, but just for completeness-sake, it can even be simplified further by keeping the logic in the View without "polluting" the controller:
<button ng-click="show = !show">toggle</button>
<div ng-if="once = once || show" ng-show="show">Heavy content</div>
plunker
EDIT: the version above could be further improved (and simplified) with one-time binding to reduce an unnecessary $watch on once
- this will only work in Angular 1.3+:
<div ng-if="::show || undefined" ng-show="show">Heavy content</div>
The undefined
is needed to ensure that the watched value does not "stabilize" before it becomes true
. Once it stabilizes, it also loses the $watch, so it would not be impacted by any further change to show
.
You could make it work by using ngIf
and ngShow
together, where each one is controller by different variable. ngIf
is going to be set to true
once and never set to false
again, while ngShow
will be toggled as much as the user wants to.
Take a look at this fiddle.