Comment fonctionne la liaison de données dans AngularJS?

1957

Comment fonctionne la liaison de données dans le AngularJScadre?

Je n'ai pas trouvé de détails techniques sur leur site . Il est plus ou moins clair comment cela fonctionne lorsque les données sont propagées de la vue au modèle. Mais comment AngularJS suit-il les changements des propriétés du modèle sans setters et getters?

J'ai trouvé qu'il existe des observateurs JavaScript qui peuvent faire ce travail. Mais ils ne sont pas pris en charge dans Internet Explorer 6 et Internet Explorer 7 . Alors, comment AngularJS sait-il que j'ai changé par exemple ce qui suit et reflété ce changement sur une vue?

myobject.myproperty="new value";
Pashec
la source
10
Gardez à l'esprit que depuis angular 1.0.0rc1, vous devez spécifier ng-model-instant ( docs-next.angularjs.org/api/… ) pour que votre modeur soit mis à jour de manière permanente. Sinon, il sera mis à jour en cas de flou.
Sotomajor
8
Le lien de Marcello est apparemment cassé, donc ici , il est à nouveau: github.com/mhevery/angular.js/blob/master/docs/content/guide/...
riffraff
6
@orian, ce lien est mauvais. mis à jour vers (je suppose) est le même - docs.angularjs.org/guide/databinding
Kevin Meredith
11
Pour ceux qui lisent encore cette question, veuillez noter qu'Angular 2.0 a considérablement changé la façon dont ils traitent la liaison de données depuis Angular 1.x afin de travailler avec les composants Web et de résoudre un grand nombre des problèmes dans les réponses ci-dessous.
août

Réponses:

2745

AngularJS se souvient de la valeur et la compare à une valeur précédente. Il s'agit d'une vérification de base. S'il y a un changement de valeur, il déclenche l'événement change.

La $apply()méthode, qui est ce que vous appelez lorsque vous passez d'un monde non AngularJS à un monde AngularJS, appelle $digest(). Un condensé est tout simplement une vieille vérification. Il fonctionne sur tous les navigateurs et est totalement prévisible.

