web-dev-qa-db-fra.com

Comment utiliser $ scope. $ Watch et $ scope. $ Dans AngularJS?

Je ne comprends pas comment utiliser $scope.$watch et $scope.$apply. La documentation officielle n'est pas utile.

Ce que je ne comprends pas spécifiquement:

  • Sont-ils connectés au DOM?
  • Comment puis-je mettre à jour les modifications DOM dans le modèle?
  • Quel est le point de connexion entre eux?

J'ai essayé ce tutoriel , mais il faut que la compréhension de $watch et $apply soit acquise.

Que font $apply et $watch et comment puis-je les utiliser correctement?

1066
ilyo

Vous devez savoir comment fonctionne AngularJS pour le comprendre.

Digest cycle et $ scope

Avant tout, AngularJS définit un concept de cycle de digestion . Ce cycle peut être considéré comme une boucle au cours de laquelle AngularJS vérifie si toutes les variables observées sont modifiées par tous les $scopes. Donc, si vous avez défini $scope.myVar dans votre contrôleur et que cette variable était marquée comme surveillée , alors vous demandez implicitement à AngularJS de surveiller les modifications. on myVar à chaque itération de la boucle.

Une question de suivi naturelle serait: Est-ce que tout ce qui est lié à $scope est surveillé? Heureusement non. Si vous surveillez les modifications apportées à chaque objet de votre $scope, alors une boucle de digestion prendrait du temps à évaluer et des problèmes de performances se poseraient rapidement. C'est pourquoi l'équipe AngularJS nous a donné deux manières de déclarer une variable $scope surveillée (voir ci-dessous).

$ watch aide à écouter les modifications de la portée de $

Il existe deux façons de déclarer une variable $scope comme étant surveillée.

  1. En l'utilisant dans votre template via l'expression <span>{{myVar}}</span>
  2. En l'ajoutant manuellement via le service $watch

Ad 1) C'est le scénario le plus courant et je suis sûr que vous l'avez déjà vu, mais vous ne saviez pas que cela a créé une montre en arrière-plan. Oui, c'était le cas! L'utilisation de directives AngularJS (telles que ng-repeat) peut également créer des surveillances implicites.

Ad 2) C’est ainsi que vous créez vos propres montres . Le service $watch vous aide à exécuter du code lorsqu'une valeur attachée au $scope a été modifiée. Il est rarement utilisé, mais est parfois utile. Par exemple, si vous souhaitez exécuter du code chaque fois que "myVar" change, vous pouvez procéder comme suit:

function MyController($scope) {

    $scope.myVar = 1;

    $scope.$watch('myVar', function() {
        alert('hey, myVar has changed!');
    });

    $scope.buttonClicked = function() {
        $scope.myVar = 2; // This will trigger $watch expression to kick in
    };
}

$ apply permet d'intégrer les changements au cycle de résumé

Vous pouvez penser à la fonction $apply comme à un mécanisme d'intégration . Vous voyez, chaque fois que vous modifiez directement une variable surveillée attachée à l'objet $scope, AngularJS saura que le changement a eu lieu. En effet, AngularJS savait déjà surveiller ces changements. Donc, si cela se produit dans un code géré par le framework, le cycle de digestion se poursuivra.

Cependant, vous souhaitez parfois modifier une valeur en dehors du monde AngularJS et voir les modifications se propager normalement. Considérez ceci - vous avez une valeur $scope.myVar qui sera modifiée dans le gestionnaire $.ajax() de jQuery. Cela se produira à un moment donné dans le futur. AngularJS ne peut pas attendre que cela se produise, car il n'a pas été chargé d'attendre jQuery.

Pour remédier à cela, $apply a été introduit. Il vous permet de démarrer le cycle de digestion de manière explicite. Cependant, vous ne devriez l'utiliser que pour migrer certaines données vers AngularJS (intégration avec d'autres frameworks), mais n'utilisez jamais cette méthode combinée avec du code AngularJS normal, car AngularJS lançera alors une erreur.

Comment tout cela est-il lié au DOM?

Eh bien, vous devriez vraiment suivre le tutoriel à nouveau, maintenant que vous savez tout cela. Le cycle de résumé veillera à ce que l'interface utilisateur et le code JavaScript restent synchronisés, en évaluant chaque observateur associé à tous les $scopes tant que rien ne change. Si plus aucune modification ne se produit dans la boucle de digestion, elle est considérée comme terminée.

Vous pouvez attacher des objets à l'objet $scope, soit explicitement dans le contrôleur, soit en les déclarant au format {{expression}} directement dans la vue.

