Imaginez un contenu lourd pouvant être rendu sur une page Web, tel qu'un graphique. Angular propose 2 options pour basculer la visibilité dudit contenu.
ng-show rendra le contenu quelle que soit l'expression et le "cache" simplement après coup. Ce n’est pas idéal car l’utilisateur ne peut jamais "ouvrir" le contenu pendant sa session, il était donc inutile de le restituer.
ng-if est meilleur à cet égard. Son utilisation à la place de ng-show empêchera le contenu lourd d'être rendu si l'expression est fausse. Cependant, sa force est aussi sa faiblesse, car si l'utilisateur masque le graphique et le montre à nouveau, le contenu est rendu à partir de zéro à chaque fois.
Comment puis-je faire une directive qui prend le meilleur des deux mondes? Cela signifie que cela fonctionne comme si-jusqu'à ce que le contenu ne soit restitué pour la première fois, puis bascule pour fonctionner comme "ng-show" afin d'empêcher le rendu à chaque fois.
+1 sur la réponse de Denis, mais par souci de complétude, on peut même la simplifier davantage en conservant la logique dans la vue sans "polluer" le contrôleur:
<button ng-click="show = !show">toggle</button>
<div ng-if="once = once || show" ng-show="show">Heavy content</div>
_ { plunker } _
EDIT: la version ci-dessus pourrait être encore améliorée (et simplifiée) avec une reliure ponctuelle afin de réduire une $ surveillance inutile sur once
- ceci ne fonctionnera que dans Angular 1.3+:
<div ng-if="::show || undefined" ng-show="show">Heavy content</div>
La undefined
est nécessaire pour s'assurer que la valeur surveillée ne "stabilise pas" avant de devenir true
. Une fois qu'il se stabilise, il perd également la montre $, de sorte qu'il ne sera plus affecté par un changement ultérieur en show
.
Vous pourriez le faire fonctionner en utilisant ngIf
et ngShow
ensemble, où chacun est contrôleur par variable différente. ngIf
sera défini sur true
une fois et ne sera jamais redéfini sur false
, tandis que ngShow
sera modifié autant que l'utilisateur le souhaite.
Jetez un oeil à ce violon .
C’est la réponse de @ new-dev qui a inspiré ma propre idée de combinaison: obtenir rapidement un composant assez lourd à l’aide d’un bouton à bascule.
Mon problème initial était que ma page se composait d'environ 20 composants prenant chacun environ 1 seconde à se charger. Chacun étant basculé par un bouton.
Si vous utilisez plain ng-if, le chargement de la page et le délai d'une seconde après un basculement sont instantanés.
Si vous utilisez plain ng-show, j'aurais des pressions instantanées, mais un délai de chargement de 20 secondes ...
Donc, maintenant je les combine avec un contrôle ng-mouseenter aussi. Comme ça.
<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>
Cela m'a donné une superbe performance à la fois en chrome et en IE. Le temps nécessaire à l'utilisateur pour cliquer sur le bouton après avoir entré le composant est utilisé pour la création du DOM. Et puis les nœuds DOM sont rapidement affichés une fois (si) le clic arrive.
De plus, je ne mets jamais à nouveau l'expression ng-if à false. Conserver le DOM mis en cache si l'utilisateur continue de basculer cette section.
Mise à jour : La raison pour laquelle je n'ai pas les images ng-if et ng-show sur le même div est que, dans IE, cela a provoqué un scintillement. Lorsque l'événement mouseenter est arrivé, il affiche le contenu puis le cache. En chrome, c'était bien de l'avoir sur la même div.
Vous pouvez être intéressé par une directive personnalisée ng-lazy-show
par Alan Colver. Il permet de combiner à la fois ng-if
et ng-show
:
<div ng-lazy-show="showFilters" lendio-business-filters></div>
Le code est petit et peut facilement être ajouté à votre projet:
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);