Pour comparer la vérification incorrecte (AngularJS) et les auditeurs de changement ( KnockoutJS et Backbone.js ): Bien que la vérification incorrecte puisse sembler simple, et même inefficace (j'y reviendrai plus tard), il s'avère qu'elle est toujours sémantiquement correcte, tandis que les écouteurs de changement ont beaucoup de cas étranges et ont besoin de choses comme le suivi des dépendances pour le rendre plus sémantiquement correct. Le suivi des dépendances de KnockoutJS est une fonctionnalité intelligente pour un problème que AngularJS n'a pas.

Problèmes avec les écouteurs de changement:

  • La syntaxe est atroce, car les navigateurs ne la prennent pas en charge de manière native. Oui, il existe des proxys, mais ils ne sont pas sémantiquement corrects dans tous les cas, et bien sûr il n'y a pas de proxys sur les anciens navigateurs. L'essentiel, c'est que la vérification non autorisée vous permet de faire POJO , tandis que KnockoutJS et Backbone.js vous forcent à hériter de leurs classes et à accéder à vos données via des accesseurs.
  • Changer la coalescence. Supposons que vous ayez un tableau d'éléments. Supposons que vous souhaitiez ajouter des éléments dans un tableau, au fur et à mesure que vous bouclez pour ajouter, chaque fois que vous ajoutez, vous déclenchez des événements sur le changement, ce qui rend l'interface utilisateur. C'est très mauvais pour les performances. Ce que vous voulez, c'est mettre à jour l'interface utilisateur une seule fois, à la fin. Les événements de changement sont trop fins.
  • Les écouteurs de changement se déclenchent immédiatement sur un setter, ce qui est un problème, car l'écouteur de changement peut modifier davantage les données, ce qui déclenche plus d'événements de changement. C'est mauvais car sur votre pile, vous pouvez avoir plusieurs événements de changement en même temps. Supposons que vous ayez deux tableaux qui doivent être synchronisés pour une raison quelconque. Vous pouvez seulement ajouter à l'un ou à l'autre, mais chaque fois que vous ajoutez, vous déclenchez un événement de modification, qui a maintenant une vue incohérente du monde. Il s'agit d'un problème très similaire au verrouillage des threads, que JavaScript évite puisque chaque rappel s'exécute exclusivement et jusqu'à son terme. Les événements de changement rompent cela, car les setters peuvent avoir des conséquences importantes qui ne sont pas prévues et non évidentes, ce qui crée à nouveau le problème de thread. Il s'avère que ce que vous voulez faire est de retarder l'exécution de l'auditeur et de garantir,

Et la performance?

Il peut donc sembler que nous sommes lents, car la vérification des résultats est inefficace. C'est là que nous devons examiner les nombres réels plutôt que de simplement avoir des arguments théoriques, mais définissons d'abord certaines contraintes.

Les humains sont:

  • Lent - Tout ce qui dépasse 50 ms est imperceptible pour l'homme et peut donc être considéré comme "instantané".

  • Limité - Vous ne pouvez pas vraiment montrer plus de 2000 informations à un humain sur une seule page. Rien de plus que cela est vraiment une mauvaise interface utilisateur, et les humains ne peuvent pas traiter cela de toute façon.

La vraie question est donc la suivante: combien de comparaisons pouvez-vous faire sur un navigateur en 50 ms? C'est une question difficile à répondre car de nombreux facteurs entrent en jeu, mais voici un cas de test: http://jsperf.com/angularjs-digest/6 qui crée 10 000 observateurs. Sur un navigateur moderne, cela prend un peu moins de 6 ms. Sur Internet Explorer 8, cela prend environ 40 ms. Comme vous pouvez le voir, ce n'est pas un problème, même sur les navigateurs lents de nos jours. Il y a une mise en garde: les comparaisons doivent être simples pour s'adapter à la limite de temps ... Malheureusement, il est beaucoup trop facile d'ajouter une comparaison lente dans AngularJS, il est donc facile de créer des applications lentes lorsque vous ne savez pas ce que vous faites. Mais nous espérons avoir une réponse en fournissant un module d'instrumentation, qui vous montrerait quelles sont les comparaisons lentes.

Il s'avère que les jeux vidéo et les GPU utilisent l'approche de vérification des erreurs, notamment parce qu'elle est cohérente. Tant qu'ils dépassent le taux de rafraîchissement du moniteur (généralement 50-60 Hz, ou toutes les 16,6-20 ms), toute performance supérieure à celle-ci est un gaspillage, il est donc préférable de dessiner plus de choses que d'augmenter les FPS.

Misko Hevery
la source
32
@Mark - oui, dans KO, vous ajoutez simplement .extend ({throttle: 500}) pour attendre 500 millisecondes après le dernier événement de modification avant d'agir.
Daniel Earwicker
158
Toute cette réponse est excellente, à part "Tant qu'ils obtiennent 50 images par seconde, toute performance par rapport à cela est un gaspillage, car l'œil humain ne peut pas l'apprécier, il est donc préférable de dessiner plus de choses que d'augmenter les images par seconde." Cette déclaration est complètement incorrecte selon votre application. L'œil peut certainement apprécier plus de 50 fps, et comme les divers problèmes avec VR montrent (lire l'un des derniers de John Carmack ou Michael Abrash, en particulier le dernier discours GDC 2013 VR), 50 fps est en réalité beaucoup trop lent. À part cela, votre réponse est excellente. Je ne veux tout simplement pas que la désinformation se propage.
Nate Bundy
10
@DavidRivers us is µs just like in utorrent 1µs = 0.000001s
Thorgeir
33
La déclaration pourrait être facilement dite en sens inverse comme "La vérification sale est une fonctionnalité intelligente pour un problème que le knockout n'a pas". ES6 utilise des observables et angulaire se débarrasse des vérifications sales. Le monde réel a rattrapé cette réponse et l'a montrée fausse.
conique
17
"Tout ce qui dépasse 50 ms est imperceptible pour l'homme" n'est pas vrai. Lors des tests, nous avons constaté que nos clients peuvent facilement faire la distinction entre une latence de mise à jour de 50 ms (20 ips) et une latence de mise à jour de 16,6 ms (60 ips). Les scènes se déroulant à l'ancienne vitesse sont systématiquement moins bien notées dans l'ensemble "comment s'est-il senti" même lorsque les gens n'ont pas consciemment enregistré le framerate.
Crashworks
323

Misko a déjà donné une excellente description du fonctionnement des liaisons de données, mais je voudrais ajouter mon point de vue sur le problème de performances avec la liaison de données.

