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

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

je ne trouvé des informations sur DONC quant à la différence entre NgZone.run() , mais pas entre ces deux fonctions.

pour les réponses avec seulement une référence au doc, s'il vous plaît illustrer quelques scénarios pratiques pour choisir l'un par rapport à l'autre.

89
demandé sur Alexander Abakumov 2016-12-28 17:49:30

2 réponses

De docs :

detectChanges() : void

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

cela signifie, s'il y a un cas où quelque chose à l'intérieur de votre modèle (votre classe) a changé mais qu'il n'a pas reflété la vue, vous pourriez avoir besoin de notifier Angular pour détecter ces changements (détecter des changements locaux) et mettre à jour la vue.

scénarios possibles pourraient être:

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

2-une mise à jour a eu lieu mais il n'a pas été à l'intérieur de la Zone angulaire, donc, angulaire ne sait pas à ce sujet.

comme quand une fonction tierce a mis à jour votre modèle et que vous voulez mettre à jour la vue après cela.

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

parce que ce code est en dehors de la zone D'Angular (probablement), vous avez très probablement besoin de s'assurer de détecter les changements et de mettre à jour la vue, donc:

 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 y a d'autres façons de faire fonctionner ci-dessus, en d'autres termes, il y a d'autres façons d'amener ce changement à l'intérieur du cycle de changement angulaire.

** Vous pouvez envelopper cette fonction de tiers à l'intérieur d'une zone.run:

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

** Vous pouvez envelopper la fonction à l'intérieur d'un setTimeout :

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

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

"l'Expression a changé après qu'il a été vérifiée";

cela signifie généralement (de la langue Angular2) :

j'ai vu un changement dans votre modèle a été causé par un de mes moyens acceptés ( événements , XHR requests , setTimeout, et ... ) et puis j'ai lancé ma détection de changement pour mettre à jour votre vue et je l'ai terminée, mais 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 plus de vérification sale comme AngularJS :D et nous devrions utiliser un flux de données à Sens Unique!

Vous aurez certainement venir à travers ce message d'erreur :P .

Couple des moyens de le réparer:

1 - la bonne façon : assurez-vous que la mise à jour est à l'intérieur du cycle de détection de changement ( Angular2 mises à jour sont un flux à sens unique qui se produisent une fois, ne pas mettre à jour le modèle après cela et déplacer votre code à un meilleur endroit/heure ).

2 - Lazy way : run detectChanges () après cette mise à jour pour rendre angular2 heureux , ce n'est certainement pas la meilleure façon, mais comme vous avez demandé ce qui sont les possibles les scénarios , c'est l'un d'entre eux.

de cette façon vous dites : je sais sincèrement que vous avez exécuté la détection de changement, mais je veux que vous le fassiez à nouveau parce que j'ai dû mettre à jour quelque chose sur le vol après que vous ayez terminé la vérification.

3 - Mettez le code à l'intérieur d'un setTimeout , parce que setTimeout est patché par zone et sera exécuté detectChanges après qu'il est terminé.


à Partir de la documentation

markForCheck() : void

marque Tous les ancêtres de la stratégie de section changée à vérifier.

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

OnPush lui-même signifie, exécuter la détection de changement seulement si l'un de ceux-ci s'est produit:

1-L'une des @entrées de la component a été complètement remplacé par une nouvelle valeur , ou simplement mis, si la référence de la propriété @Input a complètement changé .

donc si ChangeDetectionStrategy de votre composant est OnPush et alors vous avez:

   var obj = {
     name:'Milad'
   };

et ensuite vous mettez à jour/muter comme:

  obj.name = "a new name";

ceci ne mettra pas à jour le obj référence ,donc la détection de changement ne va pas fonctionner, donc la vue ne reflète pas la mise à jour/mutation.

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

donc si vous avez fait ceci:

  obj.name = "a new name";

Vous devez faire ceci:

  this.cd.markForCheck();

plutôt, bellow ferait tourner une détection de changement:

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

qui remplacer complètement l'ancien obj par un nouveau {} ;

2-un événement s'est déclenché, comme un clic ou quelque chose comme cela ou l'un des composants enfant a émis un événement.

des Événements comme :

  • , Cliquez sur
  • Keyup
  • événements D'Abonnement
  • etc.

Donc, en résumé :

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

  • Utiliser markForCheck() si vous utilisez des OnPush et vous êtes en contournant le ChangeDetectionStrategy par la mutation de certaines données ou vous avez mis à jour le modèle à l'intérieur d'un setTimeout ;

130
répondu Milad 2018-08-11 23:23:14

la plus grande différence entre les deux est que detectChanges() déclenche en fait la détection de changement, tandis que markForCheck() ne déclenche pas la détection de changement.

detectChanges

celui-ci est utilisé pour exécuter la détection de changement pour l'arbre de composants à partir du composant sur lequel vous déclenchez detectChanges() . Ainsi, la détection de changement s'effectuera pour le composant actuel et tous ses enfants. L'angle contient des références à la composante racine arbre dans le ApplicationRef et quand n'importe quelle opération d'async se produit il déclenche la détection de changement sur ce composant de racine par une méthode d'enrubannage 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 des composants racine. Il peut y avoir de nombreux composants racine comme je l'ai décrit dans le quelles sont les implications de bootstrapping composants multiples .

@milad décrit les raisons pour lesquelles vous pourriez avoir besoin pour déclencher détection de changement manuelle.

markForCheck

comme je l'ai dit, ce type ne déclenche pas la détection de changement du tout. Il va simplement vers le haut du composant courant au composant racine et met à jour leur état de vue à 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 du changement réel pour le composant n'est pas programmée mais quand il se produira dans le futur (soit dans le cadre du cycle de CD actuel ou suivant) les vues des composants parents seront vérifiées même si les détecteurs de changement se sont détachés. Les détecteurs de changement peuvent être détachés en utilisant cd.detach() ou en spécifiant OnPush stratégie de détection de changement. 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 crochet du cycle de vie ngDoCheck . Vous pouvez lire plus dans le si vous pensez que ngDoCheck signifie que votre composant est vérifié - lire cet article .

Voir aussi Tout ce que vous devez savoir à propos de la détection de changement dans Angulaire pour plus de détails.

48
répondu Max Wizard K 2017-09-21 10:40:45