J'espère que cela aide à clarifier certaines connaissances de base sur tout cela.

Lectures supplémentaires:

1717
ŁukaszBachman

Dans AngularJS, nous mettons à jour nos modèles et nos vues/modèles mettent à jour le DOM "automatiquement" (via des directives intégrées ou personnalisées).

$ apply et $ watch, toutes deux méthodes Scope, ne sont pas liées au DOM.

La page Concepts (section "Runtime") contient une assez bonne explication de la boucle $ digest, de $ apply, de la file d'attente $ evalAsync et de la liste de surveillance $. Voici la photo qui accompagne le texte:

$digest loop

Quel que soit le code ayant accès à une portée - normalement les contrôleurs et les directives (leurs fonctions de liaison et/ou leurs contrôleurs) - peut configurer un " watchExpression " que AngularJS évaluera par rapport à cette portée. Cette évaluation a lieu à chaque fois que AngularJS entre dans sa boucle $ digest (en particulier la boucle "$ watch list"). Vous pouvez regarder les propriétés individuelles des portées, vous pouvez définir une fonction pour regarder deux propriétés ensemble, vous pouvez regarder la longueur d'un tableau, etc.

Lorsque quelque chose se passe "dans AngularJS" - par exemple, vous tapez dans une zone de texte pour laquelle la liaison de données bidirectionnelle AngularJS est activée (c'est-à-dire, utilise le modèle ng), un rappel $ http se déclenche, etc. - $ apply a déjà été appelé. Vous vous trouvez dans le rectangle "AngularJS" de la figure ci-dessus. Toutes les watchExpressions seront évaluées (éventuellement plus d'une fois, jusqu'à ce qu'aucun changement ne soit détecté).

Lorsque des événements se produisent "en dehors de AngularJS" - par exemple, vous avez utilisé bind () dans une directive, puis cet événement se déclenche, ce qui entraîne l'appel de votre rappel ou des rappels enregistrés par jQuery - nous sommes toujours dans le rectangle "natif". Si le code de rappel modifie tout ce que surveille $ $, appelez $ apply pour entrer dans le rectangle AngularJS, ce qui entraînera l'exécution de la boucle $ digérer, et AngularJS remarquera donc le changement et réalisera sa magie.

157
Mark Rajcok

Ce blog a été couvert tout en créant des exemples et des explications compréhensibles.

Les fonctions AngularJS $scope$watch(), $digest() et $apply() font partie des fonctions centrales d’AngularJS. Comprendre $watch(), $digest() et $apply() est essentiel pour comprendre AngularJS.

Lorsque vous créez une liaison de données quelque part dans votre vue avec une variable de l'objet $ scope, AngularJS crée une "surveillance" en interne. Une montre signifie que AngularJS surveille les modifications de la variable sur le $scope object. Le cadre "surveille" la variable. Les montres sont créées à l'aide de la fonction $scope.$watch() que je traiterai plus loin dans ce texte.

Aux points clés de votre application, AngularJS appelle la fonction $scope.$digest(). Cette fonction parcourt toutes les surveillances et vérifie si l’une des variables surveillées a changé. Si une variable surveillée a changé, une fonction d'écoute correspondante est appelée. La fonction d'écoute effectue tout le travail nécessaire, par exemple modifier un texte HTML pour refléter la nouvelle valeur de la variable surveillée. Ainsi, la fonction $digest() déclenche la mise à jour de la liaison de données.

La plupart du temps, AngularJS appellera les fonctions $ scope. $ Watch () et $scope.$digest() pour vous, mais dans certaines situations, vous devrez peut-être les appeler vous-même. Par conséquent, il est vraiment bon de savoir comment ils fonctionnent.

La fonction $scope.$apply() est utilisée pour exécuter du code, puis appelle $scope.$digest(), afin que toutes les surveillances soient vérifiées et que les fonctions correspondantes soient écoutées. La fonction $apply() est utile pour intégrer AngularJS à un autre code.

Je donnerai plus de détails sur les fonctions $watch(), $digest() et $apply() dans la suite de ce texte.

$ regarder ()

La fonction $scope.watch() crée une surveillance d'une variable. Lorsque vous enregistrez une montre, vous transmettez deux fonctions en tant que paramètres à la fonction $watch():

  • Une fonction de valeur
  • Une fonction d'écoute

Voici un exemple:

$scope.$watch(function() {},
              function() {}
             );

La première fonction est la fonction valeur et la deuxième fonction est la fonction auditeur.