Comme l'a déclaré Misko, environ 2000 liaisons sont l'endroit où vous commencez à voir des problèmes, mais vous ne devriez pas avoir plus de 2000 informations sur une page de toute façon. Cela peut être vrai, mais toutes les liaisons de données ne sont pas visibles pour l'utilisateur. Une fois que vous commencez à créer n'importe quel type de widget ou de grille de données avec une liaison bidirectionnelle, vous pouvez facilement atteindre 2000 liaisons, sans avoir une mauvaise UX.

Prenons par exemple une zone de liste déroulante dans laquelle vous pouvez taper du texte pour filtrer les options disponibles. Ce type de contrôle pourrait avoir ~ 150 éléments et être encore très utilisable. S'il a une fonctionnalité supplémentaire (par exemple une classe spécifique sur l'option actuellement sélectionnée), vous commencez à obtenir 3-5 liaisons par option. Mettez trois de ces widgets sur une page (par exemple, l'un pour sélectionner un pays, l'autre pour sélectionner une ville dans ledit pays, et le troisième pour sélectionner un hôtel) et vous vous trouvez déjà entre 1000 et 2000 liaisons.

Ou envisagez une grille de données dans une application Web d'entreprise. 50 lignes par page n'est pas déraisonnable, chacune pouvant avoir 10 à 20 colonnes. Si vous créez cela avec ng-repeats et / ou avez des informations dans certaines cellules qui utilisent des liaisons, vous pourriez approcher 2000 liaisons avec cette seule grille.

Je trouve que c'est un énorme problème lorsque je travaille avec AngularJS, et la seule solution que j'ai pu trouver jusqu'à présent est de construire des widgets sans utiliser de liaison bidirectionnelle, au lieu d'utiliser ngOnce, de désenregistrer des observateurs et des astuces similaires, ou de construire directives qui construisent le DOM avec jQuery et manipulation DOM. Je pense que cela va à l'encontre du but d'utiliser Angular en premier lieu.

Je serais ravi d'entendre des suggestions sur d'autres façons de gérer cela, mais alors je devrais peut-être écrire ma propre question. Je voulais mettre cela dans un commentaire, mais cela s'est avéré beaucoup trop long pour cela ...

TL; DR
La liaison de données peut entraîner des problèmes de performances sur des pages complexes.

MW.
la source
26
Ouais j'appuie ça. La principale responsabilité de notre application est d'afficher les connexions entre différentes entités. Une page donnée peut avoir 10 sections. Chaque section a une table. Chaque table possède 2 à 5 filtres de type. Chaque tableau comprend 2 à 5 colonnes, chacune avec 10 lignes. Très rapidement, nous rencontrons des problèmes de performances et optons pour les options "astuces similaires".
Scott Silvi
10
Est-il juste de dire qu'Angular ne concerne pas uniquement la liaison de données et que certaines applications peuvent ne pas vouloir utiliser cette fonctionnalité pour les raisons que d'autres ont citées? Je pense que l'approche de l'ID et de la modularité vaut elle-même beaucoup; avoir une liaison automatique magique est bien, mais dans chaque implémentation existante, il y a des compromis de performances. La voie d'Angular est sans doute supérieure pour la majorité des applications Web CRUD, et les gens ne font que heurter un mur en essayant de le pousser à l'extrême. Ce serait bien d'avoir une autre méthode d'écoute des événements prise en charge, mais c'est peut-être fondamentalement trop complexe pour un seul cadre?
Jason Boyd
8
Angular propose désormais une liaison de données à sens unique et à liaison unique pour résoudre ce problème. De plus, il a maintenant des index pour votre source de répéteur, ce qui vous permet de modifier la liste sans reconstruire le dom pour tout le contenu.
Gaute Løken
6
@MW. Honnêtement, je pensais que bind-once était au cœur. Mais il semble que non. C'est juste quelque chose que vous pouvez faire lorsque vous écrivez vos propres directives, en liant essentiellement des choses sans les regarder. Cependant, il existe un mod ux: github.com/pasvaz/bindonce
Gaute Løken
9
Un cri du futur pour tous ceux qui liront ceci: la liaison unique est maintenant une fonctionnalité centrale d'Angular v1.3, lisez plus ici: docs.angularjs.org/guide/expression
Nobita
158

En vérifiant mal l' $scopeobjet

Angular maintient un simple arrayobservateur dans les $scopeobjets. Si vous en inspectez, $scopevous constaterez qu'il contient un arrayappelé $$watchers.

