Comment puis-je utiliser $champ d'application.$regarder et $champ d'application.$ appliquer à 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 mettre à jour les modifications DOM sur le modèle?
  • Quel est le point de connexion entre eux?

j'ai essayé ce tutoriel , mais il prend la compréhension de $watch et $apply pour acquis.

que font $apply et $watch , et comment les utiliser de manière appropriée?

1016
demandé sur Peter Mortensen 2013-02-27 16:50:07

7 réponses

vous devez être conscient de la façon dont AngularJS fonctionne afin de le comprendre.

Digérer cycle et $scope

tout d'abord, AngularJS définit un concept d'un soi-disant cycle de digestion . Ce cycle peut être considéré comme une boucle, au cours de laquelle AngularJS vérifie s'il y a des changements à toutes les variables regardé par tous les $scope s. Donc, si vous avez $scope.myVar défini dans votre contrôleur et cette variable était marqué pour être regardé , alors vous dites implicitement AngularJS pour surveiller les changements sur myVar dans chaque itération de la boucle.

une question de suivi naturelle serait: tout ce qui est attaché à $scope est-il surveillé? Heureusement, non. Si vous voulez surveiller les changements à chaque objet dans votre $scope , alors rapidement une boucle digest prendrait des âges pour évaluer et vous exécuteriez rapidement sur les questions de performance. C'est pourquoi L'équipe D'AngularJS nous a donné deux façons de déclarer une certaine variable $scope comme étant observée (lire ci-dessous).

$montre permet d'écouter $à des changements de périmètre

il y a deux façons de déclarer qu'une variable $scope est observée.

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

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

Ad 2) C'est ainsi que vous créez votre propre montres . Le service $watch vous aide à exécuter du code quand une valeur attachée au $scope a modifié. Il est rarement utilisé, mais il est parfois utile. Par exemple, si vous voulez exécuter un code à chaque fois que' myVar ' change, vous pouvez faire ce qui 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 des modifications avec le cycle de digestion

vous pouvez penser à la $apply fonction comme d'un mécanisme d'intégration . Vous voyez, chaque fois que vous changez quelque regarder la variable attachée à la $scope object directement, AngularJS saura que le changement est arrivé. C'est parce Qu'AngularJS savait déjà surveiller ces changements. Ainsi, si cela se produit en code géré par le cadre, le cycle de digest se poursuivra.

cependant, parfois vous voulez changer une certaine valeur en dehors du monde AngularJS et voir les changements se propager normalement. Considérez ceci - vous avez une valeur $scope.myVar qui sera modifiée à l'intérieur d'un jQuery $.ajax() manipulateur. Cela se fera à un certain moment dans l'avenir. AngularJS ne peut pas attendre que cela arrive, car il n'a pas été demandé d'attendre jQuery.

pour s'attaquer à ce problème, $apply a été introduit. Il vous permet de commencer le cycle de digestion explicitement. Cependant, vous ne devez utiliser cette méthode que pour migrer certaines données vers AngularJS (intégration avec d'autres cadres), mais ne jamais utiliser cette méthode combinée avec le code AngularJS régulier, comme AngularJS lancera une erreur alors.

en quoi tout cela est-il lié au DOM?

eh Bien, vous devriez vraiment suivre le tutoriel de nouveau, maintenant que vous savez tout cela. Le cycle de digest s'assurera que L'UI et le code JavaScript restent synchronisés, en évaluant chaque observateur attaché à tous les $scope s tant que rien ne change. Si aucun autre changement ne se produit dans la boucle digest, alors il est considéré comme terminé.

vous pouvez attacher des objets à l'objet $scope soit explicitement dans le Controller, soit en les déclarant dans le formulaire {{expression}} directement dans la vue.

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

lectures complémentaires:

1661
répondu ŁukaszBachman 2017-09-19 11:47:30

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

$ appliquer et $ watch, tous deux étant des méthodes de portée, ne sont pas liés au DOM.

la Concepts page (section "Runtime") a une assez bonne explication de la boucle $digest, $apply, la file d'attente $evalAsync et la liste $watch. 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 mettre en place un watch express " que AngularJS évaluera par rapport à cette portée. Cette évaluation se produit chaque fois Qu'AngularJS entre dans sa boucle $digest (en particulier, la boucle$watch list). Vous pouvez regarder les propriétés individuelles de portée, vous pouvez définir une fonction pour regarder deux propriétés ensemble, vous pouvez regarder la longueur d'un tableau, etc.

