Valeur de rendu sans liaison de données

87

Dans AngularJS, comment puis-je rendre une valeur sans liaison de données bidirectionnelle? On peut vouloir faire cela pour des raisons de performances, ou même pour rendre une valeur à un moment donné.

Les exemples suivants utilisent tous deux la liaison de données:

<div>{{value}}</div>

<div data-ng-bind="value"></div>

Comment effectuer un rendu value sans aucune liaison de données?

Blowsie
la source
quelle est votre entrée et sortie. plz expliquer
Nitish Kumar
3
Vos exemples sont en fait une liaison de données unidirectionnelle (modifications du modèle -> afficher les mises à jour). ng-modelvous donne une liaison de données bidirectionnelle: modifications du modèle -> afficher les mises à jour, afficher les modifications -> mises à jour du modèle.
Mark Rajcok
1
actualisé. désolé je voulais dire que je ne veux pas de liaison de données du tout
Blowsie
10
Je ne pense pas que cette question soit terrible ou mérite un vote défavorable. Il est en fait très courant de vouloir désactiver la liaison de données pour éviter les montres inutiles.
OverZealous
4
MISE À JOUR: toute personne lisant cet article trouvera probablement cette vidéo extrêmement utile. youtube.com/watch?v=zyYpHIOrk_Y
Blowsie

Réponses:

141

Angulaire 1.3+

Dans la version 1.3, Angular a pris en charge cela en utilisant la syntaxe suivante.

<div>{{::message}}</div>

Comme mentionné dans cette réponse .


Angular 1.2 et moins

C'est simple et ne nécessite pas de plugin. Regarde ça.

Cette petite directive accomplira facilement ce que vous essayez d'accomplir

app.directive('bindOnce', function() {
    return {
        scope: true,
        link: function( $scope ) {
            setTimeout(function() {
                $scope.$destroy();
            }, 0);
        }
    }
});

Vous pouvez lier une fois comme ça

<div bind-once>I bind once - {{message}}</div>

Tu peux lier comme d'habitude

<div ng-bind="message" bind-once></div>

Démo: http://jsfiddle.net/fffnb/

Certains d'entre vous utilisent peut-être le batarang angulaire, et comme mentionné dans les commentaires, si vous utilisez cette directive, l'élément apparaît toujours comme contraignant alors qu'il ne l'est pas, je suis presque sûr que cela a quelque chose à voir avec les classes qui sont attachées à l'élément. essayez ceci, cela devrait fonctionner (non testé) . Faites-moi savoir dans les commentaires si cela a fonctionné pour vous.

app.directive('bindOnce', function() {
    return {
        scope: true,
        link: function( $scope, $element ) {
            setTimeout(function() {
                $scope.$destroy();
                $element.removeClass('ng-binding ng-scope');
            }, 0);
        }
    }
});

@ x0b : Si vous avez un OCD et que vous souhaitez supprimer l' classattribut vide, procédez comme suit

!$element.attr('class') && $element.removeAttr('class')
iConnor
la source
Je n'ai pas encore testé le plugin, mais je suppose que les outils Chrome AngularJS n'afficheraient pas l'élément bind-once comme une liaison, comme le fait votre exemple. Approche intéressante, je testerai bientôt les deux approches.
Blowsie
Voir ceci - il montre les deux sous forme de liaisons dl.dropboxusercontent.com/u/14037764/Development/stackoverflow/…
Blowsie
1
Sans aucun doute, c'est parce que si la classe ng-binding que vous pouvez facilement supprimer
iConnor
4
C'est génial et beaucoup plus simple que le plugin bindonce. J'ai ajouté la possibilité d'attendre une condition avant de détruire la lunette et c'est vraiment utile. Merci.
Yaron
1
@Connor Je ne suis pas d'accord. Par exemple, je reçois un objet vidéo ($ scope.video) d'une API REST et je veux une liaison unique du titre vidéo ($ scope.video.title). Même si je résous la promesse AVANT de l'ajouter à la portée dans le contrôleur, je dois encore déclarer ng-bind = "video.title" bind-once sur le DOM. Maintenant, avant que la promesse ne soit résolue, video.title n'est pas défini et la portée est détruite avant que video.title ne soit défini. Une solution que j'ai pour cela est d'envelopper les éléments dans un type d'indicateur de chargement / init, ng-if = "someLoadingFlag", mais c'est un mauvais modèle.
SirTophamHatt
49