Chaque observateur est un objectqui contient entre autres

  1. Une expression que l'observateur surveille. Ce pourrait être simplement un attributenom ou quelque chose de plus compliqué.
  2. Une dernière valeur connue de l'expression. Cela peut être vérifié par rapport à la valeur calculée actuelle de l'expression. Si les valeurs diffèrent, l'observateur déclenche la fonction et marque le $scopecomme sale.
  3. Une fonction qui sera exécutée si l'observateur est sale.

Comment les observateurs sont définis

Il existe de nombreuses façons différentes de définir un observateur dans AngularJS.

  • Vous pouvez explicitement $watchun attributeon $scope.

    $scope.$watch('person.username', validateUnique);
  • Vous pouvez placer une {{}}interpolation dans votre modèle (un observateur sera créé pour vous sur le courant $scope).

    <p>username: {{person.username}}</p>
  • Vous pouvez demander une directive telle que ng-modeldéfinir l'observateur pour vous.

    <input ng-model="person.username" />

Le $digestcycle vérifie tous les observateurs par rapport à leur dernière valeur

Lorsque nous interagissons avec AngularJS via les canaux normaux (ng-model, ng-repeat, etc.), un cycle de résumé sera déclenché par la directive.

Un cycle de digestion est une traversée en profondeur d'abord $scopeet de tous ses enfants . Pour chacun $scope object, nous itérons sur son $$watchers arrayet évaluons toutes les expressions. Si la nouvelle valeur d'expression est différente de la dernière valeur connue, la fonction de l'observateur est appelée. Cette fonction peut recompiler une partie du DOM, recalculer une valeur $scope, déclencher un AJAX request, tout ce que vous avez besoin de faire.

Chaque étendue est traversée et chaque expression de surveillance évaluée et vérifiée par rapport à la dernière valeur.

Si un observateur est déclenché, il $scopeest sale

Si un observateur est déclenché, l'application sait que quelque chose a changé et $scopeest marqué comme sale.

Les fonctions d'observateur peuvent modifier d'autres attributs sur $scopeou sur un parent $scope. Si une $watcherfonction a été déclenchée, nous ne pouvons pas garantir que nos autres $scopesont toujours propres, et nous exécutons donc à nouveau tout le cycle de résumé.

En effet, AngularJS a une liaison bidirectionnelle, de sorte que les données peuvent être transmises dans l' $scopearborescence. Nous pouvons changer une valeur sur une valeur plus élevée $scopequi a déjà été digérée. Peut-être que nous changeons une valeur sur le $rootScope.

Si le $digestest sale, nous exécutons à $digestnouveau tout le cycle

Nous parcourons continuellement le $digestcycle jusqu'à ce que le cycle de résumé soit net (toutes les $watchexpressions ont la même valeur que dans le cycle précédent) ou que nous atteignions la limite de résumé. Par défaut, cette limite est fixée à 10.

Si nous atteignons la limite de résumé, AngularJS déclenchera une erreur dans la console:

10 $digest() iterations reached. Aborting!

Le résumé est dur pour la machine mais facile pour le développeur

Comme vous pouvez le voir, chaque fois que quelque chose change dans une application AngularJS, AngularJS vérifie chaque observateur de la $scopehiérarchie pour voir comment répondre. Pour un développeur, il s'agit d'une énorme amélioration de la productivité, car vous devez maintenant écrire presque aucun code de câblage, AngularJS remarquera simplement si une valeur a changé et rendra le reste de l'application cohérent avec le changement.

Du point de vue de la machine, cela est extrêmement inefficace et ralentira notre application si nous créons trop d'observateurs. Misko a cité un chiffre d'environ 4000 observateurs avant que votre application ne se sente lente sur les navigateurs plus anciens.

Cette limite est facile à atteindre si vous avez ng-repeatplus d'un grand JSON arraypar exemple. Vous pouvez éviter cela en utilisant des fonctionnalités telles que la liaison unique pour compiler un modèle sans créer d'observateurs.

Comment éviter de créer trop d'observateurs

Chaque fois que votre utilisateur interagit avec votre application, chaque observateur de votre application sera évalué au moins une fois. Une grande partie de l'optimisation d'une application AngularJS consiste à réduire le nombre d'observateurs dans votre $scopearbre. Une façon simple de le faire est d'utiliser une liaison unique .

Si vous avez des données qui changeront rarement, vous ne pouvez les lier qu'une seule fois en utilisant la syntaxe ::, comme ceci:

<p>{{::person.username}}</p>

ou

<p ng-bind="::person.username"></p>

La liaison ne sera déclenchée que lorsque le modèle conteneur sera rendu et les données chargées dans $scope.

Ceci est particulièrement important lorsque vous en avez un ng-repeatavec de nombreux articles.

<div ng-repeat="person in people track by username">
  {{::person.username}}
</div>
superluminaire
la source
Merci @ user2864740 - bien qu'il soit juste que la réponse de Misko soit top. Il connaît le framework mieux que quiconque, et c'est plutôt cool qu'il s'engage avec Stack Overflow ..
superluminary
4
Je ne suis pas d'accord que cette réponse devrait être au sommet; il y a une différence entre savoir quelque chose et rédiger une réponse pertinente / détaillée pour une question spécifique. Il existe de meilleures façons d'obtenir des distinctions. Quoi qu'il en soit ..
user2864740
1
Je ne doute pas que ce soit vrai, mais questions questions et réponses réponses :)
user2864740
3
Belle réponse couvrant le comportement du chèque sale et ce qu'il évalue réellement, une chose n'était pas trop claire dans la réponse de Misko.
strider
3
Réponse superbe et détaillée. @superluminary, merci pour cette réponse. De plus, après avoir lu cette réponse, j'en arrive au point qu'il ne faut pas ajouter une expression non idempotente comme expression surveillée.
Mangu Singh Rajpurohit
81

Ceci est ma compréhension de base. C'est peut-être faux!

  1. Les éléments sont surveillés en passant une fonction (renvoyant la chose à surveiller) à la $watchméthode.
  2. Les modifications apportées aux éléments surveillés doivent être effectuées dans un bloc de code encapsulé par la $applyméthode.
  3. À la fin de la, $applyla $digestméthode est invoquée qui passe par chacune des montres et vérifie si elles ont changé depuis la dernière exécution $digest.
  4. Si des modifications sont trouvées, le résumé est à nouveau invoqué jusqu'à ce que toutes les modifications se stabilisent.

Dans le développement normal, la syntaxe de liaison de données dans le code HTML indique au compilateur AngularJS de créer les montres pour vous et les méthodes de contrôleur sont $applydéjà exécutées à l'intérieur . Pour le développeur de l'application, tout est transparent.

Pete BD
la source
4
quand la méthode apply est-elle déclenchée?
numan salati
3
@EliseuMonar La boucle de résumé s'exécute à la suite d'un événement ou de l'appel de $ apply (), elle n'est pas appelée périodiquement en fonction d'un temporisateur. voir Comment fonctionne la fonction $ watch d'AngularJS? et comment fonctionne la liaison et la digestion dans AngularJS?
adl
1
@remi, je ne suis pas préoccupé par la dernière version d'AngularJS. Utilisent-ils déjà des proxys ou Object.observe? Sinon, ils sont encore à l'ère de la vérification sale, qui crée une boucle chronométrée pour voir si les attributs du modèle ont changé.
Eliseu Monar dos Santos
1
j'ai lu que le résumé fonctionnera un maximum de dix fois sitepoint.com/understanding-angulars-apply-digest
user137717
62

Je me le demandais moi-même un moment. Sans setters, comment AngularJSremarque-t-on les changements$scope objet? Les interroge-t-il?

Ce qu'il fait est le suivant: tout endroit "normal" dans lequel vous modifiez le modèle a déjà été appelé depuis les entrailles de AngularJS, donc il $applyvous appelle automatiquement après l'exécution de votre code. Supposons que votre contrôleur dispose d'une méthode qui est liée à ng-clickun élément. Parce que le AngularJScâblage de l'appel de cette méthode ensemble pour vous, il a une chance de faire un $applyà l'endroit approprié. De même, pour les expressions qui apparaissent directement dans les vues, celles-ci sont exécutées par AngularJSainsi il fait le $apply.

Lorsque la documentation parle de devoir appeler $applymanuellement du code en dehors deAngularJS , elle parle de code qui, une fois exécuté, ne provient pas de AngularJSlui-même dans la pile des appels.

jpsimons
la source
32

Expliquer avec des images:

La liaison de données a besoin d'un mappage

La référence dans la portée n'est pas exactement la référence dans le modèle. Lorsque vous liez des données à deux objets, vous avez besoin d'un troisième qui écoute le premier et modifie l'autre.