La fonction de valeur devrait retourner la valeur qui est surveillée. AngularJS peut alors comparer la valeur renvoyée à la valeur que la fonction de surveillance a renvoyée la dernière fois. De cette façon, AngularJS peut déterminer si la valeur a changé. Voici un exemple:

$scope.$watch(function(scope) { return scope.data.myVar },
              function() {}
             );

Cet exemple de fonction valule renvoie la variable $scope _ scope.data.myVar. Si la valeur de cette variable change, une valeur différente sera renvoyée et AngularJS appellera la fonction d'écoute.

Remarquez comment la fonction value prend l'étendue en paramètre (sans le $ dans le nom). Via ce paramètre, la fonction value peut accéder au $scope et à ses variables. La fonction value peut également afficher des variables globales si vous en avez besoin, mais le plus souvent, vous observerez une variable $scope.

La fonction d'écoute doit faire ce qu'elle doit faire si la valeur a changé. Peut-être devrez-vous modifier le contenu d'une autre variable ou définir le contenu d'un élément HTML ou quelque chose de ce genre. Voici un exemple:

$scope.$watch(function(scope) { return scope.data.myVar },
              function(newValue, oldValue) {
                  document.getElementById("").innerHTML =
                      "" + newValue + "";
              }
             );

Cet exemple définit le code HTML interne d'un élément HTML sur la nouvelle valeur de la variable, intégrée à l'élément b, qui met la valeur en gras. Bien sûr, vous auriez pu le faire en utilisant le code {{ data.myVar }, mais il ne s'agit que d'un exemple de ce que vous pouvez faire dans la fonction écouteur.

$ digérer ()

La fonction $scope.$digest() parcourt toutes les surveillances du $scope object et de ses objets scope $ enfant (le cas échéant). Lorsque $digest() itère sur les montres, il appelle la fonction de valeur pour chaque montre. Si la valeur renvoyée par la fonction value est différente de la valeur renvoyée la dernière fois qu'elle a été appelée, la fonction d'écoute de cette surveillance est appelée.

La fonction $digest() est appelée chaque fois que AngularJS le juge nécessaire. Par exemple, après l'exécution d'un gestionnaire de clic sur un bouton ou après le retour d'un appel AJAX (après l'exécution de la fonction de rappel done ()/fail ()).

Vous pouvez rencontrer certains cas où AngularJS n'appelle pas la fonction $digest() pour vous. Vous le détecterez généralement en remarquant que les liaisons de données ne mettent pas à jour les valeurs affichées. Dans ce cas, appelez $scope.$digest() et cela devrait fonctionner. Ou, vous pouvez peut-être utiliser $scope.$apply() à la place, ce que je vais expliquer dans la section suivante.

$ appliquer ()

La fonction $scope.$apply() prend une fonction en tant que paramètre qui est exécuté, puis $scope.$digest() est appelée en interne. Cela vous permet de vous assurer que toutes les surveillances sont vérifiées et que toutes les liaisons de données sont actualisées. Voici un exemple de $apply():

$scope.$apply(function() {
    $scope.data.myVar = "Another value";
});

La fonction transmise à la fonction $apply() en tant que paramètre modifiera la valeur de $scope.data.myVar. À la sortie de la fonction, AngularJS appellera la fonction $scope.$digest() afin que toutes les surveillances soient contrôlées afin de détecter toute modification des valeurs surveillées.

Exemple

Pour illustrer le fonctionnement de $watch(), $digest() et de $apply(), regardez cet exemple:

<div ng-controller="myController">
    {{data.time}}

    <br/>
    <button ng-click="updateTime()">update time - ng-click</button>
    <button id="updateTimeButton"  >update time</button>
</div>


<script>
    var module       = angular.module("myapp", []);
    var myController1 = module.controller("myController", function($scope) {

        $scope.data = { time : new Date() };

        $scope.updateTime = function() {
            $scope.data.time = new Date();
        }

        document.getElementById("updateTimeButton")
                .addEventListener('click', function() {
            console.log("update time clicked");
            $scope.data.time = new Date();
        });
    });
</script>

cet exemple lie la variable $scope.data.time à une directive d'interpolation qui fusionne la valeur de la variable dans la page HTML. Cette liaison crée une surveillance interne sur le $scope.data.time variable.

L'exemple contient également deux boutons. Un écouteur ng-click est attaché au premier bouton. Lorsque ce bouton est cliqué, la fonction $scope.updateTime() est appelée, puis AngularJS appelle $scope.$digest() afin que les liaisons de données soient mises à jour.