Il semble qu'Angular 1.3 (à partir de la version bêta 10) ait une liaison unique intégrée:

https://docs.angularjs.org/guide/expression#one-time-binding

Liaison unique

Une expression commençant par :: est considérée comme une expression unique. Les expressions à usage unique arrêteront de recalculer une fois qu'elles sont stables, ce qui se produit après le premier condensé si le résultat de l'expression est une valeur non indéfinie (voir l'algorithme de stabilisation de valeur ci-dessous).

Karen Zilles
la source
1
Cette réponse encore et encore. Je ne peux pas te féliciter assez Karl! Je recommande vivement l'utilisation agressive de cette fonctionnalité partout où cela a du sens.
XDS
1
Wow, je suis vraiment content d'avoir fait défiler vers le bas. Je vais demander à Connor de faire référence à cela dans sa réponse acceptée.
JSager
J'ai une table / liste avec 2000 lignes et en utilisant l'opérateur de liaison unique, mon application devient extrêmement lente lors de la première visualisation / rendu de la liste. Tellement lent, que le navigateur me demande deux ou trois fois si je veux arrêter d'exécuter le script!
Billy G
@ billy-g Pouvez-vous publier un jsfiddle ou un plunker illustrant le problème?
James Daily
@James Daily: Voici le cas "normal" plnkr.co/edit/rCRP0T5fSgNIllx7F27y et ici le cas "expression unique " plnkr.co/edit/Rd5VBVjkcX3sTJYGypUr mais ... je ne peux pas le reproduire là-bas. Quoi qu'il en soit, ce n'est pas plus rapide avec l'expression "one-time expression" et je dois faire plus d'investigation pour trouver pourquoi cela se produit dans mon environnement (j'utilise 1.3 beta 18 d'angularjs)
Billy G
20

Utilisez le module bindonce . Vous devrez inclure le fichier JS et l'ajouter en tant que dépendance à votre module d'application:

var myApp = angular.module("myApp", ['pasvaz.bindonce']);

Cette bibliothèque vous permet de restituer des éléments qui ne sont liés qu'une seule fois - lors de leur première initialisation. Toute autre mise à jour de ces valeurs sera ignorée. C'est un excellent moyen de réduire le nombre de montres sur la page pour les éléments qui ne changeront pas après leur rendu.

Exemple d'utilisation:

<div bo-text="value"></div>

Lorsqu'elle est utilisée de cette manière, la propriété sous valuesera définie une fois qu'elle sera disponible, mais la montre sera désactivée.

Trop zélé
la source
1
J'étais sur le point d'écrire une réponse "écris ta propre directive ...", mais on dirait que quelqu'un a déjà fait ça pour nous, bien.
Mark Rajcok
3
Bindonce est suffisamment utile pour pouvoir être inclus en tant que bibliothèque optionnelle intégrée, comme $resource.
OverZealous
6
c'est ce que je cherchais, mais je m'attendais à ce que quelque chose comme ça soit construit en angulaire!
Blowsie
7

Comparaison entre les réponses @OverZealous et @Connor:

Avec le traditionnel ngRepeat d'angular: 15s pour 2000 lignes et 420mo de RAM ( Plunker )

Avec ngRepeat et le module de @OverZealous: 7s pour 2000 lignes et 240mo de RAM ( Plunker )

Avec ngRepeat et la directive de @Connor: 8s pour 2000 lignes et 500mo de RAM ( Plunker )

J'ai fait mes tests avec Google Chrome 32.

Gabriel
la source
1
Ce serait bien d'avoir également angular-oncecomparé. Merci.
alecxe
@alecxe: J'avais prévu de faire les tests lors de la publication d'une version stable d'AngularJS 1.3.
Gabriel
Merci, n'oubliez pas d'inclure le angular-oncepackage (je l'ai publié comme option alternative ici).
alecxe le
5

En alternative, il existe un angular-oncepackage:

Si vous utilisez AngularJS, avez des problèmes de performances et avez besoin d'afficher beaucoup de données en lecture seule, ce projet est pour vous!

angular-oncea été en fait inspiré bindonceet fournit des once-*attributs similaires :

<ul>
    <li ng-repeat="user in users">
      <a once-href="user.profileUrl" once-text="user.name"></a>
        <a once-href="user.profileUrl"><img once-src="user.avatarUrl"></a>
        <div once-class="{'formatted': user.description}" once-bind="user.description"></div>
    </li>
</ul>
alecxe
la source