entrez la description de l'image ici

Ici, lorsque vous modifiez le <input>, vous touchez le data-ref3 . Et le mécanisme classique de liaison de données changera data-ref4 . Alors, comment l'autre{{data}} se déplaceront expressions?

Les événements mènent à $ digest ()

entrez la description de l'image ici

Angular maintient une oldValueet newValuede chaque reliure. Et après chaque événement angulaire , la célèbre $digest()boucle vérifiera la WatchList pour voir si quelque chose a changé. Ces événements angulaires sont ng-click, ng-change, $httpterminés ... La $digest()boucle de volonté aussi longtemps que tout oldValuediffère de lanewValue .

Dans l'image précédente, il remarquera que data-ref1 et data-ref2 ont changé.

Conclusions

C'est un peu comme l'oeuf et le poulet. Vous ne savez jamais qui commence, mais j'espère que cela fonctionne la plupart du temps comme prévu.

L'autre point est que vous pouvez facilement comprendre l'impact profond d'une simple liaison sur la mémoire et le CPU. Espérons que les ordinateurs de bureau soient suffisamment gros pour gérer cela. Les téléphones portables ne sont pas si forts.

Nicolas Zozol
la source
22

Évidemment, il n'y a pas de vérification périodique pour Scopesavoir s'il y a un changement dans les objets qui y sont attachés. Tous les objets attachés à la portée ne sont pas surveillés. La portée maintient de manière prototypique un $$ observateurs . Scopeseulement itère à travers cela $$watchersquand $digestest appelé.

Angular ajoute un observateur aux observateurs $$ pour chacun de ces

  1. {{expression}} - Dans vos modèles (et partout où il y a une expression) ou lorsque nous définissons ng-model.
  2. $ scope. $ watch ('expression / function') - Dans votre JavaScript, nous pouvons simplement attacher un objet scope pour angular à regarder.

La fonction $ watch prend trois paramètres:

  1. La première est une fonction d'observation qui renvoie simplement l'objet ou nous pouvons simplement ajouter une expression.

  2. La seconde est une fonction d'écoute qui sera appelée en cas de modification de l'objet. Toutes les choses comme les changements DOM seront implémentées dans cette fonction.

  3. Le troisième étant un paramètre facultatif qui prend un booléen. Si son vrai angulaire profond regarde l'objet et si son faux angulaire fait juste une observation de référence sur l'objet. L'implémentation approximative de $ watch ressemble à ceci

Scope.prototype.$watch = function(watchFn, listenerFn) {
   var watcher = {
       watchFn: watchFn,
       listenerFn: listenerFn || function() { },
       last: initWatchVal  // initWatchVal is typically undefined
   };
   this.$$watchers.push(watcher); // pushing the Watcher Object to Watchers  
};

Il y a une chose intéressante dans Angular appelée Digest Cycle. Le cycle $ digest démarre à la suite d'un appel à $ scope. $ Digest (). Supposons que vous modifiez un modèle $ scope dans une fonction de gestionnaire via la directive ng-click. Dans ce cas, AngularJS déclenche automatiquement un cycle $ digest en appelant $ digest (). En plus de ng-click, il existe plusieurs autres directives / services intégrés qui vous permettent de changer de modèle (par exemple ng-model, $ timeout, etc.) et déclencher automatiquement un cycle $ digest. L'implémentation approximative de $ digest ressemble à ceci.

Scope.prototype.$digest = function() {
      var dirty;
      do {
          dirty = this.$$digestOnce();
      } while (dirty);
}
Scope.prototype.$$digestOnce = function() {
   var self = this;
   var newValue, oldValue, dirty;
   _.forEach(this.$$watchers, function(watcher) {
          newValue = watcher.watchFn(self);
          oldValue = watcher.last;   // It just remembers the last value for dirty checking
          if (newValue !== oldValue) { //Dirty checking of References 
   // For Deep checking the object , code of Value     
   // based checking of Object should be implemented here
             watcher.last = newValue;
             watcher.listenerFn(newValue,
                  (oldValue === initWatchVal ? newValue : oldValue),
                   self);
          dirty = true;
          }
     });
   return dirty;
 };

