253 votes

Quelle est la différence entre markForCheck() et detectChanges() ?

Quelle est la différence entre ChangeDetectorRef.markForCheck() y ChangeDetectorRef.detectChanges() ?

J'ai seulement a trouvé des informations sur le SO quant à la différence entre NgZone.run() mais pas entre ces deux fonctions.

Pour les réponses ne faisant référence qu'au document, veuillez illustrer quelques scénarios pratiques permettant de choisir l'un plutôt que l'autre.

331voto

Milad Points 12206

detectChanges() : void

Vérifie le détecteur de changement et ses enfants.

En d'autres termes, si un élément de votre modèle (votre classe) a été modifié mais n'a pas été répercuté sur la vue, vous devrez peut-être demander à Angular de détecter ces modifications (détection des modifications locales) et de mettre à jour la vue.

Les scénarios possibles pourraient être :

1- Le détecteur de changement est détaché de la vue ( voir détacher )

2- Une mise à jour a eu lieu mais elle n'est pas entrée dans la zone Angular, donc Angular n'est pas au courant.

Par exemple, lorsqu'une fonction tierce a mis à jour votre modèle et que vous souhaitez ensuite mettre à jour la vue.

 someFunctionThatIsRunByAThirdPartyCode(){
     yourModel.text = "new text";
 }

Parce que ce code est en dehors de la zone d'Angular (probablement), vous devez très probablement vous assurer de détecter les changements et de mettre à jour la vue, ainsi :

 myFunction(){
   someFunctionThatIsRunByAThirdPartyCode();

   // Let's detect the changes that above function made to the model which Angular is not aware of.
    this.cd.detectChanges();
 }

NOTE :

Il existe d'autres moyens de faire fonctionner ce qui précède, en d'autres termes, il existe d'autres moyens d'apporter ce changement dans le cycle de changement d'Angular.

** Vous pourriez envelopper cette fonction tierce dans une zone.run :

 myFunction(){
   this.zone.run(this.someFunctionThatIsRunByAThirdPartyCode);
 }

** Vous pourriez envelopper la fonction dans un setTimeout :

myFunction(){
   setTimeout(this.someFunctionThatIsRunByAThirdPartyCode,0);
 }

3- Il y a aussi des cas où vous mettez à jour le modèle après le change detection cycle est terminée, où dans ces cas vous obtenez cette redoutable erreur :

"L'expression a changé après avoir été vérifiée" ;

Cela signifie généralement (dans le langage Angular2) :

J'ai vu un changement dans votre modèle qui a été causé par l'une de mes méthodes acceptées ( événements, requêtes XHR, setTimeout, et ... ) et puis j'ai lancé ma détection de changement pour mettre à jour votre vue et je l'ai terminé, mais ensuite il y avait une autre fonction dans votre code qui a mis à jour le modèle à nouveau et je ne veux pas lancer ma détection de changement à nouveau parce qu'il n'y a pas de vérification sale comme AngularJS plus :D et nous devrions utiliser un flux de données à sens unique !

Vous rencontrerez certainement cette erreur :P .

Il y a plusieurs façons d'y remédier :