Le second bouton permet d'y associer un écouteur d'événements JavaScript standard depuis la fonction de contrôleur. Lorsque le deuxième bouton est cliqué, cette fonction d'écoute est exécutée. Comme vous pouvez le constater, les fonctions d'écoute des deux boutons ont pratiquement la même fonction, mais lorsque la fonction d'écoute du second bouton est appelée, la liaison de données n'est pas mise à jour. En effet, la $scope.$digest() n'est pas appelée après l'exécution du programme d'écoute d'événements du second bouton. Ainsi, si vous cliquez sur le deuxième bouton, l'heure est mise à jour dans la variable $scope.data.time, mais la nouvelle heure ne s'affiche jamais.

Pour résoudre ce problème, nous pouvons ajouter un appel $scope.$digest() à la dernière ligne de l'écouteur d'événements de boutons, comme suit:

document.getElementById("updateTimeButton")
        .addEventListener('click', function() {
    console.log("update time clicked");
    $scope.data.time = new Date();
    $scope.$digest();
});

Au lieu d'appeler $digest() à l'intérieur de la fonction d'écoute de bouton, vous auriez également pu utiliser la fonction $apply() comme ceci:

document.getElementById("updateTimeButton")
        .addEventListener('click', function() {
    $scope.$apply(function() {
        console.log("update time clicked");
        $scope.data.time = new Date();
    });
});

Remarquez comment la fonction $scope.$apply() est appelée à partir de l'écouteur d'événements de bouton et comment la mise à jour de la variable $scope.data.time est effectuée à l'intérieur de la fonction passée en paramètre à la fonction $apply(). Lorsque l'appel de la fonction $apply() est terminé, AngularJS appelle $digest() en interne, de sorte que toutes les liaisons de données sont mises à jour.

64
Alex Jolig

AngularJS étend cette boucle d'événements en créant quelque chose appelé AngularJS context.

$ watch ()

Chaque fois que vous liez quelque chose dans l'interface utilisateur, vous insérez un $watch DANS UNE LISTE $watch.

User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />

Ici, nous avons $scope.user, qui est lié à la première entrée, et nous avons $scope.pass, qui est lié à la seconde. Pour ce faire, nous ajoutons deux $watchES À LA LISTE $watch.

Lorsque notre modèle est chargé, AKA dans la phase de liaison, le compilateur recherchera toutes les directives et créera tous les $watches nécessaires.

AngularJS fournit $watch, $watchcollection et $watch(true). Vous trouverez ci-dessous un diagramme détaillé expliquant les trois tirés de observateurs en profondeur .

Enter image description here

angular.module('MY_APP', []).controller('MyCtrl', MyCtrl)
function MyCtrl($scope,$timeout) {
  $scope.users = [{"name": "vinoth"},{"name":"yusuf"},{"name":"rajini"}];

  $scope.$watch("users", function() {
    console.log("**** reference checkers $watch ****")
  });

  $scope.$watchCollection("users", function() {
    console.log("**** Collection  checkers $watchCollection ****")
  });

  $scope.$watch("users", function() {
    console.log("**** equality checkers with $watch(true) ****")
  }, true);

  $timeout(function(){
     console.log("Triggers All ")
     $scope.users = [];
     $scope.$digest();

     console.log("Triggers $watchCollection and $watch(true)")
     $scope.users.Push({ name: 'Thalaivar'});
     $scope.$digest();

     console.log("Triggers $watch(true)")
     $scope.users[0].name = 'Superstar';
     $scope.$digest();
  });
}

http://jsfiddle.net/2Lyn0Lkb/

Boucle $digest

Lorsque le navigateur reçoit un événement pouvant être géré par le contexte AngularJS, la boucle $digest est déclenchée. Cette boucle est composée de deux boucles plus petites. L'un traite la file d'attente $evalAsync et l'autre traite le $watch list. Le $digest parcourra la liste de $watch que nous avons

app.controller('MainCtrl', function() {
  $scope.name = "vinoth";

  $scope.changeFoo = function() {
      $scope.name = "Thalaivar";
  }
});

{{ name }}
<button ng-click="changeFoo()">Change the name</button>

Ici, nous n’avons qu’un $watch car ng-click ne crée aucune surveillance.