Si nous utilisons la fonction setTimeout () de JavaScript pour mettre à jour un modèle de portée, Angular n'a aucun moyen de savoir ce que vous pourriez changer. Dans ce cas, il est de notre responsabilité d'appeler $ apply () manuellement, ce qui déclenche un cycle de $ digest. De même, si vous avez une directive qui configure un écouteur d'événement DOM et modifie certains modèles à l'intérieur de la fonction de gestionnaire, vous devez appeler $ apply () pour garantir que les modifications prennent effet. La grande idée de $ apply est que nous pouvons exécuter du code qui ne connaît pas Angular, ce code peut encore changer les choses sur la portée. Si nous encapsulons ce code dans $ apply, il se chargera d'appeler $ digest (). Implémentation approximative de $ apply ().

Scope.prototype.$apply = function(expr) {
       try {
         return this.$eval(expr); //Evaluating code in the context of Scope
       } finally {
         this.$digest();
       }
};
Sasank Sunkavalli
la source
15

AngularJS gère le mécanisme de liaison de données à l'aide de trois fonctions puissantes: $ watch () , $ digest () et $ apply () . La plupart du temps, AngularJS appellera $ scope. $ Watch () et $ scope. $ Digest (), mais dans certains cas, vous devrez peut-être appeler ces fonctions manuellement pour mettre à jour avec de nouvelles valeurs.

$ watch () : -

Cette fonction est utilisée pour observer les changements d'une variable sur la portée $. Il accepte trois paramètres: expression, écouteur et objet d'égalité, où l'écouteur et l'objet d'égalité sont des paramètres facultatifs.

$ digest () -

Cette fonction parcourt toutes les surveillances de l'objet $ scope et ses objets enfant $ scope
(le cas échéant). Lorsque $ digest () parcourt les surveillances, il vérifie si la valeur de l'expression a changé. Si la valeur a changé, AngularJS appelle l'écouteur avec la nouvelle valeur et l'ancienne valeur. La fonction $ digest () est appelée chaque fois que AngularJS pense que cela est nécessaire. Par exemple, après un clic sur un bouton ou après un appel AJAX. Dans certains cas, AngularJS n'appelle pas la fonction $ digest () pour vous. Dans ce cas, vous devez l'appeler vous-même.

$ appliquer () -

Angular ne met automatiquement à jour que les changements de modèle qui se trouvent dans le contexte AngularJS. Lorsque vous modifiez un modèle en dehors du contexte angulaire (comme les événements DOM du navigateur, setTimeout, XHR ou des bibliothèques tierces), vous devez informer Angular des changements en appelant $ apply () manuellement. Lorsque l'appel de la fonction $ apply () se termine, AngularJS appelle $ digest () en interne, donc toutes les liaisons de données sont mises à jour.

Bharath Kumar
la source
7

Il est arrivé que je devais lier un modèle de données d'une personne à un formulaire, ce que j'ai fait était une cartographie directe des données avec le formulaire.

Par exemple, si le modèle avait quelque chose comme:

$scope.model.people.name

L'entrée de contrôle du formulaire:

<input type="text" name="namePeople" model="model.people.name">

De cette façon, si vous modifiez la valeur du contrôleur d'objet, cela se reflétera automatiquement dans la vue.

Un exemple où j'ai passé le modèle est mis à jour à partir des données du serveur est lorsque vous demandez un code postal et un code postal basés sur des charges écrites une liste des colonies et des villes associées à cette vue, et par défaut définissez la première valeur avec l'utilisateur. Et cela, j'ai très bien fonctionné, ce qui arrive, c'est que cela angularJSprend parfois quelques secondes pour rafraîchir le modèle, pour ce faire, vous pouvez mettre un spinner tout en affichant les données.

gartox
la source
14
J'ai lu cette réponse 5 fois et je ne comprends toujours pas ce que l'on entend ici.
sbeduline
1
La réponse me semble être un casse-tête
Aman
6
  1. La liaison de données unidirectionnelle est une approche dans laquelle une valeur est extraite du modèle de données et insérée dans un élément HTML. Il n'y a aucun moyen de mettre à jour le modèle à partir de la vue. Il est utilisé dans les systèmes de modèles classiques. Ces systèmes lient les données dans une seule direction.

  2. La liaison de données dans les applications angulaires est la synchronisation automatique des données entre le modèle et les composants de vue.

La liaison de données vous permet de traiter le modèle comme la source de vérité unique dans votre application. La vue est une projection du modèle à tout moment. Si le modèle est modifié, la vue reflète le changement et vice versa.

Shankar Gangadhar
la source
5

Voici un exemple de liaison de données avec AngularJS, à l'aide d'un champ de saisie. J'expliquerai plus tard