1- La bonne méthode : assurez-vous que la mise à jour se situe dans le cycle de détection des changements (les mises à jour d'Angular2 sont des flux à sens unique qui se produisent une fois, ne mettez pas à jour le modèle après cela et déplacez votre code à un meilleur endroit/temps).

2- La voie de la paresse : exécuter detectChanges() après cette mise à jour pour rendre angular2 heureux, ce n'est certainement pas la meilleure façon, mais comme vous avez demandé quels sont les scénarios possibles, c'en est un.

De cette façon, vous dites : je sais sincèrement que vous avez lancé la détection des changements, mais je veux que vous la refassiez parce que j'ai dû mettre à jour quelque chose à la volée après que vous ayez terminé la vérification.

3- Placez le code à l'intérieur d'un setTimeout parce que setTimeout est patché par zone et fonctionnera detectChanges après qu'il soit terminé.


Extrait de la documentation

markForCheck() : void

Marque tous les ancêtres de ChangeDetectionStrategy comme devant être vérifiés.

Ceci est surtout nécessaire lorsque le ChangeDetectionStrategy de votre composant est OnPush .

OnPush signifie que la détection des changements ne doit être exécutée que si l'une de ces conditions est remplie :

1- L'une des @inputs du composant a été complètement remplacée par une nouvelle valeur, ou plus simplement, si la référence de la propriété @Input a changé du tout au tout.

Donc si ChangeDetectionStrategy de votre composant est OnPush et ensuite vous avez :

   var obj = {
     name:'Milad'
   };

Et ensuite vous le mettez à jour/mutant comme :

  obj.name = "a new name";

Cela ne mettra pas à jour le obj. La détection de changement ne sera donc pas exécutée, et la vue ne reflète pas la mise à jour/mutation.

Dans ce cas, vous devez indiquer manuellement à Angular de vérifier et de mettre à jour la vue (markForCheck) ;

Donc si tu as fait ça :

  obj.name = "a new name";

Vous devez le faire :

  this.cd.markForCheck();

Plutôt, ci-dessous, cela provoquerait l'exécution d'une détection de changement :

    obj = {
      name:"a new name"
    };

qui a complètement remplacé l'objet précédent par un nouvel objet {} ;

2- Un événement a été déclenché, comme un clic ou quelque chose comme ça, ou l'un des composants enfants a émis un événement.

Des événements tels que :

  • Cliquez sur
  • Keyup
  • Événements d'abonnement
  • etc.

En résumé :

  • Utilisez detectChanges() lorsque vous avez mis à jour le modèle après qu'angular ait lancé sa détection de changement, ou si la mise à jour n'a pas été dans le monde angulaire du tout.

  • Utilisez markForCheck() si vous utilisez OnPush et que vous contournez l'option ChangeDetectionStrategy en mutant certaines données ou vous avez mis à jour le modèle dans une setTimeout ;

15 votes

Donc si vous mutez cet objet, la vue ne sera pas mise à jour, et même si vous exécutez detectChanges, cela ne fonctionnera pas car il n'y a pas eu de changement. - ce n'est pas vrai. detectChanges vue des mises à jour. Voir cette explication approfondie .

0 votes

En ce qui concerne markForCheck dans la conclusion, ce n'est pas exact non plus. Voici exemple modifié de cette question il ne détecte pas les changements d'objet avec OnPush et markForCheck. Mais le même exemple fonctionnera s'il n'y a pas de stratégie OnPush.

0 votes

@Maximus, Concernant votre premier commentaire, j'ai lu votre post, merci pour cela c'était bien. Mais dans votre explication, vous dites que si la stratégie est OnPush, cela signifie que la stratégie si this.cdMode === ChangeDetectorStatus.Checked cela ne mettra pas à jour la vue, c'est pourquoi vous utilisez markForCheck.

138voto

Maximus Points 1342

La plus grande différence entre les deux est que detectChanges() déclenche effectivement la détection des changements, tandis que markForCheck() ne déclenche pas la détection des changements.

detectChanges

Celui-ci est utilisé pour exécuter la détection des changements pour l'arbre des composants en commençant par le composant que vous déclenchez. detectChanges() on. Ainsi, la détection des changements sera exécutée pour le composant actuel et tous ses enfants. Angular conserve les références à l'arborescence du composant racine dans le fichier ApplicationRef et lorsqu'une opération asynchrone se produit, elle déclenche la détection des changements sur ce composant racine par l'intermédiaire d'une méthode enveloppante. tick() :

@Injectable()
export class ApplicationRef_ extends ApplicationRef {
  ...
  tick(): void {
    if (this._runningTick) {
      throw new Error('ApplicationRef.tick is called recursively');
    }

    const scope = ApplicationRef_._tickScope();
    try {
      this._runningTick = true;
      this._views.forEach((view) => view.detectChanges()); <------------------

view Voici la vue du composant racine. Il peut y avoir de nombreux composants racine, comme je l'ai décrit dans le document Quelles sont les implications de l'amorçage de plusieurs composants ? .

@milad a décrit les raisons pour lesquelles vous pourriez potentiellement avoir besoin de déclencher la détection des changements manuellement.

markForCheck

Comme je l'ai dit, ce type ne déclenche pas du tout la détection de changement. Il remonte simplement du composant actuel au composant Root et met à jour leur état de vue en ChecksEnabled . Voici le code source :

export function markParentViewsForCheck(view: ViewData) {
  let currView: ViewData|null = view;
  while (currView) {
    if (currView.def.flags & ViewFlags.OnPush) {
      currView.state |= ViewState.ChecksEnabled;  <-----------------
    }
    currView = currView.viewContainerParent || currView.parent;
  }
}

La détection des changements pour le composant n'est pas planifiée, mais lorsqu'elle aura lieu à l'avenir (dans le cadre du cycle CD actuel ou suivant), les vues du composant parent seront vérifiées, même si les détecteurs de changement sont détachés. Les détecteurs de changement peuvent être détachés en utilisant la fonction cd.detach() ou en spécifiant OnPush stratégie de détection des changements. Tous les gestionnaires d'événements natifs marquent toutes les vues des composants parents pour vérification.

Cette approche est souvent utilisée dans le ngDoCheck le crochet du cycle de vie. Vous pouvez en savoir plus dans le Si vous pensez ngDoCheck signifie que votre composant est vérifié - lisez cet article. .

Voir aussi Tout ce que vous devez savoir sur la détection des changements en Angular pour plus de détails.

4 votes

Pourquoi detectChanges fonctionne-t-il sur le composant et ses enfants alors que markForCheck sur le composant et ses ancêtres ?

1 votes

@pablo, c'est à dessein. Je ne suis pas vraiment familier avec le raisonnement.

0 votes

@AngularInDepth.com la détection de changement bloque-t-elle l'interface utilisateur si le traitement est très intensif ?

28voto

STEVER Points 1545

J'ai créé un screencast de 4 minutes pour expliquer la différence entre markForCheck() y detectChanges() - https://www.youtube.com/watch?v=OcphK_aEd7I

enter image description here

25voto

Kevin Beal Points 1078

cd.detectChanges() exécutera la détection des changements immédiatement à partir du composant actuel jusqu'à ses descendants.

cd.markForCheck() n'exécutera pas la détection de changement, mais marquera ses ancêtres comme devant exécuter la détection de changement. La prochaine fois que la détection des modifications sera exécutée quelque part, elle le sera également pour les composants qui ont été marqués.

  • Si vous voulez réduire le nombre de fois où la détection des changements est appelée, utilisez la méthode suivante cd.markForCheck() . Souvent, les changements affectent plusieurs composants et quelque part, la détection des changements sera appelée. Vous êtes essentiellement en train de dire : assurons-nous simplement que ce composant est également mis à jour lorsque cela se produira. (La vue est immédiatement mise à jour dans chaque projet que j'ai écrit, mais pas dans chaque test unitaire).
  • Si vous ne pouvez pas être sûr que cd.detectChanges() n'est pas actuellement en cours d'exécution détection des changements, utilisation cd.markForCheck() . detectChanges() sera une erreur dans ce cas. Cela signifie probablement que vous essayez de modifier l'état d'un composant ancêtre, ce qui va à l'encontre des hypothèses sur lesquelles repose la détection des modifications d'Angular.
  • S'il est essentiel que la vue soit mise à jour de manière synchrone avant une autre action, utilisez detectChanges() . markForCheck() peut ne pas mettre à jour votre vue à temps. Le test unitaire de quelque chose qui affecte votre vue, par exemple, peut vous obliger à appeler manuellement fixture.detectChanges() alors que ce n'était pas nécessaire dans l'application elle-même.
  • Si vous modifiez l'état d'un composant ayant plus d'ancêtres que de descendants, vous pouvez obtenir une augmentation des performances en utilisant la fonction detectChanges() puisque vous ne lancez pas inutilement la détection des changements sur les ancêtres du composant.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X