quand les choses se produisent "à L'intérieur D'AngularJS" - par exemple, vous tapez dans une boîte de texte qui a la possibilité D'AngularJS bidirectionnel databinding (i.e., utilise le modèle ng), un $http callback lance, etc. - $apply a déjà été appelé, donc nous sommes à l'intérieur du rectangle "AngularJS" dans la figure ci-dessus. Toutes les expressions de veille seront évaluées (peut – être plus d'une fois-jusqu'à ce qu'aucun autre changement ne soit détecté).

quand les choses arrivent "outside AngularJS" – par exemple, vous avez utilisé bind() dans une directive et ensuite cet événement se déclenche, ce qui entraîne l'appel de votre callback, ou des incendies de callback enregistrés par jQuery-nous sommes toujours dans le rectangle" Native". Si le code de callback modifie quelque chose que $watch regarde, call $apply pour entrer dans le rectangle AngularJS, provoquant l'exécution de la boucle $digest, et donc AngularJS remarquera le changement et fera sa magie.

152
répondu Mark Rajcok 2016-02-02 08:17:08

ce blog a été couvert tout ce que la création d'exemples et des explications compréhensibles.

La AngularJS $scope fonctions $watch(), $digest() et $apply() sont certaines des fonctions centrales dans AngularJS. Comprendre $watch() , $digest() et $apply() est essentiel pour comprendre AngularJS.

lorsque vous créez une liaison de données à partir de quelque part dans votre vue à une variable sur le $ scope object, AngularJS crée une" montre " en interne. Une montre signifie que AngularJS montres change dans la variable sur le $scope object . Le cadre "observe" la variable. Les montres sont créées en utilisant la fonction $scope.$watch() que je couvrirai plus tard dans ce texte.

aux points clés de votre application AngularJS appelle la fonction $scope.$digest() . Cette fonction itère à travers toutes les montres et vérifie si l'une des variables observées ont changé. Si un regardé variable a changé, une fonction d'écoute correspondante est appelée. La fonction d'écoute fait tout le travail qu'elle doit faire, par exemple en changeant un texte HTML pour refléter la nouvelle valeur de la variable observée. Ainsi, la fonction $digest() est ce qui déclenche la liaison de données à mettre à jour.

la plupart du temps AngularJS appellera le $scope.$watch() et $scope.$digest() fonctionne pour vous, mais dans certaines situations, vous pouvez appeler vous-même. Par conséquent, il est vraiment bon savoir comment ils fonctionnent.

la fonction $scope.$apply() est utilisée pour exécuter du code, puis appeler $scope.$digest() après cela, de sorte que toutes les montres sont vérifiées et les fonctions d'écoute de montre correspondantes sont appelées. La fonction $apply() est utile lors de l'intégration D'AngularJS avec d'autres codes.

je vais entrer dans plus de détails sur les fonctions $watch(), $digest() et $apply() dans le reste de ce texte.

$watch()

la fonction $scope.watch() crée une montre d'une certaine variable. Lorsque vous enregistrez une montre, vous passez deux fonctions comme 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 de valeur et la seconde fonction est la fonction d'écoute.

la fonction de valeur doit retourner la valeur qui est observée. AngularJS peut alors vérifier la valeur retournée par rapport à la valeur que la fonction watch a retourné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 de valule renvoie la $scope variable scope.data.myVar . Si la valeur de cette variable change, une valeur différente sera retournée, et AngularJS sera appel de la fonction d'écoute.

remarquez comment la fonction value prend le scope comme paramètre (sans le $ dans le nom). Grâce à ce paramètre, la fonction value peut accéder au $scope et à ses variables. La fonction de valeur peut également regarder des variables globales à la place si vous avez besoin de cela, mais le plus souvent vous verrez une variable $scope .

la fonction d'écoute doit faire tout ce qu'elle doit faire si la valeur a changé. Peut-être vous avez besoin de changer le contenu d'une autre variable, ou définir le contenu D'un élément HTML ou quelque chose. Voici un exemple:

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

cet exemple définit le HTML interne d'un élément HTML à la nouvelle valeur de la variable, intégrée à l'élément b qui rend la valeur en gras. Bien sûr, vous auriez pu le faire en utilisant le code {{ data.myVar } , mais ce n'est qu'un exemple de ce que vous pouvez faire à l'intérieur de la fonction listener.

$ digest ()

la fonction $scope.$digest() itère à travers toutes les montres du $scope object , et ses enfants $scope objects (si elle en a). Lorsque $digest() itère au-dessus des montres, il appelle la fonction de valeur pour chaque montre. Si la valeur retournée par la fonction valeur est différente de la valeur qu'elle a retournée la dernière fois qu'elle a été appelée, la fonction d'écoute pour cette montre est appelée.

la fonction $digest() est appelée chaque fois Qu'AngularJS pense qu'elle est 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 des cas de coin où AngularJS n'appelle pas la fonction $digest() pour vous. Vous le détecterez habituellement en remarquant que les liaisons de données ne mettent pas à jour les valeurs affichées. Dans ce cas, appelez $scope.$digest() et ça devrait marcher. Ou, vous pouvez peut-être utiliser $scope.$apply() à la place que je vais expliquer dans la section suivante.

$apply ()

la fonction $scope.$apply() prend une fonction comme paramètre qui est exécutée, et après cela $scope.$digest() est appelé en interne. Cela rend plus facile pour vous de s'assurer que toutes les montres sont vérifiées, et donc toutes les liaisons de données rafraîchies. Voici un exemple $apply() :

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

la fonction est passée à la $apply() fonction comme paramètre va changer la valeur de $scope.data.myVar . Lorsque la fonction sort AngularJS appellera la fonction $scope.$digest() de sorte que toutes les montres sont vérifiées pour les changements dans les valeurs observées.

exemple

Pour illustrer comment $watch() , $digest( ) et $apply() fonctionne, 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>

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

l'exemple contient aussi deux boutons. Le premier bouton est doté d'un écouteur ng-click . Lorsque ce bouton est cliqué la fonction $scope.updateTime() est appelée, et après cela AngularJS appelle $scope.$digest() pour que les liaisons de données soient mises à jour.

le deuxième bouton reçoit un écouteur D'événements JavaScript standard attaché à lui de l'intérieur de la fonction de contrôleur. Lorsque le deuxième bouton est cliqué, cette fonction d'écoute est exécutée. Comme vous pouvez le voir, les fonctions d'écoute pour les deux boutons font presque la même chose, mais lorsque la fonction d'écoute du second bouton est appelée, la liaison de données n'est pas mise à jour. C'est parce que le $scope.$digest() n'est pas appelé après l'exécution de l'écouteur 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 n'est jamais affichée.

pour corriger que nous pouvons ajouter un $scope.$digest() appel à la dernière ligne de l'écouteur d'événement bouton, comme ceci:

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 écouteur bouton, vous auriez pu également 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 de l'intérieur de l'écouteur d'événements bouton, et comment la mise à jour de la variable $scope.data.time est exécuté à l'intérieur de la fonction passée comme paramètre à la fonction $apply() . Lorsque la fonction $apply() termine L'appel AngularJS appelle $digest() en interne, de sorte que toutes les liaisons de données sont mises à jour.

56
répondu Alex Jolig 2016-02-22 10:13:37

AngularJS étend ce events-loop , créant quelque chose appelé AngularJS context .

$watch ()

chaque fois que vous liez quelque chose dans L'UI vous insérez un $watch dans un $watch liste .

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

nous avons Ici $scope.user , qui est liée à la première entrée, et nous avons $scope.pass , qui est lié à la seconde un. Ce faisant, nous ajoutons deux $watch à la $watch liste .

quand notre template est chargé, AKA dans la phase de liaison, le compilateur va chercher chaque directive et crée tous les $watch es qui sont nécessaires.

AngularJS fournit $watch , $watchcollection et $watch(true) . Ci-dessous est un diagramme soigné expliquant tous les trois pris 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 /

$digest boucle

lorsque le navigateur reçoit un événement qui peut être géré par le contexte AngularJS la boucle $digest sera activée. Cette boucle est faite à partir de deux petites boucles. L'une traite la file d'attente $evalAsync , et l'autre on traite le $watch list . Le $digest boucle à travers 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 avons seulement un $watch parce que ng-click ne crée pas de montres.

on appuie sur le bouton.

  1. le navigateur reçoit un événement qui entrera dans le contexte AngularJS
  2. la boucle $digest s'exécute et demande à chaque $watch des modifications.
  3. depuis le $watch qui surveillait les changements dans $scope.nom signale un changement, il va forcer une autre boucle $digest .
  4. la nouvelle boucle ne rapporte rien.
  5. le navigateur récupère le contrôle et mettra à jour le DOM reflétant la nouvelle valeur de $ scope.nom
  6. ce qui est important ici, c'est que chaque événement qui entre dans le contexte AngularJS s'exécute en boucle $digest . Cela signifie que que chaque fois que nous écrivons une lettre dans une entrée, la boucle s'exécute en vérifiant chaque $watch dans cette page.

$apply ()

Si vous appelez $apply lorsqu'un événement est déclenché, il faudra passer par la angulaires-le contexte, mais si vous ne l'appelez pas, il sera exécuté à l'extérieur. C'est aussi simple que ça. $apply appellera la boucle $digest() en interne et il itérera sur toutes les montres pour assurer la DOM est mis à jour avec la valeur nouvellement mise à jour.

la méthode $apply() déclenchera les observateurs sur toute la chaîne $scope tandis que la méthode $digest() ne déclenchera les observateurs que sur le courant $scope et son children . quand aucun des objets plus élevés $scope n'a besoin de connaître les changements locaux, vous pouvez utiliser $digest() .

37
répondu Thalaivar 2017-01-07 15:14:54

il y a aussi $watchGroup et $watchCollection . Plus précisément, $watchGroup est vraiment utile si vous voulez appeler une fonction pour mettre à jour un objet qui a plusieurs propriétés dans une vue qui n'est pas un objet dom, par exemple pour une autre vue dans une requête canvas, webGL ou server. Ici, la documentation lien .

17
répondu Utkarsh Bhardwaj 2015-04-08 05:29:37

j'ai trouvé des vidéos très approfondies qui couvrent $watch , $apply , $digest et de digérer les cycles dans:

voici 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 markup). Les deux autres ( $scope.a et $scope.b ) seront regardées.

Enter image description here

de l'image ci-dessus: basé sur l'événement de navigateur respectif, AngularJS capture l'événement, effectue le cycle digest (passe par toutes les montres pour les changements), exécuter des fonctions de veille et mettre à jour le DOM. Si ce n'est pas le cas, le cycle de digest peut être déclenché manuellement en utilisant $apply ou $digest .

plus d'informations sur $apply et $digest :

Enter image description here

15
répondu user203687 2017-01-07 15:08:25

il suffit de finir de lire tout ce qui précède, ennuyeux et somnolent (désolé mais est vrai). Très technique, en profondeur, précis et secs. Pourquoi suis-je écrire? Parce Qu'AngularJS est énorme, beaucoup de concepts connectés peuvent rendre n'importe qui fou. Je me suis demandé souvent, je ne suis pas assez intelligent pour le comprendre? Pas de! C'est parce que si peu peuvent expliquer la technologie dans un for-dummie langue avec tous les terminologies! Ok, laissez-moi essayer:

1) Ils sont tous pilotés par les événements choses. (j'entends le rire, mais lisez la suite)

si vous ne savez pas ce qui est motivé par l'événement pensez vous placez un bouton sur la page, branchez-le avec une fonction "ON-click", en attente de les utilisateurs à cliquer sur elle pour déclencher les actions que vous plantez à l'intérieur de la fonction. Ou pensez à "trigger" de SQL Server / Oracle.

2) $montre est "sur clic".

ce qui est spécial au sujet est qu'il prend 2 fonctions comme paramètres, d'abord un donne la valeur de l'événement, le second prend la valeur en considération...

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

4) $apply vous donne le chemin quand vous voulez le faire manuellement , comme un fail-proof (dans le cas où on-click ne donne pas de coup de pied dans, vous forcer à exécuter.)

maintenant, rendons ça visuel. Imaginez ceci pour le rendre encore plus facile de saisir l'idée:

dans un restaurant,

- serveurs sont censés prendre des commandes de clients, c'est

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

- gestionnaire courir partout pour s'assurer que tous les serveurs sont éveillés, répondant à tout signe de changement de la part des clients. C'est $digest()

- propriétaire a le pouvoir ultime de conduire tout le monde sur demande, c'est $apply()

10
répondu Jeb50 2017-02-07 01:20:51