Nous appuyons sur le bouton.

  1. Le navigateur reçoit un événement qui entrera dans le contexte AngularJS
  2. La boucle $digest sera exécutée et demandera à chaque surveillance de faire des changements.
  3. Étant donné que $watch qui surveillait les modifications dans $ scope.name rapporte une modification, il forcera une autre boucle $digest.
  4. La nouvelle boucle ne rapporte rien.
  5. Le navigateur récupère le contrôle et met à jour le DOM en tenant compte de la nouvelle valeur de $ scope.name
  6. La chose importante ici est que CHAQUE événement entrant dans le contexte AngularJS exécute une boucle $digest. Cela signifie que chaque fois que nous écrivons une lettre dans une entrée, la boucle sera exécutée en vérifiant tous les $watch de cette page.

$ appliquer ()

Si vous appelez $apply lorsqu’un événement est déclenché, il passera par le contexte angulaire, mais si vous ne l’appelez pas, il se déroulera en dehors de celui-ci. C'est aussi facile que ça. $apply APPELLERA LA BOUCLE $digest() en interne et itérera sur toutes les surveillances pour s'assurer que le DOM est mis à jour avec la nouvelle valeur mise à jour.

La méthode $apply() déclenchera des observateurs sur toute la chaîne $scope tandis que la méthode $digest() ne déclenchera que des observateurs sur le $scope actuel et son children. Lorsqu'aucun des objets $scope supérieurs ne doit connaître les modifications locales, vous pouvez utiliser $digest(). .

45
Thalaivar

Il existe également $watchGroup et $watchCollection. Plus précisément, $watchGroup est très utile si vous souhaitez appeler une fonction pour mettre à jour un objet possédant plusieurs propriétés dans une vue qui n'est pas un objet dom, par exemple. autre vue dans canvas, webGL ou requête du serveur. Ici, la documentation lien .

17
Utkarsh Bhardwaj

J'ai trouvé des vidéos très détaillées couvrant $watch, $apply, $digest et résument les cycles dans:

Vous trouverez ci-dessous quelques diapositives utilisées dans ces vidéos pour expliquer les concepts (juste au cas où, si les liens ci-dessus sont supprimés/ne fonctionnent pas).

Enter image description here

Dans l'image ci-dessus, "$ scope.c" n'est pas regardé car il n'est utilisé dans aucune des liaisons de données (dans le balisage). Les deux autres ($scope.a et $scope.b) seront regardés.

Enter image description here

A partir de l'image ci-dessus: En fonction de l'événement de navigateur respectif, AngularJS enregistre l'événement, effectue un cycle de résumé (vérifie toutes les modifications), exécute les fonctions de surveillance et met à jour le DOM. S'il ne s'agit pas d'événements de navigateur, le cycle de résumé peut être déclenché manuellement à l'aide de $apply ou $digest.

En savoir plus sur $apply et $digest:

Enter image description here

17
user203687

Vient de finir de lire TOUT ce qui précède, ennuyeux et somnolent (désolé mais c'est vrai). Très technique, en profondeur, détaillé et sec. Pourquoi est-ce que j'écris? Comme AngularJS est énorme, de nombreux concepts interconnectés peuvent rendre fou tout le monde. Je me suis souvent demandé si je ne suis pas assez intelligent pour les comprendre. Non! C'est parce que si peu de gens peuvent expliquer la technologie dans un langage factice sans toutes les terminologies! Ok, laissez-moi essayer:

1) Ce sont toutes des choses événementielles. (J'entends le rire, mais lisez la suite)

Si vous ne savez pas ce que l'événement est conduit, alors pensez à placer un bouton sur la page, associez-le à une fonction à l'aide de "sur-clic", en attendant que les utilisateurs cliquent dessus pour déclencher les actions que vous insérez dans la page. une fonction. Ou pensez au "déclencheur" de SQL Server/Oracle.

2) $ watch est "sur le clic".

La particularité est qu'il faut 2 fonctions en tant que paramètres, le premier donne la valeur de l'événement, le second prend en compte la valeur ...

) $ digest est le patron qui vérifie sans relâche, bla-bla-bla mais un bon patron.

4) $ apply vous indique comment procéder lorsque vous souhaitez le faire manuellement, comme un système anti-échec (au cas où le clic ne déclencherait pas, vous le forceriez à s'exécuter.)

Maintenant, rendons-le visuel. Imaginez ceci pour le rendre encore plus facile à saisir:

Dans un restaurant,

- Les serveurs sont supposés prendre les commandes des clients, c’est

$watch(
  function(){return orders;},
  function(){Kitchen make it;}
);

- MANAGER se promène pour s'assurer que tous les serveurs sont réveillés, réactifs à tout signe de changement de la part des clients. C'est $digest()

- PROPRIÉTAIRE a le pouvoir ultime de conduire tout le monde à la demande, il s'agit de $apply()

12
Jeb50