Code HTML

<div ng-app="myApp" ng-controller="myCtrl" class="formInput">
     <input type="text" ng-model="watchInput" Placeholder="type something"/>
     <p>{{watchInput}}</p> 
</div>

Code AngularJS

myApp = angular.module ("myApp", []);
myApp.controller("myCtrl", ["$scope", function($scope){
  //Your Controller code goes here
}]);

Comme vous pouvez le voir dans l'exemple ci-dessus, AngularJS utilise ng-modelpour écouter et regarder ce qui se passe sur les éléments HTML, en particulier sur les inputchamps. Quand quelque chose arrive, faites quelque chose. Dans notre cas, ng-modelest lié à notre vue, en utilisant la notation moustache{{}} . Tout ce qui est tapé à l'intérieur du champ de saisie s'affiche instantanément à l'écran. Et c'est la beauté de la liaison de données, en utilisant AngularJS dans sa forme la plus simple.

J'espère que cela t'aides.

Voir un exemple de travail ici sur Codepen

AllJs
la source
5

AngularJs prend en charge la liaison de données bidirectionnelle .
Signifie que vous pouvez accéder aux données Affichage -> Contrôleur et contrôleur -> Affichage

Pour Ex.

1)

// If $scope have some value in Controller. 
$scope.name = "Peter";

// HTML
<div> {{ name }} </div>

O / P

Peter

Vous pouvez lier des données dans ng-modelLike: -
2)

<input ng-model="name" />

<div> {{ name }} </div>

Ici, dans l'exemple ci-dessus, quelle que soit l'entrée donnée par l'utilisateur, elle sera visible dans la <div>balise.

Si vous voulez lier l'entrée de html au contrôleur: -
3)

<form name="myForm" ng-submit="registration()">
   <label> Name </lbel>
   <input ng-model="name" />
</form>

Ici, si vous souhaitez utiliser l'entrée namedans le contrôleur,

$scope.name = {};

$scope.registration = function() {
   console.log("You will get the name here ", $scope.name);
};

ng-modellie notre point de vue et le rendre dans l'expression {{ }}.
ng-modelsont les données qui sont montrées à l'utilisateur dans la vue et avec lesquelles l'utilisateur interagit.
Il est donc facile de lier des données dans AngularJs.

ojus kulkarni
la source
4

Angular.js crée un observateur pour chaque modèle que nous créons en vue. Chaque fois qu'un modèle est modifié, une classe "ng-dirty" est ajoutée au modèle, donc l'observateur observera tous les modèles qui ont la classe "ng-dirty" et mettra à jour leurs valeurs dans le contrôleur et vice versa.

Shankar Gangadhar
la source
3

liaison de données:

Qu'est-ce que la liaison de données?

Chaque fois que l'utilisateur modifie les données dans la vue, il se produit une mise à jour de ce changement dans le modèle d'étendue et vice versa.

Comment est-ce possible?

Réponse courte: Avec l'aide du cycle de digestion.

Description: js angulaire définit l'observateur sur le modèle d'oscilloscope, ce qui déclenche la fonction d'écoute s'il y a un changement dans le modèle.

$scope.$watch('modelVar' , function(newValue,oldValue){

// Code de mise à jour Dom avec une nouvelle valeur

});

Alors, quand et comment la fonction de surveillance est-elle appelée?

La fonction d'observateur est appelée dans le cadre du cycle de digestion.

Le cycle de résumé est appelé déclenché automatiquement dans le cadre de js angulaires intégrés dans des directives / services comme ng-model, ng-bind, $ timeout, ng-click et autres .. qui vous permettent de déclencher le cycle de résumé.

Fonction de cycle de digestion:

$scope.$digest() -> digest cycle against the current scope.
$scope.$apply() -> digest cycle against the parent scope 

c'est à dire$rootScope.$apply()

Remarque: $ apply () est égal à $ rootScope. $ Digest () cela signifie que la vérification incorrecte commence directement à partir de la racine ou du sommet ou de la portée parent jusqu'à toutes les portées $ enfant dans l'application js angulaire.

Les fonctionnalités ci-dessus fonctionnent également dans les navigateurs IE pour les versions mentionnées simplement en vous assurant que votre application est une application js angulaire, ce qui signifie que vous utilisez le fichier de script de framework angularjs référencé dans la balise de script.

Je vous remercie.

Dhana
la source